From 21bd4997d1dc2eea55e25ff6a76c86a83a5d8567 Mon Sep 17 00:00:00 2001 From: Ningyuan Li Date: Thu, 6 Feb 2025 14:08:08 +0900 Subject: [PATCH 1/3] use unicapture backend --- .gitmodules | 3 + CMakeLists.txt | 16 ++--- frontend/App.js | 8 +++ package.json | 2 +- src/main.c | 5 +- src/server.c | 145 ++++++++++++++++++++------------------ src/server.h | 17 ++--- src/service.c | 2 - src/settings.c | 4 +- src/settings.h | 1 + unicapture/CMakeLists.txt | 2 + unicapture/hyperion-webos | 1 + 12 files changed, 107 insertions(+), 99 deletions(-) create mode 100644 unicapture/CMakeLists.txt create mode 160000 unicapture/hyperion-webos diff --git a/.gitmodules b/.gitmodules index 668e070..48f0407 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "tv-native-apis"] path = tv-native-apis url = https://github.com/webosbrew/tv-native-apis.git +[submodule "unicapture/hyperion-webos"] + path = unicapture/hyperion-webos + url = https://github.com/webosbrew/hyperion-webos.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e313584..02c99a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.14) -project (webos-vncserver C) +project (webos-vncserver C CXX) add_subdirectory(prebuilt) add_subdirectory(tv-native-apis) @@ -35,6 +35,8 @@ include_directories(${GLIB2_INCLUDE_DIRS}) include_directories(${LS2_INCLUDE_DIRS}) include_directories(${PMLOG_INCLUDE_DIRS}) +add_subdirectory(unicapture) + set(CMAKE_INSTALL_RPATH $ORIGIN) add_executable(webos-vncserver src/main.c @@ -45,20 +47,10 @@ add_executable(webos-vncserver src/log.c ) set_property(TARGET webos-vncserver PROPERTY ENABLE_EXPORTS 1) -target_link_libraries(webos-vncserver im vncserver pthread z png jpeg ssl crypto dl ${LS2_LDFLAGS} ${PBNJSON_LDFLAGS} ${GLIB2_LDFLAGS} ${PMLOG_LDFLAGS} rt) +target_link_libraries(webos-vncserver im vncserver unicapture pthread z png jpeg ssl crypto dl ${LS2_LDFLAGS} ${PBNJSON_LDFLAGS} ${GLIB2_LDFLAGS} ${PMLOG_LDFLAGS} rt) set_target_properties(webos-vncserver PROPERTIES BUILD_RPATH_USE_ORIGIN ON INSTALL_RPATH "$ORIGIN:$ORIGIN/lib" BUILD_RPATH "$ORIGIN:$ORIGIN/lib" ) - -add_library(capture_gm SHARED - src/capture_gm.c -) -target_link_libraries(capture_gm gm) - -add_library(capture_halgal SHARED - src/capture_halgal.c -) -target_link_libraries(capture_halgal halgal) diff --git a/frontend/App.js b/frontend/App.js index 00cbc94..bda982f 100644 --- a/frontend/App.js +++ b/frontend/App.js @@ -41,6 +41,7 @@ export function App() { const [width, setWidth] = React.useState(""); const [height, setHeight] = React.useState(""); const [autostart, setAutostart] = React.useState(false); + const [captureVideo, setCaptureVideo] = React.useState(false); const [running, setRunning] = React.useState(null); const [activeClients, setActiveClients] = React.useState(0); @@ -92,6 +93,7 @@ export function App() { setHeight(status.settings.height); setAutostart(status.settings.autostart); setFramerate(status.settings.framerate); + setCaptureVideo(status.settings.captureVideo); setLoading(false); } catch (err) { @@ -109,6 +111,7 @@ export function App() { autostart, password, framerate, + captureVideo, }); setStatusText("Configuration changed."); } catch (err) { @@ -169,6 +172,11 @@ export function App() { value={password} onChange={(evt) => setPassword(evt.target.value)} /> + setCaptureVideo(!captureVideo)} + /> running; -} - -int capture_backend_load(capture_backend_t* backend, const char* name) { - void* handle; - - handle = dlopen(name, RTLD_NOW); - - if (handle == NULL) { - DBG("Failed to load %s: %s", name, dlerror()); - return -1; - } - backend->name = name; - backend->init = dlsym(handle, "capture_init"); - backend->execute = dlsym(handle, "capture_execute"); - backend->destroy = dlsym(handle, "capture_destroy"); + if (server->active_clients > 0) { + memcpy(server->screen->frameBuffer, rgb_data, width * height * 4); + rfbMarkRectAsModified(server->screen, 0, 0, width, height); + } - return 0; + return 0; } -int capture_backend_init(capture_backend_t* backend, uint32_t width, uint32_t height) { - int ret; - - if ((ret = capture_backend_load(backend, "libcapture_halgal.so")) != 0) { - DBG("%s load failed: %d", "libcapture_halgal", ret); - } else { - if ((ret = backend->init(width, height)) != 0) { - DBG("%s init failed: %d", "libcapture_halgal", ret); - } else { - return 0; - } - } - - if ((ret = capture_backend_load(backend, "libcapture_gm.so")) != 0) { - DBG("%s load failed: %d", "libcapture_gm", ret); - } else { - if ((ret = backend->init(width, height)) != 0) { - DBG("%s init failed: %d", "libcapture_gm", ret); - } else { - return 0; - } - } - - WARN("No eligible capture backends found"); +int server_init_backends(server_t * server, settings_t* settings) { + + cap_backend_config_t config = {0}; + config.resolution_width = settings->width; + config.resolution_height = settings->height; + config.fps = settings->framerate; + + const char* const ui_backends[] = { "libgm_backend.so", "libhalgal_backend.so", NULL }; + const char* const video_backends[] = { "libvtcapture_backend.so", "libdile_vt_backend.so", NULL }; + char backend_name[FILENAME_MAX] = { 0 }; + + if (!server->ui_backend_initialized) { + server->unicapture.ui_capture = NULL; + INFO("Autodetecting UI backend..."); + if (unicapture_try_backends(&config, &server->ui_backend, ui_backends) == 0) { + server->unicapture.ui_capture = &server->ui_backend; + server->ui_backend_initialized = true; + } else { + ERR("Failed to initialize UI capture backend"); + return -1; + } + } + + if (!server->video_backend_initialized) { + server->unicapture.video_capture = NULL; + + if (!settings->capture_video) { + INFO("Video capture disabled"); + } else { + INFO("Autodetecting video backend..."); + if (unicapture_try_backends(&config, &server->video_backend, video_backends) == 0) { + server->unicapture.video_capture = &server->video_backend; + server->video_backend_initialized = true; + } else { + ERR("Failed to initialize video capture backend"); + return -1; + } + } + } + return 0; +} - return -1; +void server_destroy_backends(server_t* server) { + if (server->ui_backend_initialized && server->ui_backend.cleanup) { + DBG("Cleaning up UI backend..."); + DBG("Result: %d", server->ui_backend.cleanup(server->ui_backend.state)); + server->ui_backend_initialized = false; + } + + if (server->video_backend_initialized && server->video_backend.cleanup) { + DBG("Cleaning up video backend..."); + DBG("Result: %d", server->video_backend.cleanup(server->video_backend.state)); + server->video_backend_initialized = false; + } } static void server_client_gone(rfbClientPtr cl) { @@ -83,11 +96,15 @@ static void server_client_gone(rfbClientPtr cl) { INFO("%s [%d]: Client disconnected", cl->host, server->active_clients); server->active_clients -= 1; + if (server->active_clients == 0) { + unicapture_stop(&server->unicapture); + } } static enum rfbNewClientAction server_client_incoming(rfbClientPtr cl) { server_t* server = (server_t*) cl->screen->screenData; server->active_clients += 1; + unicapture_start(&server->unicapture); cl->clientGoneHook = &server_client_gone; @@ -108,12 +125,19 @@ static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl) { int server_start(server_t* server, settings_t* settings) { int ret; - if ((ret = capture_backend_init(&server->capture, settings->width, settings->height)) != 0) { + unicapture_init(&server->unicapture); + server->unicapture.vsync = true; + server->unicapture.fps = settings->framerate; + server->unicapture.target_format = PIXFMT_ARGB; + server->unicapture.callback = server_frame_handler; + server->unicapture.callback_data = server; + + if ((ret = server_init_backends(server, settings)) != 0) { ERR("capture_init() failed: %d", ret); return -2; } - INFO("Using capture backend: %s", server->capture.name); + INFO("Using capture backend: ui=%s, video=%s", server->ui_backend.name, server->video_backend.name); rfbLogEnable(0); @@ -166,31 +190,12 @@ int server_start(server_t* server, settings_t* settings) { return 0; } -void server_bind_gmainloop(server_t* server) { - server->timeout_ref = g_timeout_add(1000 / server->settings->framerate, server_frame_handler, (gpointer) server); -} - -int server_update(server_t* server) { - int ret; - if (server->active_clients > 0) { - int fbsize = server->screen->width * server->screen->height * bpp; - if ((ret = server->capture.execute(server->screen->frameBuffer, fbsize)) != 0) { - ERR("capture execute failed: %08x", ret); - return -5; - } - - rfbMarkRectAsModified(server->screen, 0, 0, server->screen->width, server->screen->height); - } - - return 0; -} - - int server_stop(server_t* server) { INFO("Shutting down..."); g_source_remove(server->timeout_ref); server->running = false; - server->capture.destroy(); + unicapture_stop(&server->unicapture); + server_destroy_backends(server); rfbShutdownServer(server->screen, TRUE); free(server->screen->frameBuffer); rfbScreenCleanup(server->screen); diff --git a/src/server.h b/src/server.h index 7717e2e..e8c28f3 100644 --- a/src/server.h +++ b/src/server.h @@ -4,16 +4,15 @@ #include #include #include "settings.h" - -typedef struct _capture_backend { - const char* name; - int (*init)(uint32_t width, uint32_t height); - int (*execute)(uint8_t* target, uint32_t size); - int (*destroy)(void); -} capture_backend_t; +#include "../unicapture/hyperion-webos/unicapture/unicapture.h" typedef struct { - capture_backend_t capture; + unicapture_state_t unicapture; + bool ui_backend_initialized; + bool video_backend_initialized; + capture_backend_t ui_backend; + capture_backend_t video_backend; + rfbScreenInfoPtr screen; int active_clients; settings_t* settings; @@ -24,5 +23,3 @@ typedef struct { int server_start(server_t* server, settings_t* settings); int server_stop(server_t* server); -int server_update(server_t* server); -void server_bind_gmainloop(server_t* server); diff --git a/src/service.c b/src/service.c index 10e9648..302f388 100644 --- a/src/service.c +++ b/src/service.c @@ -32,7 +32,6 @@ bool method_start(LSHandle *sh, LSMessage *message, void *data) { jobject_set(jobj, j_cstr_to_buffer("message"), jstring_create("Running already")); } else { if ((ret = server_start(server_p, settings_p)) == 0) { - server_bind_gmainloop(server_p); INFO("Server started, replying..."); server_p->running = true; jobject_set(jobj, j_cstr_to_buffer("returnValue"), jboolean_create(TRUE)); @@ -124,7 +123,6 @@ bool method_configure(LSHandle *sh, LSMessage *message, void *data) { if (was_running) { if ((ret = server_start(server_p, settings_p)) == 0) { - server_bind_gmainloop(server_p); INFO("Server started, replying..."); jobject_set(jobj, j_cstr_to_buffer("returnValue"), jboolean_create(TRUE)); jobject_set(jobj, j_cstr_to_buffer("message"), jstring_create("Restarted")); diff --git a/src/settings.c b/src/settings.c index 5e59150..3d77209 100644 --- a/src/settings.c +++ b/src/settings.c @@ -8,9 +8,10 @@ int settings_load_json(settings_t* settings_p, jvalue_ref parsed) { if ((value = jobject_get(parsed, j_cstr_to_buffer("width"))) && jis_number(value)) jnumber_get_i32(value, &settings_p->width); if ((value = jobject_get(parsed, j_cstr_to_buffer("height"))) && jis_number(value)) jnumber_get_i32(value, &settings_p->height); if ((value = jobject_get(parsed, j_cstr_to_buffer("framerate"))) && jis_number(value)) jnumber_get_i32(value, &settings_p->framerate); + if ((value = jobject_get(parsed, j_cstr_to_buffer("captureVideo"))) && jis_boolean(value)) jboolean_get(value, &settings_p->capture_video); if ((value = jobject_get(parsed, j_cstr_to_buffer("password"))) && jis_string(value)) { free(settings_p->password); - settings_p->password = jstring_get(value).m_str; + settings_p->password = (char *) jstring_get(value).m_str; } return 0; @@ -21,6 +22,7 @@ int settings_save_json(settings_t* settings_p, jvalue_ref target) { jobject_set(target, j_cstr_to_buffer("width"), jnumber_create_i32(settings_p->width)); jobject_set(target, j_cstr_to_buffer("height"), jnumber_create_i32(settings_p->height)); jobject_set(target, j_cstr_to_buffer("framerate"), jnumber_create_i32(settings_p->framerate)); + jobject_set(target, j_cstr_to_buffer("captureVideo"), jboolean_create(settings_p->capture_video)); jobject_set(target, j_cstr_to_buffer("password"), jstring_create(settings_p->password)); return 0; } diff --git a/src/settings.h b/src/settings.h index c563664..b77bb4f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -12,6 +12,7 @@ typedef struct { int framerate; char* password; bool autostart; + bool capture_video; } settings_t; int settings_load_json(settings_t* settings, jvalue_ref source); diff --git a/unicapture/CMakeLists.txt b/unicapture/CMakeLists.txt new file mode 100644 index 0000000..c2a5ada --- /dev/null +++ b/unicapture/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(hyperion-webos/libyuv) +add_subdirectory(hyperion-webos/unicapture) diff --git a/unicapture/hyperion-webos b/unicapture/hyperion-webos new file mode 160000 index 0000000..27cde35 --- /dev/null +++ b/unicapture/hyperion-webos @@ -0,0 +1 @@ +Subproject commit 27cde35b96389137279f3692fb80df8cc57ba247 From c45cf0e0101dca7cbd40a6f9b8f5b8f406278669 Mon Sep 17 00:00:00 2001 From: Ningyuan Li Date: Thu, 6 Feb 2025 14:43:17 +0900 Subject: [PATCH 2/3] removed unused source --- src/capture.h | 6 ---- src/capture_gm.c | 31 --------------------- src/capture_halgal.c | 66 -------------------------------------------- 3 files changed, 103 deletions(-) delete mode 100644 src/capture.h delete mode 100644 src/capture_gm.c delete mode 100644 src/capture_halgal.c diff --git a/src/capture.h b/src/capture.h deleted file mode 100644 index c62683d..0000000 --- a/src/capture.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include - -int capture_init(uint32_t width, uint32_t height); -int capture_execute(uint8_t* target, uint32_t size); -int capture_destroy(); diff --git a/src/capture_gm.c b/src/capture_gm.c deleted file mode 100644 index 242c6a9..0000000 --- a/src/capture_gm.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include - -#include "capture.h" - -GM_SURFACE surface_info; -uint32_t w; -uint32_t h; - -int capture_init(uint32_t width, uint32_t height) { - w = width; - h = height; - return GM_CreateSurface(width, height, 0, &surface_info); -} - -int capture_execute(uint8_t* target, uint32_t size) { - int ret; - - if ((ret = GM_CaptureGraphicScreen(surface_info.surfaceID, &w, &h)) != 0) { - return ret; - } - - memcpy(target, surface_info.framebuffer, size); - - return 0; -} - -int capture_destroy() { - return GM_DestroySurface(surface_info.surfaceID); -} diff --git a/src/capture_halgal.c b/src/capture_halgal.c deleted file mode 100644 index fddfd40..0000000 --- a/src/capture_halgal.c +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include "capture.h" -#include "log.h" - -HAL_GAL_SURFACE surface_info; -int gfx_fd; -uint8_t* framebuffer_mem; - -int capture_init(uint32_t width, uint32_t height) { - int ret; - - if ((ret = HAL_GAL_Init()) != 0) { - ERR("Unable to initialize HAL_GAL: %08x", ret); - return -2; - } - - if ((ret = HAL_GAL_CreateSurface(width, height, 0, &surface_info)) != 0) { - ERR("HAL_GAL_CreateSurface failed: %08x", ret); - return -3; - } - - // Attempt one frame capture to verify this backend works properly - if ((ret = HAL_GAL_CaptureFrameBuffer(&surface_info)) != 0) { - ERR("HAL_GAL_CaptureFrameBuffer failed: %d", ret); - return -5; - } - - gfx_fd = open("/dev/gfx",2); - if (gfx_fd < 0) { - ERR("/dev/gfx open failed: %s", strerror(errno)); - return -4; - } - - int len = surface_info.property; - - if (len == 0){ - len = surface_info.height * surface_info.pitch; - } - - framebuffer_mem = (uint8_t*) mmap(0, len, PROT_READ, MAP_SHARED, gfx_fd, surface_info.offset); - - return 0; -} - -int capture_execute(uint8_t* target, uint32_t size) { - int ret; - if ((ret = HAL_GAL_CaptureFrameBuffer(&surface_info)) != 0) { - ERR("HAL_GAL_CaptureFrameBuffer failed: %d", ret); - return -5; - } - - memcpy(target, framebuffer_mem, size); - - return 0; -} - -int capture_destroy() { - return HAL_GAL_DestroySurface(&surface_info); -} From ca643297e1592d2722efcc4d2a91be220bdff20d Mon Sep 17 00:00:00 2001 From: Ningyuan Li Date: Fri, 7 Feb 2025 09:23:30 +0900 Subject: [PATCH 3/3] updated hyperion-webos commit --- unicapture/hyperion-webos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unicapture/hyperion-webos b/unicapture/hyperion-webos index 27cde35..177e8cf 160000 --- a/unicapture/hyperion-webos +++ b/unicapture/hyperion-webos @@ -1 +1 @@ -Subproject commit 27cde35b96389137279f3692fb80df8cc57ba247 +Subproject commit 177e8cf21b09fed98843e411a6301aadaea37846