diff --git a/scripts/raspi/sysroot-packages.list b/scripts/raspi/sysroot-packages.list index 59b6f0429..24c91737c 100644 --- a/scripts/raspi/sysroot-packages.list +++ b/scripts/raspi/sysroot-packages.list @@ -1,11 +1,13 @@ libavcodec-dev libavutil-dev +libcec-dev libcurl4-openssl-dev libexpat1-dev libfontconfig1-dev libinih-dev libmbedtls-dev -libopus-dev +libopus-devlib +libp8-platform-dev libraspberrypi-dev libsdl2-dev libsdl2-image-dev diff --git a/src/app/app.c b/src/app/app.c index 618f42447..dc661ed1e 100644 --- a/src/app/app.c +++ b/src/app/app.c @@ -85,7 +85,7 @@ int app_init(app_t *app, app_settings_loader *settings_loader, int argc, char *a } void app_deinit(app_t *app) { - app_bus_drain(); + app_bus_drain(app); app_session_destroy(app); app_ui_close(&app->ui); app_ui_deinit(&app->ui); @@ -178,7 +178,7 @@ static int app_event_filter(void *userdata, SDL_Event *event) { case SDL_USEREVENT: { if (event->user.code == BUS_INT_EVENT_ACTION) { bus_actionfunc actionfn = event->user.data1; - actionfn(event->user.data2); + actionfn(event->user.data2, app); } else if (event->user.code == USER_INPUT_CONTROLLERDB_UPDATED) { app_input_handle_event(&app->input, event); } else { diff --git a/src/app/lvgl/lv_disp_drv_app.c b/src/app/lvgl/lv_disp_drv_app.c index 9fdc19cce..f85c66b17 100644 --- a/src/app/lvgl/lv_disp_drv_app.c +++ b/src/app/lvgl/lv_disp_drv_app.c @@ -7,7 +7,7 @@ static void lv_sdl_drv_fb_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, static void lv_sdl_drv_fb_clear(lv_disp_drv_t *disp_drv, uint8_t *buf, uint32_t size); -lv_disp_drv_t *lv_app_disp_drv_create(SDL_Window *window, int dpi) { +lv_disp_drv_t *lv_app_disp_drv_create(SDL_Window *window, int dpi, void *user_data) { int width = 0, height = 0; SDL_GetWindowSize(window, &width, &height); LV_ASSERT(width > 0 && height > 0); @@ -21,6 +21,7 @@ lv_disp_drv_t *lv_app_disp_drv_create(SDL_Window *window, int dpi) { lv_draw_sdl_drv_param_t *param = lv_mem_alloc(sizeof(lv_draw_sdl_drv_param_t)); param->renderer = renderer; + param->user_data = user_data; driver->user_data = param; driver->draw_buf = draw_buf; driver->dpi = dpi; @@ -68,10 +69,11 @@ void lv_app_display_resize(lv_disp_t *disp, int width, int height) { void lv_app_redraw_now(lv_disp_drv_t *disp_drv) { lv_draw_sdl_drv_param_t *param = disp_drv->user_data; + app_ui_t *ui = param->user_data; SDL_Renderer *renderer = param->renderer; SDL_Texture *texture = disp_drv->draw_buf->buf1; SDL_SetRenderTarget(renderer, NULL); - if (!ui_render_background()) { + if (!ui_render_background(ui)) { bool has_renderer = false; if (has_renderer) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF); diff --git a/src/app/lvgl/lv_disp_drv_app.h b/src/app/lvgl/lv_disp_drv_app.h index 7d6fc5d97..8d1537c51 100644 --- a/src/app/lvgl/lv_disp_drv_app.h +++ b/src/app/lvgl/lv_disp_drv_app.h @@ -3,7 +3,7 @@ #include "lvgl.h" #include -lv_disp_drv_t *lv_app_disp_drv_create(SDL_Window *window, int dpi); +lv_disp_drv_t *lv_app_disp_drv_create(SDL_Window *window, int dpi, void *user_data); void lv_app_disp_drv_deinit(lv_disp_drv_t *driver); diff --git a/src/app/platform/sdl/bus.c b/src/app/platform/sdl/bus.c index 3fb41427a..3fee73027 100644 --- a/src/app/platform/sdl/bus.c +++ b/src/app/platform/sdl/bus.c @@ -12,7 +12,7 @@ typedef struct bus_blocking_action_t { bool done; } bus_action_sync_t; -static void invoke_action_sync(bus_action_sync_t *sync); +static void invoke_action_sync(bus_action_sync_t *sync, app_t *app); bool bus_pushevent(int which, void *data1, void *data2) { SDL_Event ev; @@ -55,20 +55,20 @@ bool app_bus_post_sync(app_t *app, bus_actionfunc action, void *data) { return true; } -void app_bus_drain() { +void app_bus_drain(app_t *app) { SDL_Event event; while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_USEREVENT, SDL_USEREVENT) > 0) { if (event.user.code != BUS_INT_EVENT_ACTION) { continue; } bus_actionfunc actionfn = event.user.data1; - actionfn(event.user.data2); + actionfn(event.user.data2, app); } } -static void invoke_action_sync(bus_action_sync_t *sync) { +static void invoke_action_sync(bus_action_sync_t *sync, app_t *app) { SDL_LockMutex(sync->mutex); - sync->action(sync->data); + sync->action(sync->data, app); sync->done = true; SDL_CondSignal(sync->cond); SDL_UnlockMutex(sync->mutex); diff --git a/src/app/stream/connection/session_connnection.c b/src/app/stream/connection/session_connnection.c index 7679e12ab..03647d740 100644 --- a/src/app/stream/connection/session_connnection.c +++ b/src/app/stream/connection/session_connnection.c @@ -6,9 +6,14 @@ #include "input/input_gamepad.h" #include "app.h" #include "stream/session_priv.h" +#include "util/bus.h" static session_t *current_session = NULL; +static void connection_status_poor_main(void *data, app_t *app); + +static void connection_status_okay_main(void *data, app_t *app); + static void connection_terminated(int errorCode) { if (errorCode == ML_ERROR_GRACEFUL_TERMINATION) { session_interrupt(current_session, false, STREAMING_INTERRUPT_HOST); @@ -30,11 +35,11 @@ static void connection_status_update(int status) { switch (status) { case CONN_STATUS_OKAY: commons_log_info("Session", "Connection is okay"); - streaming_notice_show(NULL); + app_bus_post(current_session->app, connection_status_okay_main, NULL); break; case CONN_STATUS_POOR: commons_log_warn("Session", "Connection is poor"); - streaming_notice_show(locstr("Unstable connection.")); + app_bus_post(current_session->app, connection_status_poor_main, NULL); break; default: break; @@ -94,4 +99,13 @@ CONNECTION_LISTENER_CALLBACKS *session_connection_callbacks_prepare(session_t *s void session_connection_callbacks_reset(session_t *session) { current_session = NULL; +} + + +void connection_status_poor_main(void *data, app_t *app) { + streaming_notice_show(locstr("Unstable connection.")); +} + +void connection_status_okay_main(void *data, app_t *app) { + streaming_notice_show(NULL); } \ No newline at end of file diff --git a/src/app/stream/session.c b/src/app/stream/session.c index cd94868c4..2ad8a2fab 100644 --- a/src/app/stream/session.c +++ b/src/app/stream/session.c @@ -109,6 +109,13 @@ bool session_accepting_input(session_t *session) { return session->input.started && !ui_should_block_input(); } +bool session_is_streaming(session_t *session) { + SDL_LockMutex(session->state_lock); + bool result = session->state == STREAMING_STREAMING; + SDL_UnlockMutex(session->state_lock); + return result; +} + void session_start_input(session_t *session) { session_input_started(&session->input); } diff --git a/src/app/stream/session.h b/src/app/stream/session.h index 4f9f384b2..1f2a10fbc 100644 --- a/src/app/stream/session.h +++ b/src/app/stream/session.h @@ -83,6 +83,8 @@ void session_toggle_vmouse(session_t *session); bool session_accepting_input(session_t *session); +bool session_is_streaming(session_t *session); + void streaming_display_size(session_t *session, short width, short height); void streaming_enter_fullscreen(session_t *session); diff --git a/src/app/stream/video/session_video.c b/src/app/stream/video/session_video.c index 3dc827c5b..061a28854 100644 --- a/src/app/stream/video/session_video.c +++ b/src/app/stream/video/session_video.c @@ -13,6 +13,8 @@ #include "stream/connection/session_connection.h" #include "stream/session_priv.h" #include "app.h" +#include "lvgl/lv_disp_drv_app.h" +#include "draw/sdl/lv_draw_sdl.h" #include #include @@ -37,8 +39,16 @@ static int vdec_delegate_submit(PDECODE_UNIT decodeUnit); static void vdec_stat_submit(const struct VIDEO_STATS *src, unsigned long now); +static void vdec_frame_callback(SS4S_VideoOutputFrame *frame, void *userdata); + static void stream_info_parse_size(PDECODE_UNIT decodeUnit, struct VIDEO_INFO *info); +static void stream_ui_video_opened(SS4S_VideoInfo *info, app_t *app); + +static void stream_ui_video_closed(void *data, app_t *app); + +static void stream_put_frame(SS4S_VideoOutputFrame *frame, app_t *app); + DECODER_RENDERER_CALLBACKS ss4s_dec_callbacks = { .setup = vdec_delegate_setup, .cleanup = vdec_delegate_cleanup, @@ -109,6 +119,8 @@ int vdec_delegate_setup(int videoFormat, int width, int height, int redrawRate, case SS4S_VIDEO_OPEN_UNSUPPORTED_CODEC: return CALLBACKS_SESSION_ERROR_VDEC_UNSUPPORTED; default: + app_bus_post_sync(app, (bus_actionfunc) stream_ui_video_opened, &info); + SS4S_PlayerVideoSetFrameCallback(player, vdec_frame_callback, app); return 0; } } @@ -117,6 +129,7 @@ void vdec_delegate_cleanup() { assert(player != NULL); free(buffer); SS4S_PlayerVideoClose(player); + app_bus_post_sync(session->app, (bus_actionfunc) stream_ui_video_closed, NULL); session = NULL; } @@ -193,6 +206,11 @@ void vdec_stat_submit(const struct VIDEO_STATS *src, unsigned long now) { app_bus_post(session->app, (bus_actionfunc) streaming_refresh_stats, NULL); } +void vdec_frame_callback(SS4S_VideoOutputFrame *frame, void *userdata) { + app_t *app = userdata; + app_bus_post_sync(app, (bus_actionfunc) stream_put_frame, frame); +} + void stream_info_parse_size(PDECODE_UNIT decodeUnit, struct VIDEO_INFO *info) { if (decodeUnit->frameType != FRAME_TYPE_IDR) { return; } for (PLENTRY entry = decodeUnit->bufferList; entry != NULL; entry = entry->next) { @@ -210,4 +228,27 @@ void stream_info_parse_size(PDECODE_UNIT decodeUnit, struct VIDEO_INFO *info) { info->height = dimension.height; return; } +} + +static void stream_ui_video_opened(SS4S_VideoInfo *info, app_t *app) { + lv_draw_sdl_drv_param_t *param = app->ui.disp->driver->user_data; + app->ui.video_texture = SDL_CreateTexture(param->renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, + info->width, info->height); +} + +static void stream_ui_video_closed(void *data, app_t *app) { + (void) data; + if (app->ui.video_texture != NULL) { + SDL_DestroyTexture(app->ui.video_texture); + app->ui.video_texture = NULL; + } +} + +void stream_put_frame(SS4S_VideoOutputFrame *frame, app_t *app) { + if (app->ui.video_texture == NULL) { + return; + } + SDL_UpdateYUVTexture(app->ui.video_texture, NULL, frame->yuv.data[0], frame->yuv.linesize[0], frame->yuv.data[1], + frame->yuv.linesize[1], frame->yuv.data[2], frame->yuv.linesize[2]); + lv_app_redraw_now(lv_disp_get_default()->driver); } \ No newline at end of file diff --git a/src/app/ui/fatal_error.c b/src/app/ui/fatal_error.c index 23de3215b..91a67b60b 100644 --- a/src/app/ui/fatal_error.c +++ b/src/app/ui/fatal_error.c @@ -11,7 +11,7 @@ #include "util/bus.h" #include "logging.h" -static void fatal_error_popup(void *data); +static void fatal_error_popup(void *data, app_t *app); static void fatal_error_quit(lv_event_t *ev); @@ -31,7 +31,7 @@ void app_fatal_error(const char *title, const char *fmt, ...) { } -static void fatal_error_popup(void *data) { +void fatal_error_popup(void *data, app_t *app) { fatal_error_data_t *error_data = data; static const char *btn_txts[] = {"Quit", ""}; lv_obj_t *msgbox = lv_msgbox_create(NULL, error_data->title, error_data->message, btn_txts, false); @@ -40,6 +40,6 @@ static void fatal_error_popup(void *data) { lv_obj_center(msgbox); } -static void fatal_error_quit(lv_event_t *ev) { +void fatal_error_quit(lv_event_t *ev) { exit(1); } \ No newline at end of file diff --git a/src/app/ui/root.c b/src/app/ui/root.c index 44ad09bbe..15163bd03 100644 --- a/src/app/ui/root.c +++ b/src/app/ui/root.c @@ -69,7 +69,7 @@ void app_ui_open(app_ui_t *ui, const app_launch_params_t *params) { if (ui->window == NULL) { ui->window = app_ui_create_window(ui); } - lv_disp_drv_t *driver = lv_app_disp_drv_create(ui->window, ui->dpi); + lv_disp_drv_t *driver = lv_app_disp_drv_create(ui->window, ui->dpi, ui); lv_disp_t *disp = lv_disp_drv_register(driver); disp->bg_color = lv_color_make(0, 0, 0); disp->bg_opa = 0; @@ -125,15 +125,20 @@ bool app_ui_is_opened(const app_ui_t *ui) { return ui->disp != NULL; } -bool ui_has_stream_renderer() { +bool ui_has_stream_renderer(app_ui_t *ui) { // return ui_stream_render != NULL && ui_stream_render->renderDraw; return false; } -bool ui_render_background() { -// if (streaming_status == STREAMING_STREAMING && ui_stream_render && ui_stream_render->renderDraw) { -// return ui_stream_render->renderDraw(); -// } +bool ui_render_background(app_ui_t *ui) { + app_t *app = ui->app; + if (app->session != NULL && session_is_streaming(app->session) && + app->ss4s.video_cap.output != SS4S_VIDEO_OUTPUT_OPAQUE) { + lv_draw_sdl_drv_param_t *param = lv_disp_get_default()->driver->user_data; + SDL_Renderer *renderer = param->renderer; + SDL_RenderCopy(renderer, ui->video_texture, NULL, NULL); + return true; + } return false; } diff --git a/src/app/ui/root.h b/src/app/ui/root.h index 22e1d433a..e1bf89cb1 100644 --- a/src/app/ui/root.h +++ b/src/app/ui/root.h @@ -23,6 +23,7 @@ typedef struct app_launch_params_t app_launch_params_t; struct app_ui_t { app_t *app; SDL_Window *window; + SDL_Texture *video_texture; int width, height, dpi; lv_img_decoder_t *img_decoder; app_fonts_t fonts; @@ -55,9 +56,9 @@ void app_ui_close(app_ui_t *ui); bool app_ui_is_opened(const app_ui_t *ui); -bool ui_has_stream_renderer(); +bool ui_has_stream_renderer(app_ui_t *ui); -bool ui_render_background(); +bool ui_render_background(app_ui_t *ui); bool ui_dispatch_userevent(app_t *app, int which, void *data1, void *data2); diff --git a/src/app/util/bus.h b/src/app/util/bus.h index 9427bbff7..ca8839e29 100644 --- a/src/app/util/bus.h +++ b/src/app/util/bus.h @@ -5,10 +5,10 @@ #define BUS_INT_EVENT_ACTION 99 #define BUS_EVENT_START 100 -typedef void(*bus_actionfunc)(void *); - typedef struct app_t app_t; +typedef void(*bus_actionfunc)(void *, app_t *app); + bool bus_pushevent(int which, void *data1, void *data2); bool app_bus_post(app_t *app, bus_actionfunc action, void *data); @@ -18,4 +18,4 @@ bool app_bus_post_sync(app_t *app, bus_actionfunc action, void *data); /** * Drain all bus events */ -void app_bus_drain(); \ No newline at end of file +void app_bus_drain(app_t *app); \ No newline at end of file diff --git a/third_party/ss4s b/third_party/ss4s index 2e5b4a041..283263391 160000 --- a/third_party/ss4s +++ b/third_party/ss4s @@ -1 +1 @@ -Subproject commit 2e5b4a041044b5517185083f7c3c74ef68a8c8e2 +Subproject commit 283263391483ef97d1d8a0c52c3e52e716208b52