From cfbb73703e1dab7d18f7804c46c8ef08c11e4cd7 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Mon, 19 Aug 2024 14:09:20 +0100 Subject: [PATCH 1/9] browser: Disambiguate app naming --- gstcefsrc.cc | 12 ++++++------ gstcefsrc.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index e3298d6..d0622fd 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -414,17 +414,17 @@ void BrowserClient::MakeBrowser(int arg) g_mutex_unlock(&mElement->state_lock); } -App::App(GstCefSrc *src) : src(src) +BrowserApp::BrowserApp(GstCefSrc *src) : src(src) { } -CefRefPtr App::GetBrowserProcessHandler() +CefRefPtr BrowserApp::GetBrowserProcessHandler() { return this; } #ifdef __APPLE__ -void App::OnScheduleMessagePumpWork(int64_t delay_ms) +void BrowserApp::OnScheduleMessagePumpWork(int64_t delay_ms) { static const int64_t kMaxTimerDelay = 1000.0 / 60.0; @@ -453,7 +453,7 @@ void App::OnScheduleMessagePumpWork(int64_t delay_ms) } #endif -void App::OnBeforeCommandLineProcessing(const CefString &process_type, +void BrowserApp::OnBeforeCommandLineProcessing(const CefString &process_type, CefRefPtr command_line) { command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); @@ -566,7 +566,7 @@ run_cef (GstCefSrc *src) #endif CefSettings settings; - CefRefPtr app; + CefRefPtr app; CefWindowInfo window_info; CefBrowserSettings browserSettings; @@ -646,7 +646,7 @@ run_cef (GstCefSrc *src) g_free(base_path); g_free(locales_dir_path); - app = new App(src); + app = new BrowserApp(src); if (!CefInitialize(args, settings, app, nullptr)) { GST_ERROR ("Failed to initialize CEF"); diff --git a/gstcefsrc.h b/gstcefsrc.h index b3d51fe..0ebd28e 100644 --- a/gstcefsrc.h +++ b/gstcefsrc.h @@ -57,9 +57,9 @@ struct _GstCefSrcClass { GstPushSrcClass parent_class; }; -class App : public CefApp, public CefBrowserProcessHandler { +class BrowserApp : public CefApp, public CefBrowserProcessHandler { public: - App(GstCefSrc *src); + BrowserApp(GstCefSrc *src); void OnBeforeCommandLineProcessing(const CefString &process_type, CefRefPtr command_line) override; @@ -69,7 +69,7 @@ class App : public CefApp, public CefBrowserProcessHandler { void OnScheduleMessagePumpWork(int64_t delay_ms) override; #endif private: - IMPLEMENT_REFCOUNTING(App); + IMPLEMENT_REFCOUNTING(BrowserApp); GstCefSrc *src; }; From d0b41e3353092400d6ab26b2c7b801c938c8a231 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Mon, 19 Aug 2024 14:10:14 +0100 Subject: [PATCH 2/9] client: Improve encapsulation --- gstcefsrc.cc | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index d0622fd..05459de 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -326,17 +326,17 @@ class BrowserClient : { public: - BrowserClient(CefRefPtr rptr, CefRefPtr aptr, CefRefPtr rqptr, CefRefPtr display_handler, GstCefSrc *element) : - render_handler(rptr), - audio_handler(aptr), - request_handler(rqptr), - display_handler(display_handler), - mElement(element) + BrowserClient(GstCefSrc *element) : mElement(element) { + this->render_handler = new RenderHandler(element); + this->audio_handler = new AudioHandler(element); + this->request_handler = new RequestHandler(element); + this->display_handler = new DisplayHandler(element); } - virtual CefRefPtr GetLifeSpanHandler() override { - return this; + virtual CefRefPtr GetLifeSpanHandler() override + { + return this; } virtual CefRefPtr GetRenderHandler() override @@ -769,11 +769,7 @@ gst_cef_src_start(GstBaseSrc *base_src) { gboolean ret = FALSE; GstCefSrc *src = GST_CEF_SRC (base_src); - CefRefPtr browserClient; - CefRefPtr renderHandler = new RenderHandler(src); - CefRefPtr audioHandler = new AudioHandler(src); - CefRefPtr requestHandler = new RequestHandler(src); - CefRefPtr displayHandler = new DisplayHandler(src); + CefRefPtr browserClient = new BrowserClient(src); /* Make sure CEF is initialized before posting a task */ g_mutex_lock (&init_lock); @@ -787,8 +783,6 @@ gst_cef_src_start(GstBaseSrc *base_src) GST_OBJECT_LOCK (src); src->n_frames = 0; GST_OBJECT_UNLOCK (src); - - browserClient = new BrowserClient(renderHandler, audioHandler, requestHandler, displayHandler, src); #ifdef __APPLE__ if (pthread_main_np()) { /* in the main thread as per Cocoa */ From b363f5235c4aeeed9555101d36b171f9d33e0ae5 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Tue, 20 Aug 2024 12:13:33 +0100 Subject: [PATCH 3/9] client: Make main client inherit request handler --- gstcefsrc.cc | 119 +++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 70 deletions(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index 05459de..311e680 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -172,32 +172,6 @@ class RenderHandler : public CefRenderHandler IMPLEMENT_REFCOUNTING(RenderHandler); }; -class RequestHandler : public CefRequestHandler -{ - public: - - RequestHandler(GstCefSrc *element) : - element (element) - { - } - - ~RequestHandler() - { - } - - virtual void OnRenderProcessTerminated(CefRefPtr browser, TerminationStatus status) override - { - GST_WARNING_OBJECT (element, "Render subprocess terminated, reloading URL!"); - browser->Reload(); - } - - private: - - GstCefSrc *element; - IMPLEMENT_REFCOUNTING(RequestHandler); -}; - - class AudioHandler : public CefAudioHandler { public: @@ -322,15 +296,16 @@ class DisplayHandler : public CefDisplayHandler { class BrowserClient : public CefClient, - public CefLifeSpanHandler + public CefLifeSpanHandler, + public CefRequestHandler { public: BrowserClient(GstCefSrc *element) : mElement(element) { + this->render_handler = new RenderHandler(element); this->audio_handler = new AudioHandler(element); - this->request_handler = new RequestHandler(element); this->display_handler = new DisplayHandler(element); } @@ -351,7 +326,7 @@ class BrowserClient : virtual CefRefPtr GetRequestHandler() override { - return request_handler; + return this; } virtual CefRefPtr GetDisplayHandler() override @@ -359,15 +334,56 @@ class BrowserClient : return display_handler; } - virtual void OnBeforeClose(CefRefPtr browser) override; + virtual void OnRenderProcessTerminated(CefRefPtr browser, TerminationStatus status) override + { + CEF_REQUIRE_UI_THREAD(); + GST_WARNING_OBJECT (mElement, "Render subprocess terminated, reloading URL!"); + browser->Reload(); + } + + virtual void OnBeforeClose(CefRefPtr browser) override + { + mElement->browser = nullptr; + g_mutex_lock (&mElement->state_lock); + mElement->started = FALSE; + g_cond_signal (&mElement->state_cond); + g_mutex_unlock(&mElement->state_lock); + g_mutex_lock(&init_lock); + g_assert (browsers > 0); + browsers -= 1; + if (browsers == 0) { + CefQuitMessageLoop(); + } + g_mutex_unlock (&init_lock); + } + + void MakeBrowser(int) + { + CefWindowInfo window_info; + CefRefPtr browser; + CefBrowserSettings browser_settings; + + window_info.SetAsWindowless(0); + browser = CefBrowserHost::CreateBrowserSync(window_info, this, std::string(mElement->url), browser_settings, nullptr, nullptr); + g_mutex_lock (&init_lock); + g_assert (browsers < G_MAXUINT64); + browsers += 1; + g_mutex_unlock(&init_lock); - void MakeBrowser(int); + browser->GetHost()->SetAudioMuted(true); + + mElement->browser = browser; + + g_mutex_lock (&mElement->state_lock); + mElement->started = TRUE; + g_cond_signal (&mElement->state_cond); + g_mutex_unlock(&mElement->state_lock); + } private: CefRefPtr render_handler; CefRefPtr audio_handler; - CefRefPtr request_handler; CefRefPtr display_handler; public: @@ -376,43 +392,6 @@ class BrowserClient : IMPLEMENT_REFCOUNTING(BrowserClient); }; -void BrowserClient::OnBeforeClose(CefRefPtr browser) { - mElement->browser = nullptr; - g_mutex_lock (&mElement->state_lock); - mElement->started = FALSE; - g_cond_signal (&mElement->state_cond); - g_mutex_unlock(&mElement->state_lock); - g_mutex_lock(&init_lock); - g_assert (browsers > 0); - browsers -= 1; - if (browsers == 0) { - CefQuitMessageLoop(); - } - g_mutex_unlock (&init_lock); -} - -void BrowserClient::MakeBrowser(int arg) -{ - CefWindowInfo window_info; - CefRefPtr browser; - CefBrowserSettings browser_settings; - - window_info.SetAsWindowless(0); - browser = CefBrowserHost::CreateBrowserSync(window_info, this, std::string(mElement->url), browser_settings, nullptr, nullptr); - g_mutex_lock (&init_lock); - g_assert (browsers < G_MAXUINT64); - browsers += 1; - g_mutex_unlock(&init_lock); - - browser->GetHost()->SetAudioMuted(true); - - mElement->browser = browser; - - g_mutex_lock (&mElement->state_lock); - mElement->started = TRUE; - g_cond_signal (&mElement->state_cond); - g_mutex_unlock(&mElement->state_lock); -} BrowserApp::BrowserApp(GstCefSrc *src) : src(src) { @@ -454,7 +433,7 @@ void BrowserApp::OnScheduleMessagePumpWork(int64_t delay_ms) #endif void BrowserApp::OnBeforeCommandLineProcessing(const CefString &process_type, - CefRefPtr command_line) + CefRefPtr command_line) { command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); command_line->AppendSwitch("enable-media-stream"); From bb5b12e994dbc0e338409377c761efe632d0f27a Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Wed, 21 Aug 2024 13:33:04 +0100 Subject: [PATCH 4/9] message: Add message routers and handlers Define message handlers for the render and browser processes (to enable inter-process communication - see https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-inter-process-communication-ipc). In short this allows us to send messages from js to C++ in the main "browser" process of cef. The two functions ("gstSendMsg" and "gsCancelMsg") are attached to the javascript global "window" object. Note that we need to add a RendererApp in this commit to contain and run the javascript side of the message routing. See cef sample for more detail: https://github.com/chromiumembedded/cef-project/tree/aaccb78661c390a5ce6650df8db54ea5424d431c/examples/message_router --- gstcefsrc.cc | 136 ++++++++++++++++++++++++++++++++++++++------ gstcefsubprocess.cc | 109 ++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 17 deletions(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index 311e680..dc78875 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include "gstcefsrc.h" #include "gstcefaudiometa.h" @@ -127,6 +128,40 @@ gchar* get_plugin_base_path () { return base_path; } + +/** Cef Client */ + +/** Handlers */ + +// Handle messages in the browser process. +class MessageHandler : public CefMessageRouterBrowserSide::Handler { + public: + explicit MessageHandler(const CefString& startup_url) + : startup_url_(startup_url) {} + + // Called due to cefQuery execution in message_router.html. + bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64_t query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) override { + // Only handle messages from the startup URL. + const std::string& url = frame->GetURL(); + if (url.find(startup_url_) != 0) + return false; + + const std::string& message_name = request; + callback->Success(message_name + " is working"); + return true; + } + + private: + const CefString startup_url_; + + DISALLOW_COPY_AND_ASSIGN(MessageHandler); +}; + class RenderHandler : public CefRenderHandler { public: @@ -301,7 +336,7 @@ class BrowserClient : { public: - BrowserClient(GstCefSrc *element) : mElement(element) + BrowserClient(GstCefSrc *element) : src(element) { this->render_handler = new RenderHandler(element); @@ -309,6 +344,7 @@ class BrowserClient : this->display_handler = new DisplayHandler(element); } + // CefClient Methods: virtual CefRefPtr GetLifeSpanHandler() override { return this; @@ -334,20 +370,48 @@ class BrowserClient : return display_handler; } - virtual void OnRenderProcessTerminated(CefRefPtr browser, TerminationStatus status) override + bool OnProcessMessageReceived( + CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message + ) override { CEF_REQUIRE_UI_THREAD(); - GST_WARNING_OBJECT (mElement, "Render subprocess terminated, reloading URL!"); - browser->Reload(); + + return browser_msg_router_->OnProcessMessageReceived( + browser, + frame, + source_process, + message + ); + } + + // CefLifeSpanHandler Methods: + void OnAfterCreated(CefRefPtr browser) override + { + CEF_REQUIRE_UI_THREAD(); + + if (!browser_msg_router_) { + // Create the browser-side router for query handling. + CefMessageRouterConfig config; + config.js_query_function = "gstSendMsg"; + config.js_cancel_function = "gstCancelMsg"; + browser_msg_router_ = CefMessageRouterBrowserSide::Create(config); + + // Register handlers with the router. + browser_msg_handler_.reset(new MessageHandler(src->url)); + browser_msg_router_->AddHandler(browser_msg_handler_.get(), false); + } } virtual void OnBeforeClose(CefRefPtr browser) override { - mElement->browser = nullptr; - g_mutex_lock (&mElement->state_lock); - mElement->started = FALSE; - g_cond_signal (&mElement->state_cond); - g_mutex_unlock(&mElement->state_lock); + src->browser = nullptr; + g_mutex_lock (&src->state_lock); + src->started = FALSE; + g_cond_signal (&src->state_cond); + g_mutex_unlock(&src->state_lock); g_mutex_lock(&init_lock); g_assert (browsers > 0); browsers -= 1; @@ -357,6 +421,31 @@ class BrowserClient : g_mutex_unlock (&init_lock); } + // CefRequestHandler methods: + // TODO - is the ResourceRequestHandler stuff required? + bool OnBeforeBrowse( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + bool user_gesture, + bool is_redirect + ) override + { + CEF_REQUIRE_UI_THREAD(); + + browser_msg_router_->OnBeforeBrowse(browser, frame); + return false; + } + + virtual void OnRenderProcessTerminated(CefRefPtr browser, TerminationStatus status) override + { + CEF_REQUIRE_UI_THREAD(); + GST_WARNING_OBJECT (src, "Render subprocess terminated, reloading URL!"); + browser_msg_router_->OnRenderProcessTerminated(browser); + browser->Reload(); + } + + // Custom methods: void MakeBrowser(int) { CefWindowInfo window_info; @@ -364,7 +453,14 @@ class BrowserClient : CefBrowserSettings browser_settings; window_info.SetAsWindowless(0); - browser = CefBrowserHost::CreateBrowserSync(window_info, this, std::string(mElement->url), browser_settings, nullptr, nullptr); + browser = CefBrowserHost::CreateBrowserSync( + window_info, + this, + std::string(src->url), + browser_settings, + nullptr, + nullptr + ); g_mutex_lock (&init_lock); g_assert (browsers < G_MAXUINT64); browsers += 1; @@ -372,27 +468,32 @@ class BrowserClient : browser->GetHost()->SetAudioMuted(true); - mElement->browser = browser; + src->browser = browser; - g_mutex_lock (&mElement->state_lock); - mElement->started = TRUE; - g_cond_signal (&mElement->state_cond); - g_mutex_unlock(&mElement->state_lock); + g_mutex_lock (&src->state_lock); + src->started = TRUE; + g_cond_signal (&src->state_cond); + g_mutex_unlock(&src->state_lock); } private: + // Handles the browser side of query routing. + CefRefPtr browser_msg_router_; + std::unique_ptr browser_msg_handler_; CefRefPtr render_handler; CefRefPtr audio_handler; CefRefPtr display_handler; public: - GstCefSrc *mElement; + GstCefSrc *src; IMPLEMENT_REFCOUNTING(BrowserClient); }; +/** Browser App methods */ + BrowserApp::BrowserApp(GstCefSrc *src) : src(src) { } @@ -480,6 +581,9 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &process_type, } } + +/** cefsrc (Gstreamer) methods */ + static GstFlowReturn gst_cef_src_create(GstPushSrc *push_src, GstBuffer **buf) { GstCefSrc *src = GST_CEF_SRC (push_src); diff --git a/gstcefsubprocess.cc b/gstcefsubprocess.cc index af7aec2..154f3a2 100644 --- a/gstcefsubprocess.cc +++ b/gstcefsubprocess.cc @@ -17,6 +17,7 @@ * Boston, MA 02110-1301, USA. */ +#include "include/wrapper/cef_message_router.h" #include #include @@ -28,6 +29,98 @@ #include "include/cef_sandbox_mac.h" #endif + +enum ProcessType { + PROCESS_TYPE_BROWSER, + PROCESS_TYPE_RENDERER, + PROCESS_TYPE_OTHER, +}; + +// These flags must match the Chromium values. +const char kProcessType[] = "type"; +const char kRendererProcess[] = "renderer"; +#if defined(OS_LINUX) +const char kZygoteProcess[] = "zygote"; +#endif + +CefRefPtr CreateCommandLine(const CefMainArgs& main_args) { + CefRefPtr command_line = CefCommandLine::CreateCommandLine(); +#if defined(OS_WIN) + command_line->InitFromString(::GetCommandLineW()); +#else + command_line->InitFromArgv(main_args.argc, main_args.argv); +#endif + return command_line; +} + +ProcessType GetProcessType(const CefMainArgs& main_args) { + // Create a temporary CommandLine object. + CefRefPtr command_line = CreateCommandLine(main_args); + // The command-line flag won't be specified for the browser process. + if (!command_line->HasSwitch(kProcessType)) + return PROCESS_TYPE_BROWSER; + + const std::string& process_type = command_line->GetSwitchValue(kProcessType); + if (process_type == kRendererProcess) + return PROCESS_TYPE_RENDERER; + +#if defined(OS_LINUX) + // On Linux the zygote process is used to spawn other process types. Since we + // don't know what type of process it will be we give it the renderer app. + if (process_type == kZygoteProcess) + return PROCESS_TYPE_RENDERER; +#endif + + return PROCESS_TYPE_OTHER; +} + +// Implementation of CefApp for the renderer process. +class RendererApp : public CefApp, public CefRenderProcessHandler { + public: + RendererApp() {} + + // CefApp methods: + CefRefPtr GetRenderProcessHandler() override { + return this; + } + + // CefRenderProcessHandler methods: + void OnWebKitInitialized() override { + // Create the renderer-side router for query handling. + CefMessageRouterConfig config; + config.js_query_function = "gstSendMsg"; + config.js_cancel_function = "gstCancelMsg"; + renderer_msg_router_ = CefMessageRouterRendererSide::Create(config); + } + + void OnContextCreated(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override { + renderer_msg_router_->OnContextCreated(browser, frame, context); + } + + void OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override { + renderer_msg_router_->OnContextReleased(browser, frame, context); + } + + bool OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override { + return renderer_msg_router_->OnProcessMessageReceived( + browser, frame, source_process, message); + } + + private: + // Handles the renderer side of query routing. + CefRefPtr renderer_msg_router_; + + IMPLEMENT_REFCOUNTING(RendererApp); + DISALLOW_COPY_AND_ASSIGN(RendererApp); +}; + int main(int argc, char * argv[]) { #if !defined(__APPLE__) && defined(GST_CEF_USE_SANDBOX) @@ -57,5 +150,19 @@ int main(int argc, char * argv[]) } #endif - return CefExecuteProcess(args, nullptr, nullptr); + // NB: this function is executed on new thread per new CEF "process" / app + CefRefPtr app = nullptr; + switch (GetProcessType(args)) { + case PROCESS_TYPE_RENDERER: + app = new RendererApp(); + break; + case PROCESS_TYPE_BROWSER: + // browser app created in main thread / executable + break; + case PROCESS_TYPE_OTHER: + // this is unused?? + break; + } + + return CefExecuteProcess(args, app, nullptr); } From 8ddee256a5f7b87c34a3917d5f7687122cb84769 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Thu, 22 Aug 2024 20:22:26 +0100 Subject: [PATCH 5/9] gst: Standardize on src naming --- gstcefsrc.cc | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index dc78875..7e65b67 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -166,8 +166,8 @@ class RenderHandler : public CefRenderHandler { public: - RenderHandler(GstCefSrc *element) : - element (element) + RenderHandler(GstCefSrc *src) : + src (src) { } @@ -177,32 +177,32 @@ class RenderHandler : public CefRenderHandler void GetViewRect(CefRefPtr browser, CefRect &rect) override { - GST_LOG_OBJECT(element, "getting view rect"); - GST_OBJECT_LOCK (element); - rect = CefRect(0, 0, element->vinfo.width ? element->vinfo.width : DEFAULT_WIDTH, element->vinfo.height ? element->vinfo.height : DEFAULT_HEIGHT); - GST_OBJECT_UNLOCK (element); + GST_LOG_OBJECT(src, "getting view rect"); + GST_OBJECT_LOCK (src); + rect = CefRect(0, 0, src->vinfo.width ? src->vinfo.width : DEFAULT_WIDTH, src->vinfo.height ? src->vinfo.height : DEFAULT_HEIGHT); + GST_OBJECT_UNLOCK (src); } void OnPaint(CefRefPtr browser, PaintElementType type, const RectList &dirtyRects, const void * buffer, int w, int h) override { GstBuffer *new_buffer; - GST_LOG_OBJECT (element, "painting, width / height: %d %d", w, h); + GST_LOG_OBJECT (src, "painting, width / height: %d %d", w, h); - new_buffer = gst_buffer_new_allocate (NULL, element->vinfo.width * element->vinfo.height * 4, NULL); + new_buffer = gst_buffer_new_allocate (NULL, src->vinfo.width * src->vinfo.height * 4, NULL); gst_buffer_fill (new_buffer, 0, buffer, w * h * 4); - GST_OBJECT_LOCK (element); - gst_buffer_replace (&(element->current_buffer), new_buffer); + GST_OBJECT_LOCK (src); + gst_buffer_replace (&(src->current_buffer), new_buffer); gst_buffer_unref (new_buffer); - GST_OBJECT_UNLOCK (element); + GST_OBJECT_UNLOCK (src); - GST_LOG_OBJECT (element, "done painting"); + GST_LOG_OBJECT (src, "done painting"); } private: - GstCefSrc *element; + GstCefSrc *src; IMPLEMENT_REFCOUNTING(RenderHandler); }; @@ -211,8 +211,8 @@ class AudioHandler : public CefAudioHandler { public: - AudioHandler(GstCefSrc *element) : - mElement (element) + AudioHandler(GstCefSrc *src) : + src (src) { } @@ -233,9 +233,9 @@ class AudioHandler : public CefAudioHandler mRate = params.sample_rate; mChannels = channels; - GST_OBJECT_LOCK (mElement); - mElement->audio_events = g_list_append (mElement->audio_events, event); - GST_OBJECT_UNLOCK (mElement); + GST_OBJECT_LOCK (src); + src->audio_events = g_list_append (src->audio_events, event); + GST_OBJECT_UNLOCK (src); } void OnAudioStreamPacket(CefRefPtr browser, @@ -247,7 +247,7 @@ class AudioHandler : public CefAudioHandler GstMapInfo info; gint i, j; - GST_LOG_OBJECT (mElement, "Handling audio stream packet with %d frames", frames); + GST_LOG_OBJECT (src, "Handling audio stream packet with %d frames", frames); buf = gst_buffer_new_allocate (NULL, mChannels * frames * 4, NULL); @@ -261,18 +261,18 @@ class AudioHandler : public CefAudioHandler } gst_buffer_unmap (buf, &info); - GST_OBJECT_LOCK (mElement); + GST_OBJECT_LOCK (src); GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (frames, GST_SECOND, mRate); - if (!mElement->audio_buffers) { - mElement->audio_buffers = gst_buffer_list_new(); + if (!src->audio_buffers) { + src->audio_buffers = gst_buffer_list_new(); } - gst_buffer_list_add (mElement->audio_buffers, buf); - GST_OBJECT_UNLOCK (mElement); + gst_buffer_list_add (src->audio_buffers, buf); + GST_OBJECT_UNLOCK (src); - GST_LOG_OBJECT (mElement, "Handled audio stream packet"); + GST_LOG_OBJECT (src, "Handled audio stream packet"); } void OnAudioStreamStopped(CefRefPtr browser) override @@ -281,12 +281,12 @@ class AudioHandler : public CefAudioHandler void OnAudioStreamError(CefRefPtr browser, const CefString& message) override { - GST_WARNING_OBJECT (mElement, "Audio stream error: %s", message.ToString().c_str()); + GST_WARNING_OBJECT (src, "Audio stream error: %s", message.ToString().c_str()); } private: - GstCefSrc *mElement; + GstCefSrc *src; gint mRate; gint mChannels; IMPLEMENT_REFCOUNTING(AudioHandler); @@ -294,7 +294,7 @@ class AudioHandler : public CefAudioHandler class DisplayHandler : public CefDisplayHandler { public: - DisplayHandler(GstCefSrc *element) : mElement(element) {} + DisplayHandler(GstCefSrc *src) : src(src) {} ~DisplayHandler() = default; @@ -319,13 +319,13 @@ class DisplayHandler : public CefDisplayHandler { gst_level = GST_LEVEL_NONE; break; }; - GST_CAT_LEVEL_LOG (cef_console_debug, gst_level, mElement, "%s:%d %s", source.ToString().c_str(), line, + GST_CAT_LEVEL_LOG (cef_console_debug, gst_level, src, "%s:%d %s", source.ToString().c_str(), line, message.ToString().c_str()); return false; } private: - GstCefSrc *mElement; + GstCefSrc *src; IMPLEMENT_REFCOUNTING(DisplayHandler); }; @@ -336,12 +336,12 @@ class BrowserClient : { public: - BrowserClient(GstCefSrc *element) : src(element) + BrowserClient(GstCefSrc *src) : src(src) { - this->render_handler = new RenderHandler(element); - this->audio_handler = new AudioHandler(element); - this->display_handler = new DisplayHandler(element); + this->render_handler = new RenderHandler(src); + this->audio_handler = new AudioHandler(src); + this->display_handler = new DisplayHandler(src); } // CefClient Methods: @@ -422,7 +422,6 @@ class BrowserClient : } // CefRequestHandler methods: - // TODO - is the ResourceRequestHandler stuff required? bool OnBeforeBrowse( CefRefPtr browser, CefRefPtr frame, @@ -758,7 +757,7 @@ run_cef (GstCefSrc *src) } static GstStateChangeReturn -gst_cef_src_change_state(GstElement *element, GstStateChange transition) +gst_cef_src_change_state(GstElement *src, GstStateChange transition) { GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; @@ -780,16 +779,16 @@ gst_cef_src_change_state(GstElement *element, GstStateChange transition) /* in the main thread as per Cocoa */ if (pthread_main_np()) { g_mutex_unlock (&init_lock); - run_cef ((GstCefSrc*) element); + run_cef ((GstCefSrc*) src); g_mutex_lock (&init_lock); } else { - dispatch_async_f(dispatch_get_main_queue(), (GstCefSrc*)element, (dispatch_function_t)&run_cef); + dispatch_async_f(dispatch_get_main_queue(), (GstCefSrc*)src, (dispatch_function_t)&run_cef); while (cef_status == CEF_STATUS_INITIALIZING) g_cond_wait (&init_cond, &init_lock); } #else /* in a separate UI thread */ - thread = g_thread_new("cef-ui-thread", (GThreadFunc) run_cef, (GstCefSrc*)element); + thread = g_thread_new("cef-ui-thread", (GThreadFunc) run_cef, (GstCefSrc*)src); while (cef_status == CEF_STATUS_INITIALIZING) g_cond_wait (&init_cond, &init_lock); #endif @@ -821,7 +820,7 @@ gst_cef_src_change_state(GstElement *element, GstStateChange transition) gst_cef_shutdown (nullptr); g_mutex_lock (&init_lock); } else { - dispatch_async_f(dispatch_get_main_queue(), (GstCefSrc*)element, (dispatch_function_t)&gst_cef_shutdown); + dispatch_async_f(dispatch_get_main_queue(), (GstCefSrc*)src, (dispatch_function_t)&gst_cef_shutdown); while (cef_status == CEF_STATUS_SHUTTING_DOWN) g_cond_wait (&init_cond, &init_lock); } @@ -842,7 +841,7 @@ gst_cef_src_change_state(GstElement *element, GstStateChange transition) } if (result == GST_STATE_CHANGE_FAILURE) return result; - result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + result = GST_ELEMENT_CLASS(parent_class)->change_state(src, transition); return result; } @@ -1209,7 +1208,8 @@ gst_cef_src_class_init (GstCefSrcClass * klass) gst_element_class_set_static_metadata (gstelement_class, "Chromium Embedded Framework source", "Source/Video", - "Creates a video stream from an embedded Chromium browser", "Mathieu Duponchelle "); + "Creates a video stream from an embedded Chromium browser", + "Mathieu Duponchelle "); gst_element_class_add_static_pad_template (gstelement_class, &gst_cef_src_template); From d46c13d0d297539fb236579f73ddb956a8c36a25 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Thu, 22 Aug 2024 20:23:22 +0100 Subject: [PATCH 6/9] signals: Add support for starting and stopping cefsrc from js See the sample in the html/ directory for an example usage. This should be useful for those webpages that have non-trivial load times (webgl apps, etc) - allowing the page to signal from javascript to the gst pipeline that the page is ready for display. We can also send an EOS signal to be turned into an event on cefsrc. Flag for enabling this feature is listen-for-js-signals, and then on the javascript side you can send signals (see html sample for detail): window.gstSendMsg({ request: "ready" | "eos", ...}) --- gstcefsrc.cc | 131 +++++++++++++++++++++++++++++++++---------- gstcefsrc.h | 17 +++++- html/ready_test.html | 90 +++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 32 deletions(-) create mode 100644 html/ready_test.html diff --git a/gstcefsrc.cc b/gstcefsrc.cc index 7e65b67..5ddd39d 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -1,4 +1,7 @@ #include +#include +#include + #ifdef __APPLE__ #include #include @@ -38,12 +41,13 @@ GST_DEBUG_CATEGORY_STATIC (cef_console_debug); #else #define DEFAULT_SANDBOX FALSE #endif +#define DEFAULT_LISTEN_FOR_JS_SIGNALS FALSE using CefStatus = enum : guint8 { // CEF was either unloaded successfully or not yet loaded. CEF_STATUS_NOT_LOADED = 0U, // Blocks other elements from initializing CEF is it's already in progress. - CEF_STATUS_INITIALIZING = 1U, + CEF_STATUS_INITIALIZING = 1U << 1U, // CEF's initialization process has completed successfully. CEF_STATUS_INITIALIZED = 1U << 2U, // No CEF elements will be allowed to complete initialization. @@ -55,7 +59,8 @@ using CefStatus = enum : guint8 { }; static CefStatus cef_status = CEF_STATUS_NOT_LOADED; static const guint8 CEF_STATUS_MASK_INITIALIZED = CEF_STATUS_FAILURE | CEF_STATUS_INITIALIZED; -static const guint8 CEF_STATUS_MASK_TRANSITIONING = CEF_STATUS_SHUTTING_DOWN | CEF_STATUS_INITIALIZING; +static const guint8 CEF_STATUS_MASK_TRANSITIONING = + CEF_STATUS_SHUTTING_DOWN | CEF_STATUS_INITIALIZING; // Number of running CEF instances. Setting this to 0 must be accompanied // with cef_shutdown to prevent leaks on application exit. @@ -103,6 +108,7 @@ enum PROP_CHROMIUM_DEBUG_PORT, PROP_CHROME_EXTRA_FLAGS, PROP_SANDBOX, + PROP_LISTEN_FOR_JS_SIGNAL, PROP_JS_FLAGS, PROP_LOG_SEVERITY, PROP_CEF_CACHE_LOCATION, @@ -133,31 +139,73 @@ gchar* get_plugin_base_path () { /** Handlers */ +// see https://bitbucket.org/chromiumembedded/cef-project/src/master/examples/message_router +// and https://bitbucket.org/chromiumembedded/cef/src/master/include/wrapper/cef_message_router.h +// for details of the message passing infrastructure in CEF // Handle messages in the browser process. class MessageHandler : public CefMessageRouterBrowserSide::Handler { public: - explicit MessageHandler(const CefString& startup_url) - : startup_url_(startup_url) {} + explicit MessageHandler(GstCefSrc* src) + : src(src) {} - // Called due to cefQuery execution in message_router.html. + // Called due to gstSendMsg execution in ready_test.html. bool OnQuery(CefRefPtr browser, CefRefPtr frame, int64_t query_id, const CefString& request, bool persistent, - CefRefPtr callback) override { - // Only handle messages from the startup URL. - const std::string& url = frame->GetURL(); - if (url.find(startup_url_) != 0) - return false; + CefRefPtr callback) override + { + if (!src) return false; + + // TODO: do we want to make the incoming payload json?? + bool success = false; + + if (request == "ready") { + g_mutex_lock (&src->state_lock); + if (src->state == CEF_SRC_WAITING_FOR_READY) { + src->state = CEF_SRC_READY; + g_cond_broadcast (&src->state_cond); + success = true; + } else { + std::ostringstream error_msg; + error_msg << "error: (" << request << ") - " << + "js ready signal sent with invalid cef state: " << cef_status; + GST_WARNING_OBJECT(src, "%s", error_msg.str().c_str()); + success = false; + } + g_mutex_unlock (&src->state_lock); + } else if (request == "eos") { + if (src) { + gst_element_send_event(GST_ELEMENT(src), gst_event_new_eos()); + success = true; + } + } + + // send json response back to js + std::ostringstream response; + response << + "{ " << + "\"success\": \"" << (success ? "true" : "false") << "\", " << + "\"cmd\": \"" << request << "\"" << + " }"; + if (success) { + GST_INFO_OBJECT( + src, "js signal processed successfully: %s", request.ToString().c_str() + ); + callback->Success(response.str()); + } else { + GST_WARNING_OBJECT( + src, "js signal processing error: %s", request.ToString().c_str() + ); + callback->Failure(0, response.str()); + } - const std::string& message_name = request; - callback->Success(message_name + " is working"); return true; } private: - const CefString startup_url_; + GstCefSrc* src; DISALLOW_COPY_AND_ASSIGN(MessageHandler); }; @@ -379,12 +427,14 @@ class BrowserClient : { CEF_REQUIRE_UI_THREAD(); - return browser_msg_router_->OnProcessMessageReceived( - browser, - frame, - source_process, - message - ); + return browser_msg_router_ + ? browser_msg_router_->OnProcessMessageReceived( + browser, + frame, + source_process, + message + ) + : false; } // CefLifeSpanHandler Methods: @@ -392,7 +442,7 @@ class BrowserClient : { CEF_REQUIRE_UI_THREAD(); - if (!browser_msg_router_) { + if (src->listen_for_js_signals && !browser_msg_router_) { // Create the browser-side router for query handling. CefMessageRouterConfig config; config.js_query_function = "gstSendMsg"; @@ -400,7 +450,7 @@ class BrowserClient : browser_msg_router_ = CefMessageRouterBrowserSide::Create(config); // Register handlers with the router. - browser_msg_handler_.reset(new MessageHandler(src->url)); + browser_msg_handler_.reset(new MessageHandler(src)); browser_msg_router_->AddHandler(browser_msg_handler_.get(), false); } } @@ -409,7 +459,7 @@ class BrowserClient : { src->browser = nullptr; g_mutex_lock (&src->state_lock); - src->started = FALSE; + src->state = CEF_SRC_CLOSED; g_cond_signal (&src->state_cond); g_mutex_unlock(&src->state_lock); g_mutex_lock(&init_lock); @@ -432,7 +482,7 @@ class BrowserClient : { CEF_REQUIRE_UI_THREAD(); - browser_msg_router_->OnBeforeBrowse(browser, frame); + if (browser_msg_router_) browser_msg_router_->OnBeforeBrowse(browser, frame); return false; } @@ -440,7 +490,7 @@ class BrowserClient : { CEF_REQUIRE_UI_THREAD(); GST_WARNING_OBJECT (src, "Render subprocess terminated, reloading URL!"); - browser_msg_router_->OnRenderProcessTerminated(browser); + if (browser_msg_router_) browser_msg_router_->OnRenderProcessTerminated(browser); browser->Reload(); } @@ -470,7 +520,7 @@ class BrowserClient : src->browser = browser; g_mutex_lock (&src->state_lock); - src->started = TRUE; + src->state = src->listen_for_js_signals ? CEF_SRC_WAITING_FOR_READY : CEF_SRC_OPEN; g_cond_signal (&src->state_cond); g_mutex_unlock(&src->state_lock); } @@ -746,7 +796,6 @@ run_cef (GstCefSrc *src) cef_status = CEF_STATUS_INITIALIZED; g_cond_broadcast (&init_cond); g_mutex_unlock (&init_lock); - #ifndef __APPLE__ CefRunMessageLoop(); gst_cef_shutdown(nullptr); @@ -792,7 +841,7 @@ gst_cef_src_change_state(GstElement *src, GstStateChange transition) while (cef_status == CEF_STATUS_INITIALIZING) g_cond_wait (&init_cond, &init_lock); #endif - if (cef_status != CEF_STATUS_INITIALIZED) { + if (cef_status & ~CEF_STATUS_MASK_INITIALIZED) { // BAIL OUT, CEF is not loaded. result = GST_STATE_CHANGE_FAILURE; #ifndef __APPLE__ @@ -875,13 +924,20 @@ gst_cef_src_start(GstBaseSrc *base_src) /* And wait for this src's browser to have been created */ g_mutex_lock(&src->state_lock); - while (!src->started) + while (!CefSrcStateIsOpen(src->state)) g_cond_wait (&src->state_cond, &src->state_lock); g_mutex_unlock (&src->state_lock); #ifdef __APPLE__ } #endif + if (src->listen_for_js_signals) { + g_mutex_lock (&src->state_lock); + while (src->state == CEF_SRC_WAITING_FOR_READY) + g_cond_wait (&src->state_cond, &src->state_lock); + g_mutex_unlock (&src->state_lock); + } + ret = src->browser != NULL; done: @@ -908,7 +964,7 @@ gst_cef_src_stop (GstBaseSrc *base_src) #endif /* And wait for this src's browser to have been closed */ g_mutex_lock(&src->state_lock); - while (src->started) + while (CefSrcStateIsOpen(src->state)) g_cond_wait (&src->state_cond, &src->state_lock); g_mutex_unlock (&src->state_lock); #ifdef __APPLE__ @@ -1022,7 +1078,7 @@ gst_cef_src_set_property (GObject * object, guint prop_id, const GValue * value, src->url = g_strdup (url); g_mutex_lock(&src->state_lock); - if (src->started) { + if (CefSrcStateIsOpen(src->state)) { src->browser->GetMainFrame()->LoadURL(src->url); } g_mutex_unlock(&src->state_lock); @@ -1049,6 +1105,11 @@ gst_cef_src_set_property (GObject * object, guint prop_id, const GValue * value, src->sandbox = g_value_get_boolean (value); break; } + case PROP_LISTEN_FOR_JS_SIGNAL: + { + src->listen_for_js_signals = g_value_get_boolean (value); + break; + } case PROP_JS_FLAGS: { g_free (src->js_flags); src->js_flags = g_value_dup_string (value); @@ -1091,6 +1152,9 @@ gst_cef_src_get_property (GObject * object, guint prop_id, GValue * value, case PROP_SANDBOX: g_value_set_boolean (value, src->sandbox); break; + case PROP_LISTEN_FOR_JS_SIGNAL: + g_value_set_boolean (value, src->listen_for_js_signals); + break; case PROP_JS_FLAGS: g_value_set_string (value, src->js_flags); break; @@ -1135,9 +1199,10 @@ gst_cef_src_init (GstCefSrc * src) src->current_buffer = NULL; src->audio_buffers = NULL; src->audio_events = NULL; - src->started = FALSE; + src->state = CEF_SRC_CLOSED; src->chromium_debug_port = DEFAULT_CHROMIUM_DEBUG_PORT; src->sandbox = DEFAULT_SANDBOX; + src->listen_for_js_signals = DEFAULT_LISTEN_FOR_JS_SIGNALS; src->js_flags = NULL; src->log_severity = DEFAULT_LOG_SEVERITY; src->cef_cache_location = NULL; @@ -1187,6 +1252,10 @@ gst_cef_src_class_init (GstCefSrcClass * klass) g_param_spec_boolean ("sandbox", "sandbox", "Toggle chromium sandboxing capabilities", DEFAULT_SANDBOX, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_LISTEN_FOR_JS_SIGNAL, + g_param_spec_boolean ("listen-for-js-signals", "listen-for-js-signals", + "Listen and respond to signals sent from javascript: window.gstSendMsg({request: \"ready|eos\", ...})", + DEFAULT_LISTEN_FOR_JS_SIGNALS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (gobject_class, PROP_JS_FLAGS, g_param_spec_string ("js-flags", "js-flags", diff --git a/gstcefsrc.h b/gstcefsrc.h index 0ebd28e..e7c46ee 100644 --- a/gstcefsrc.h +++ b/gstcefsrc.h @@ -29,6 +29,20 @@ G_BEGIN_DECLS typedef struct _GstCefSrc GstCefSrc; typedef struct _GstCefSrcClass GstCefSrcClass; +typedef enum : guint8 { + // browser app not yet initialized + CEF_SRC_CLOSED = 0, + // browser app initialized + CEF_SRC_OPEN = 1, + // following states only possible if `listen_for_js_signals`: + // waiting for CEF browser to send "ready" message (using window.gstSendMsg) + CEF_SRC_WAITING_FOR_READY = 2, + // received ready signal from webpage + CEF_SRC_READY = 3, +} CefSrcState; + +#define CefSrcStateIsOpen(state) (state >= CEF_SRC_OPEN) + struct _GstCefSrc { GstPushSrc parent; GstBuffer *current_buffer; @@ -44,13 +58,14 @@ struct _GstCefSrc { gchar *cef_cache_location; gboolean gpu; gboolean sandbox; + gboolean listen_for_js_signals; gint chromium_debug_port; CefRefPtr browser; CefRefPtr app; GCond state_cond; GMutex state_lock; - gboolean started; + CefSrcState state; }; struct _GstCefSrcClass { diff --git a/html/ready_test.html b/html/ready_test.html new file mode 100644 index 0000000..1aceb93 --- /dev/null +++ b/html/ready_test.html @@ -0,0 +1,90 @@ + + + Signals Sample + + + +
+ Responses: +
+
+ + + From 235d23c859e98ebdfcfbcb26dc954abc0a0cd574 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Tue, 24 Sep 2024 13:23:17 +0100 Subject: [PATCH 7/9] start: Add progress messages --- gstcefsrc.cc | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/gstcefsrc.cc b/gstcefsrc.cc index 5ddd39d..93a9df9 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -23,6 +23,16 @@ #include "gstcefnsapplication.h" #endif +#define GST_ELEMENT_PROGRESS(el, type, code, text) \ +G_STMT_START { \ + gchar *__txt = _gst_element_error_printf text; \ + gst_element_post_message (GST_ELEMENT_CAST (el), \ + gst_message_new_progress (GST_OBJECT_CAST (el), \ + GST_PROGRESS_TYPE_ ##type, code, __txt)); \ + g_free (__txt); \ +} G_STMT_END + + GST_DEBUG_CATEGORY_STATIC (cef_src_debug); #define GST_CAT_DEFAULT cef_src_debug @@ -900,7 +910,11 @@ gst_cef_src_start(GstBaseSrc *base_src) { gboolean ret = FALSE; GstCefSrc *src = GST_CEF_SRC (base_src); + + GST_ELEMENT_PROGRESS(src, START, "open", ("Creating CEF browser client")); + CefRefPtr browserClient = new BrowserClient(src); + gulong browser_id = browsers; /* Make sure CEF is initialized before posting a task */ g_mutex_lock (&init_lock); @@ -908,12 +922,17 @@ gst_cef_src_start(GstBaseSrc *base_src) g_cond_wait (&init_cond, &init_lock); g_mutex_unlock (&init_lock); - if (cef_status == CEF_STATUS_FAILURE) + if (cef_status == CEF_STATUS_FAILURE) { + GST_ELEMENT_PROGRESS(src, ERROR, "open", ("CEF in failed state")); goto done; + } GST_OBJECT_LOCK (src); src->n_frames = 0; GST_OBJECT_UNLOCK (src); + + GST_ELEMENT_PROGRESS(src, CONTINUE, "open", ("Creating CEF browser (#%lu)...", browser_id)); + #ifdef __APPLE__ if (pthread_main_np()) { /* in the main thread as per Cocoa */ @@ -923,6 +942,8 @@ gst_cef_src_start(GstBaseSrc *base_src) CefPostTask(TID_UI, base::BindOnce(&BrowserClient::MakeBrowser, browserClient.get(), 0)); /* And wait for this src's browser to have been created */ + GST_ELEMENT_PROGRESS(src, CONTINUE, "open", ("Waiting for CEF browser initialization...")); + g_mutex_lock(&src->state_lock); while (!CefSrcStateIsOpen(src->state)) g_cond_wait (&src->state_cond, &src->state_lock); @@ -931,6 +952,7 @@ gst_cef_src_start(GstBaseSrc *base_src) } #endif + if (src->listen_for_js_signals) { g_mutex_lock (&src->state_lock); while (src->state == CEF_SRC_WAITING_FOR_READY) @@ -940,6 +962,18 @@ gst_cef_src_start(GstBaseSrc *base_src) ret = src->browser != NULL; + if (ret) { + GST_ELEMENT_PROGRESS( + src, COMPLETE, "open", + ("CEF browser created (#%lu)", browser_id) + ); + } else { + GST_ELEMENT_PROGRESS( + src, ERROR, "open", + ("CEF browser failed to create (#%lu)", browser_id) + ); + } + done: return ret; } From 933290e7d7e353632955b016ab9cf54dfa9099e7 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Wed, 11 Sep 2024 13:35:46 +0100 Subject: [PATCH 8/9] signals: Improve sample html page --- html/ready_test.html | 130 ++++++++++++++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 31 deletions(-) diff --git a/html/ready_test.html b/html/ready_test.html index 1aceb93..debe597 100644 --- a/html/ready_test.html +++ b/html/ready_test.html @@ -1,29 +1,93 @@ Signals Sample + - -
- Responses: -
-
+ +
+
+ Signals: +
+
+ Responses: +
+
+
+ +
From e027f895110dd9e79a2d187cc79ffacbcce7ce43 Mon Sep 17 00:00:00 2001 From: Aiden Jeffrey Date: Tue, 24 Sep 2024 12:07:20 +0100 Subject: [PATCH 9/9] signals: Add docs --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++--- gstcefsrc.cc | 5 ++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 00b9c50..770c354 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ first be turned into a macOS app. To do this, you can follow these steps: ``` -After this, you can run the steps below, replacing `gst-launch-1.0` with +After this, you can run the steps below, replacing `gst-launch-1.0` with `/bin/gst-launch-1.0.app/Contents/MacOS/gst-launch-1.0`. ## Run @@ -118,7 +118,7 @@ gst-launch-1.0 playbin uri=web+https://www.soundcloud.com/platform/sama ## Docker GPU Acceleration -This is simply a hint/note for those who want to use this plugin in a docker container with GPU acceleration. Your particular setup may vary. The following was tested on Ubuntu 22.04 with a Nvidia GPU. This assumes you have installed the Nvidia drivers, docker, and the Nvidia Container Toolkit. You may also need to configure your xorg.conf within the container to use the Nvidia GPU. +This is simply a hint/note for those who want to use this plugin in a docker container with GPU acceleration. Your particular setup may vary. The following was tested on Ubuntu 22.04 with a Nvidia GPU. This assumes you have installed the Nvidia drivers, docker, and the Nvidia Container Toolkit. You may also need to configure your xorg.conf within the container to use the Nvidia GPU. - xserver-xorg-video-dummy is required in the container @@ -130,7 +130,7 @@ docker run --gpus all -v /usr/local/cuda:/usr/local/cuda --device=/dev/dri/card0 ``` Inside the container run: - + ``` shell Xorg -noreset +extension GLX +extension RANDR \+extension RENDER -logfile ./xserver.log vt1 :1 & ``` @@ -146,3 +146,43 @@ chrome-extra-flags="use-gl=egl, enable-gpu-rasterization,ignore-gpu-blocklist" \ ``` It is also helpful to run nvidia-smi or nvtop to verify the GPU is being used. Note it is possible to use the GPU within a kube pod as well. This has been tested and runs in a production environment on GKE using Container Optimized OS. + +## Javascript Signals + +There is an optional feature in the cefsrc element that allows the webpage to send signals to +cefsrc: + +- "ready" - signals that the page content is ready to be rendered, and the src will then start + capturing and pushing buffers +- "eos" - signals that the webpage has no more content and to push an EOS event out on the + cefsrc's src pad + +Two methods are attached to the javascript global `window` object: `gstSendMsg` and +`gstCancelMsg`. For example, here is how you would send the "ready" signal: + +```js +window.gstSendMsg({ + request: "ready", + onSuccess: function(response) { + try { + const json = JSON.parse(response); + showResult(response, json, "response"); + if (json && json.success) { + // message processed by cefsrc successfully + } + } catch (e) { + console.error("parse error", e); + } + }, + onFailure: function(error_code, error_message) { + try { + const json = JSON.parse(error_message); + showResult(response, JSON.stringify(json), "error"); + } catch (e) { + console.error("parse error", e); + } + } +}); +``` + +Refer to the [sample html page](html/ready_test.html) for more detail. diff --git a/gstcefsrc.cc b/gstcefsrc.cc index 93a9df9..57d9452 100644 --- a/gstcefsrc.cc +++ b/gstcefsrc.cc @@ -1288,7 +1288,10 @@ gst_cef_src_class_init (GstCefSrcClass * klass) DEFAULT_SANDBOX, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (gobject_class, PROP_LISTEN_FOR_JS_SIGNAL, g_param_spec_boolean ("listen-for-js-signals", "listen-for-js-signals", - "Listen and respond to signals sent from javascript: window.gstSendMsg({request: \"ready|eos\", ...})", + "Listen and respond to signals sent from javascript: " + "window.gstSendMsg({request: \"ready|eos\", ...}) - " + "see [README](https://github.com/centricular/gstcefsrc?tab=readme-ov-file#javascript-signals) " + "for more detail", DEFAULT_LISTEN_FOR_JS_SIGNALS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (gobject_class, PROP_JS_FLAGS,