카메라2번 연결 시 자동 절체 컨텐츠 재생 처리

해제시 1번 카메라 절체 처리
This commit is contained in:
seiki7788 2025-03-17 09:30:32 +09:00
parent 5827b1eb7d
commit 936d804d1d
9 changed files with 284 additions and 20 deletions

View File

@ -18,7 +18,7 @@ video_source avformat,android_camera,1
videnc_format yuv420p videnc_format yuv420p
audio_jitter_buffer_type adaptive audio_jitter_buffer_type adaptive
audio_jitter_buffer_delay 0-20 audio_jitter_buffer_delay 0-20
video_jitter_buffer_type adaptive video_jitter_buffer_type off
video_jitter_buffer_delay 1-100 video_jitter_buffer_delay 1-100
rtp_stats yes rtp_stats yes
rtp_timeout 60 rtp_timeout 60

View File

@ -116,7 +116,7 @@ add_library(lib_baresip STATIC IMPORTED)
set_target_properties(lib_baresip PROPERTIES IMPORTED_LOCATION set_target_properties(lib_baresip PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/baresip/lib/${ANDROID_ABI}/libbaresip.a) ${distribution_DIR}/baresip/lib/${ANDROID_ABI}/libbaresip.a)
add_library(baresip SHARED baresip.c vidisp.c) add_library(baresip SHARED baresip.c vidisp.c serial.c)
#set(CMAKE_SHARED_LINKER_FLAGS #set(CMAKE_SHARED_LINKER_FLAGS
# "${CMAKE_SHARED_LINKER_FLAGS} -u AImageReader_new") # "${CMAKE_SHARED_LINKER_FLAGS} -u AImageReader_new")

119
app/src/main/cpp/serial.c Normal file
View File

@ -0,0 +1,119 @@
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <jni.h>
#include <stdlib.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <termios.h>
//
// Created by ritos on 2025-03-10.
//
//------------------------------------------------------------------------------
// 설명 : 시리얼포트를 연다.
// 주의 : RTS/CTS 를 제어하지 않는다.
// 시리얼포트를 열고 이전의 포트설정상태를 저장하지 않았다.
//------------------------------------------------------------------------------
int open_serial(const char *dev_name, int baud, int vtime, int vmin )
{
int fd;
struct termios newtio;
// 시리얼포트를 연다.
fd = open( dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK );
if ( fd < 0 )
{
// 화일 열기 실패
printf( "Device OPEN FAIL %s\n", dev_name );
return -1;
}
// 시리얼 포트 환경을 설정한다.
memset(&newtio, 0, sizeof(newtio));
newtio.c_iflag = IGNPAR; // non-parity
newtio.c_oflag = 0;
newtio.c_cflag = CS8 | CLOCAL | CREAD; // NO-rts/cts
switch( baud )
{
case 115200 : newtio.c_cflag |= B115200; break;
case 57600 : newtio.c_cflag |= B57600; break;
case 38400 : newtio.c_cflag |= B38400; break;
case 19200 : newtio.c_cflag |= B19200; break;
case 9600 : newtio.c_cflag |= B9600; break;
case 4800 : newtio.c_cflag |= B4800; break;
case 2400 : newtio.c_cflag |= B2400; break;
default : newtio.c_cflag |= B115200; break;
}
//set input mode (non-canonical, no echo,.....)
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = vtime; // timeout 0.1초 단위
newtio.c_cc[VMIN] = vmin; // 최소 n 문자 받을 때까진 대기
tcflush ( fd, TCIFLUSH );
tcsetattr( fd, TCSANOW, &newtio );
return fd;
}
//------------------------------------------------------------------------------
// 설명 : 시리얼포트를 닫는다.
// 주의 :
//------------------------------------------------------------------------------
void close_serial( int fd )
{
close( fd );
}
JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_openSerial(JNIEnv *env, jobject thiz, jstring strDev, jint baudrate)
{
(void)env;
(void)thiz;
const char *pType;
int result;
pType = (*env)->GetStringUTFChars(env, strDev, NULL);
result = open_serial(pType, baudrate, 100, 1);
(*env)->ReleaseStringUTFChars(env, strDev, pType);
return result;
}
JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_readSerial(JNIEnv *env, jobject thiz, jint devHandle, jbyteArray buffer, jint size)
{
(void)env;
(void)thiz;
int result;
jbyte *pBuffer = (*env)->GetByteArrayElements(env, buffer, NULL);
result = read( devHandle, (char *)pBuffer, size );
(*env)->ReleaseByteArrayElements(env, buffer, pBuffer, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_writeSerial(JNIEnv *env, jobject thiz, jint devHandle, jbyteArray buffer, jint size)
{
(void)env;
(void)thiz;
int result;
jbyte *pBuffer = (*env)->GetByteArrayElements(env, buffer, NULL);
result = write( devHandle, (char *)pBuffer, size );
(*env)->ReleaseByteArrayElements(env, buffer, pBuffer, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_closeSerial(JNIEnv *env, jobject thiz, jint devHandle)
{
(void)env;
(void)thiz;
close_serial(devHandle);
return 1;
}

View File

@ -73,6 +73,27 @@ static void renderer_destroy(struct vidisp_st *st)
gst = NULL; gst = NULL;
} }
static void renderer_destroy_self(struct vidisp_st *st)
{
LOGD("At renderer_destroy_self() on thread %li\n", (long)pthread_self());
if (st->display != EGL_NO_DISPLAY) {
eglMakeCurrent(st->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (st->surface != EGL_NO_SURFACE) {
eglDestroySurface(st->display, st->surface);
st->surface = EGL_NO_SURFACE;
}
if (st->context != EGL_NO_CONTEXT) {
eglDestroyContext(st->display, st->context);
st->context = EGL_NO_CONTEXT;
}
eglTerminate(st->display);
st->display = EGL_NO_DISPLAY;
}
eglReleaseThread();
gst_self = NULL;
}
static void destructor(void *arg) static void destructor(void *arg)
{ {
struct vidisp_st *st = arg; struct vidisp_st *st = arg;
@ -81,6 +102,14 @@ static void destructor(void *arg)
mem_deref(st->vf); mem_deref(st->vf);
} }
static void destructor_self(void *arg)
{
struct vidisp_st *st = arg;
renderer_destroy_self(st);
mem_deref(st->vf);
}
static int context_initialize(struct vidisp_st *st) static int context_initialize(struct vidisp_st *st)
{ {
const EGLint attribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, const EGLint attribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
@ -166,6 +195,93 @@ static int context_initialize(struct vidisp_st *st)
return 0; return 0;
} }
static int context_initialize_self(struct vidisp_st *st)
{
const EGLint attribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8, EGL_NONE};
EGLDisplay display;
EGLConfig config;
EGLint numConfigs;
EGLint format;
EGLSurface surface;
EGLContext context;
if ((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) {
LOGW("eglGetDisplay() returned error %d\n", eglGetError());
return eglGetError();
}
if (!eglInitialize(display, NULL, NULL)) {
LOGW("eglInitialize() returned error %d\n", eglGetError());
return eglGetError();
}
if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs)) {
LOGW("eglChooseConfig() returned error %d\n", eglGetError());
renderer_destroy_self(st);
return eglGetError();
}
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
LOGW("eglGetConfigAttrib() returned error %d\n", eglGetError());
renderer_destroy_self(st);
return eglGetError();
}
ANativeWindow_setBuffersGeometry(st->window, 0, 0, format);
if (!(surface = eglCreateWindowSurface(display, config, st->window, NULL))) {
LOGW("eglCreateWindowSurface() returned error %s\n", egl_error(eglGetError()));
renderer_destroy_self(st);
return eglGetError();
}
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
LOGW("eglBindApi failed with error %s\n", egl_error(eglGetError()));
renderer_destroy_self(st);
return eglGetError();
}
if (!(context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL))) {
LOGW("eglCreateContext() returned error %d\n", eglGetError());
renderer_destroy_self(st);
return eglGetError();
}
if (!eglMakeCurrent(display, surface, surface, context)) {
LOGW("eglMakeCurrent() returned error %d\n", eglGetError());
renderer_destroy_self(st);
return eglGetError();
}
if (!eglQuerySurface(display, surface, EGL_WIDTH, &st->width)
|| !eglQuerySurface(display, surface, EGL_HEIGHT, &st->height)) {
LOGW("eglQuerySurface() returned error %d\n", eglGetError());
renderer_destroy_self(st);
return eglGetError();
}
LOGD("RenderSelf buffer w/h = %d/%d", st->width, st->height);
st->display = display;
st->surface = surface;
st->context = context;
glDisable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
resize = false;
resize_self = false;
LOGD("Rendered contextSelf initialized");
return 0;
}
int opengles_alloc(struct vidisp_st **stp, const struct vidisp *vd, struct vidisp_prm *prm, int opengles_alloc(struct vidisp_st **stp, const struct vidisp *vd, struct vidisp_prm *prm,
const char *dev, vidisp_resize_h *resizeh, void *arg) const char *dev, vidisp_resize_h *resizeh, void *arg)
{ {
@ -217,9 +333,9 @@ int opengles_alloc_self(struct vidisp_st **stp, const struct vidisp *vd, struct
LOGD("At opengles_alloc_self() on thread %li\n", (long)pthread_self()); LOGD("At opengles_alloc_self() on thread %li\n", (long)pthread_self());
if (gst_self) if (gst_self)
renderer_destroy(gst_self); renderer_destroy_self(gst_self);
gst_self = mem_zalloc(sizeof(*gst_self), destructor); gst_self = mem_zalloc(sizeof(*gst_self), destructor_self);
if (!gst_self) if (!gst_self)
return ENOMEM; return ENOMEM;
@ -612,11 +728,11 @@ static int opengles_render_self(struct vidisp_st *st)
err = texture_init(st); err = texture_init(st);
if (err) { if (err) {
LOGW("opengles_render_self: failed to initialize texture\n"); LOGW("[SELF] opengles_render_self: failed to initialize texture\n");
return err; return err;
} }
LOGD("video frame width/height = %d/%d\n", st->vf->size.w, st->vf->size.h); LOGD("[SELF] video frame width/height = %d/%d\n", st->vf->size.w, st->vf->size.h);
#if 1 // 원본 이미지 원본 크기를 View 에 매핑. 작으면 가운데로, 크면 Crop 발생 #if 1 // 원본 이미지 원본 크기를 View 에 매핑. 작으면 가운데로, 크면 Crop 발생
frame_width_self = st->vf->size.w; frame_width_self = st->vf->size.w;
@ -729,22 +845,22 @@ int opengles_display_self(
/* This is hack to make sure that context is initialised on the same thread */ /* This is hack to make sure that context is initialised on the same thread */
if (!st->context) { if (!st->context) {
err = context_initialize(st); err = context_initialize_self(st);
if (err) { if (err) {
LOGW("Renderer context init failed with error %d\n", err); LOGW("[SELF] Renderer context init failed with error %d\n", err);
return err; return err;
} }
} }
if (!st->vf) { if (!st->vf) {
if (frame->size.w & 3) { if (frame->size.w & 3) {
LOGW("opengles_display: frame width must be multiple of 4\n"); LOGW("[SELF] opengles_display: frame width must be multiple of 4\n");
return EINVAL; return EINVAL;
} }
err = vidframe_alloc(&st->vf, VID_FMT_RGB565, &frame->size); err = vidframe_alloc(&st->vf, VID_FMT_RGB565, &frame->size);
if (err) { if (err) {
LOGW("opengles_display: vidframe_alloc failed: %d\n", err); LOGW("[SELF] opengles_display: vidframe_alloc failed: %d\n", err);
return err; return err;
} }
} }
@ -754,8 +870,16 @@ int opengles_display_self(
opengles_render_self(st); opengles_render_self(st);
err = eglSwapBuffers(st->display, st->surface); err = eglSwapBuffers(st->display, st->surface);
if (!err) if (!err) {
LOGW("eglSwapBuffers() returned error %s\n", egl_error(eglGetError())); LOGW("[SELF] eglSwapBuffers() returned error %s\n", egl_error(eglGetError()));
renderer_destroy_self(st);
mem_deref(st->vf);
err = context_initialize_self(st);
err = vidframe_alloc(&st->vf, VID_FMT_RGB565, &frame->size);
glDeleteTextures(1, &st->texture_id);
st->texture_id = 0;
}
return err; return err;
} }
@ -808,20 +932,20 @@ JNIEXPORT void JNICALL Java_com_tutpro_baresip_plus_VideoView_set_1surfaceSelf(
{ {
int w, h; int w, h;
LOGD("At set_surface() on thread %li\n", (long)pthread_self()); LOGD("[SELF] At set_surface() on thread %li\n", (long)pthread_self());
if (surface != 0) { if (surface != 0) {
window_self = ANativeWindow_fromSurface(env, surface); window_self = ANativeWindow_fromSurface(env, surface);
w = ANativeWindow_getWidth(window_self); w = ANativeWindow_getWidth(window_self);
h = ANativeWindow_getHeight(window_self); h = ANativeWindow_getHeight(window_self);
if ((w != window_width_self) || (h != window_height_self)) { if ((w != window_width_self) || (h != window_height_self)) {
LOGI("Got new windowSelf %p with w/h = %d/%d", window_self, w, h); LOGI("[SELF] Got new windowSelf %p with w/h = %d/%d", window_self, w, h);
window_width_self = w; window_width_self = w;
window_height_self = h; window_height_self = h;
resize_self = true; resize_self = true;
} }
} else { } else {
LOGI("Releasing windowSelf"); LOGI("[SELF] Releasing windowSelf");
ANativeWindow_release(window_self); ANativeWindow_release(window_self);
} }
} }

View File

@ -216,10 +216,10 @@ object Config {
val videoSize = previousVariable("video_size") val videoSize = previousVariable("video_size")
BaresipService.videoSize = if (videoSize !in videoSizes) { BaresipService.videoSize = if (videoSize !in videoSizes) {
if ("1280x720" in videoSizes) if ("1920x1080" in videoSizes)
Size(1280, 720) Size(1920, 1080)
else else
Size(640, 480) Size(1280, 720)
} else { } else {
Size(videoSize.substringBefore("x").toInt(), Size(videoSize.substringBefore("x").toInt(),
videoSize.substringAfter("x").toInt()) videoSize.substringAfter("x").toInt())

View File

@ -13,12 +13,12 @@ import android.content.Intent.ACTION_DIAL
import android.content.Intent.ACTION_VIEW import android.content.Intent.ACTION_VIEW
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager
import android.media.AudioManager import android.media.AudioManager
import android.media.MediaActionSound import android.media.MediaActionSound
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
import android.os.StrictMode.VmPolicy
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.provider.MediaStore import android.provider.MediaStore
import android.text.InputType import android.text.InputType
@ -46,7 +46,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.tutpro.baresip.plus.Utils.showSnackBar import com.tutpro.baresip.plus.Utils.showSnackBar
import com.tutpro.baresip.plus.databinding.ActivityMainBinding import com.tutpro.baresip.plus.databinding.ActivityMainBinding
import org.w3c.dom.Text
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.LocalDateTime import java.time.LocalDateTime
@ -54,6 +53,7 @@ import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
class SecondScreenPresentation(context: Context, display: Display) : Presentation(context, display) { class SecondScreenPresentation(context: Context, display: Display) : Presentation(context, display) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -159,6 +159,18 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// if (BuildConfig.DEBUG) {
//
// }
//
// StrictMode.setVmPolicy(
// VmPolicy.Builder()
// .detectLeakedClosableObjects() // ✨ close() 호출 누락 감지
// .penaltyLog() // Logcat에 로그 출력
// .penaltyDeath() // 앱 크래시 (원인 추적 용이)
// .build()
// )
enableEdgeToEdge() enableEdgeToEdge()
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
@ -2671,6 +2683,7 @@ class MainActivity : AppCompatActivity() {
if (atStartup) if (atStartup)
moveTaskToBack(true) moveTaskToBack(true)
} }
} }
private fun backup(password: String) { private fun backup(password: String) {

View File

@ -608,6 +608,7 @@ object Utils {
RandomAccessFile(filePath, "rw").use { raf -> RandomAccessFile(filePath, "rw").use { raf ->
raf.fd.sync() // eMMC에 확실히 기록 raf.fd.sync() // eMMC에 확실히 기록
raf.close()
} }
} }
catch (e: IOException) { catch (e: IOException) {
@ -1185,6 +1186,9 @@ object Utils {
} }
output.fd.sync() output.fd.sync()
output.close()
input.close()
} }
} }
@ -1351,5 +1355,9 @@ object Utils {
).joinToString(".") ).joinToString(".")
} }
external fun openSerial(strDev: String, baudrate: Int): Int // by ritoseo
external fun readSerial(devHandle:Int, buffer: ByteArray, size: Int): Int // by ritoseo
external fun writeSerial(devHandle:Int, buffer: ByteArray, size: Int): Int // by ritoseo
external fun closeSerial(devHandle: Int): Int // by ritoseo
} }