From 314a8c023fe7f722821c73a1d06ebf6f62dd8806 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 5 Aug 2020 18:41:45 +0100 Subject: [PATCH] wpe: WebView and WebContext handling fixes The WPEThreaded view is now split in 2 classes: - WPEContextThread handles the persistent WebKit thread, where all WebKit API calls should be handled. - WPEView: is created from the WPEContextThread. It handles the WebView and maintains the public interface on which wpesrc relies. This is the facade for the WebView, basically. It takes care of dispatching API calls into the context thread. With these fixes it is now possible to create (and reuse) mutlple wpesrc elements during the application lifetime. Fixes #1372 Part-of: --- ext/wpe/WPEThreadedView.cpp | 587 ++++++++++++++++++-------------------------- ext/wpe/WPEThreadedView.h | 71 ++++-- ext/wpe/gstwpesrc.cpp | 45 ++-- 3 files changed, 305 insertions(+), 398 deletions(-) diff --git a/ext/wpe/WPEThreadedView.cpp b/ext/wpe/WPEThreadedView.cpp index 98a54e6..4c8047b 100644 --- a/ext/wpe/WPEThreadedView.cpp +++ b/ext/wpe/WPEThreadedView.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) <2018> Philippe Normand +/* Copyright (C) <2018, 2019, 2020> Philippe Normand * Copyright (C) <2018> Žan Doberšek * * This library is free software; you can redistribute it and/or @@ -62,54 +62,33 @@ private: GMutex& m; }; -WPEThreadedView::WPEThreadedView() +static WPEContextThread *s_view = NULL; + +WPEContextThread& WPEContextThread::singleton() +{ + if (!s_view) + s_view = new WPEContextThread; + + return *s_view; +} + +WPEContextThread::WPEContextThread() { g_mutex_init(&threading.mutex); g_cond_init(&threading.cond); g_mutex_init(&threading.ready_mutex); g_cond_init(&threading.ready_cond); - g_mutex_init(&images_mutex); - { GMutexHolder lock(threading.mutex); - threading.thread = g_thread_new("WPEThreadedView", - s_viewThread, this); + threading.thread = g_thread_new("WPEContextThread", s_viewThread, this); g_cond_wait(&threading.cond, &threading.mutex); GST_DEBUG("thread spawned"); } } -WPEThreadedView::~WPEThreadedView() +WPEContextThread::~WPEContextThread() { - { - GMutexHolder lock(images_mutex); - - if (egl.pending) { - gst_egl_image_unref(egl.pending); - egl.pending = nullptr; - } - if (egl.committed) { - gst_egl_image_unref(egl.committed); - egl.committed = nullptr; - } - } - - { - GMutexHolder lock(threading.mutex); - wpe_view_backend_exportable_fdo_destroy(wpe.exportable); - } - - if (gst.display) { - gst_object_unref(gst.display); - gst.display = nullptr; - } - - if (gst.context) { - gst_object_unref(gst.context); - gst.context = nullptr; - } - if (threading.thread) { g_thread_unref(threading.thread); threading.thread = nullptr; @@ -119,12 +98,41 @@ WPEThreadedView::~WPEThreadedView() g_cond_clear(&threading.cond); g_mutex_clear(&threading.ready_mutex); g_cond_clear(&threading.ready_cond); - g_mutex_clear(&images_mutex); } -gpointer WPEThreadedView::s_viewThread(gpointer data) +template +void WPEContextThread::dispatch(Function func) { - auto& view = *static_cast(data); + struct Payload { + Function& func; + }; + struct Payload payload { func }; + + GSource* source = g_idle_source_new(); + g_source_set_callback(source, [](gpointer data) -> gboolean { + auto& view = WPEContextThread::singleton(); + GMutexHolder lock(view.threading.mutex); + + auto* payload = static_cast(data); + payload->func(); + + g_cond_signal(&view.threading.cond); + return G_SOURCE_REMOVE; + }, &payload, nullptr); + g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); + + { + GMutexHolder lock(threading.mutex); + g_source_attach(source, glib.context); + g_cond_wait(&threading.cond, &threading.mutex); + } + + g_source_unref(source); +} + +gpointer WPEContextThread::s_viewThread(gpointer data) +{ + auto& view = *static_cast(data); view.glib.context = g_main_context_new(); view.glib.loop = g_main_loop_new(view.glib.context, FALSE); @@ -135,7 +143,7 @@ gpointer WPEThreadedView::s_viewThread(gpointer data) GSource* source = g_idle_source_new(); g_source_set_callback(source, [](gpointer data) -> gboolean { - auto& view = *static_cast(data); + auto& view = *static_cast(data); GMutexHolder lock(view.threading.mutex); g_cond_signal(&view.threading.cond); return G_SOURCE_REMOVE; @@ -150,52 +158,13 @@ gpointer WPEThreadedView::s_viewThread(gpointer data) g_main_loop_unref(view.glib.loop); view.glib.loop = nullptr; - if (view.webkit.view) { - g_object_unref(view.webkit.view); - view.webkit.view = nullptr; - } - if (view.webkit.uri) { - g_free(view.webkit.uri); - view.webkit.uri = nullptr; - } - g_main_context_pop_thread_default(view.glib.context); g_main_context_unref(view.glib.context); view.glib.context = nullptr; return nullptr; } -struct wpe_view_backend* WPEThreadedView::backend() const -{ - return wpe.exportable ? wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable) : nullptr; -} - -struct InitializeContext { - GstWpeSrc* src; - WPEThreadedView& view; - GstGLContext* context; - GstGLDisplay* display; - EGLDisplay eglDisplay; - int width; - int height; - bool result; - gulong loadFailedHandler; -}; - -void WPEThreadedView::s_loadFailed(WebKitWebView*, WebKitLoadEvent event, gchar *failing_uri, GError *error, gpointer data) -{ - InitializeContext *ctx = (InitializeContext*) data; - GMutexHolder lock(ctx->view.threading.ready_mutex); - - - GST_ERROR_OBJECT (ctx->src, "Failed to load %s (%s)", failing_uri, error->message); - ctx->result = false; - - ctx->view.threading.ready = true; - g_cond_signal(&ctx->view.threading.ready_cond); -} - -bool WPEThreadedView::initialize(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) +WPEView* WPEContextThread::createWPEView(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) { GST_DEBUG("context %p display %p, size (%d,%d)", context, display, width, height); threading.ready = FALSE; @@ -208,105 +177,143 @@ bool WPEThreadedView::initialize(GstWpeSrc* src, GstGLContext* context, GstGLDis #endif }); + WPEView* view = nullptr; + dispatch([&]() mutable { + if (!glib.web_context) { + auto* manager = webkit_website_data_manager_new_ephemeral(); + glib.web_context = webkit_web_context_new_with_website_data_manager(manager); + g_object_unref(manager); + } + + view = new WPEView(glib.web_context, src, context, display, width, height); + }); + + if (view && view->hasUri()) { + GST_DEBUG("waiting load to finish"); + GMutexHolder lock(threading.ready_mutex); + while (!threading.ready) + g_cond_wait(&threading.ready_cond, &threading.ready_mutex); + GST_DEBUG("done"); + } + + return view; +} + +void WPEContextThread::notifyLoadFinished() +{ + GMutexHolder lock(threading.ready_mutex); + if (!threading.ready) { + threading.ready = TRUE; + g_cond_signal(&threading.ready_cond); + } +} + +static gboolean s_loadFailed(WebKitWebView*, WebKitLoadEvent, gchar* failing_uri, GError* error, gpointer data) +{ + GstWpeSrc* src = GST_WPE_SRC(data); + GST_ELEMENT_ERROR (GST_ELEMENT_CAST(src), RESOURCE, FAILED, (NULL), ("Failed to load %s (%s)", failing_uri, error->message)); + return FALSE; +} + +WPEView::WPEView(WebKitWebContext* web_context, GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) +{ + g_mutex_init(&images_mutex); + if (context) + gst.context = GST_GL_CONTEXT(gst_object_ref(context)); + if (display) + gst.display = GST_GL_DISPLAY(gst_object_ref(display)); + + wpe.width = width; + wpe.height = height; + EGLDisplay eglDisplay = EGL_NO_DISPLAY; if (context && display) - eglDisplay = gst_gl_display_egl_get_from_native(GST_GL_DISPLAY_TYPE_WAYLAND, - gst_gl_display_get_handle(display)); + eglDisplay = gst_gl_display_egl_get_from_native(GST_GL_DISPLAY_TYPE_WAYLAND, gst_gl_display_get_handle(display)); GST_DEBUG("eglDisplay %p", eglDisplay); - struct InitializeContext initializeContext { src, *this, context, display, eglDisplay, width, height, FALSE, 0 }; - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - GST_DEBUG("on view thread"); - auto& initializeContext = *static_cast(data); - auto& view = initializeContext.view; - - GMutexHolder lock(view.threading.mutex); - - if (initializeContext.context) - view.gst.context = GST_GL_CONTEXT(gst_object_ref(initializeContext.context)); - if (initializeContext.display) - view.gst.display = GST_GL_DISPLAY(gst_object_ref(initializeContext.display)); - - view.wpe.width = initializeContext.width; - view.wpe.height = initializeContext.height; - - if (initializeContext.eglDisplay) { - initializeContext.result = wpe_fdo_initialize_for_egl_display(initializeContext.eglDisplay); - GST_DEBUG("FDO EGL display initialisation result: %d", initializeContext.result); - } else { + if (eglDisplay) { + m_isValid = wpe_fdo_initialize_for_egl_display(eglDisplay); + GST_DEBUG("FDO EGL display initialisation result: %d", m_isValid); + } else { #if ENABLE_SHM_BUFFER_SUPPORT - initializeContext.result = wpe_fdo_initialize_shm(); - GST_DEBUG("FDO SHM initialisation result: %d", initializeContext.result); + m_isValid = wpe_fdo_initialize_shm(); + GST_DEBUG("FDO SHM initialisation result: %d", m_isValid); #else - GST_WARNING("FDO SHM support is available only in WPEBackend-FDO 1.7.0"); + GST_WARNING("FDO SHM support is available only in WPEBackend-FDO 1.7.0"); #endif - } - if (!initializeContext.result) { - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - } - - if (initializeContext.eglDisplay) { - view.wpe.exportable = wpe_view_backend_exportable_fdo_egl_create(&s_exportableEGLClient, - &view, view.wpe.width, view.wpe.height); - } else { + } + if (!m_isValid) + return; + + if (eglDisplay) { + wpe.exportable = wpe_view_backend_exportable_fdo_egl_create(&s_exportableEGLClient, this, wpe.width, wpe.height); + } else { #if ENABLE_SHM_BUFFER_SUPPORT - view.wpe.exportable = wpe_view_backend_exportable_fdo_create(&s_exportableClient, - &view, view.wpe.width, view.wpe.height); + wpe.exportable = wpe_view_backend_exportable_fdo_create(&s_exportableClient, this, wpe.width, wpe.height); #endif - } - auto* wpeViewBackend = wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable); - auto* viewBackend = webkit_web_view_backend_new(wpeViewBackend, nullptr, nullptr); + } + + auto* wpeViewBackend = wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable); + auto* viewBackend = webkit_web_view_backend_new(wpeViewBackend, (GDestroyNotify) wpe_view_backend_exportable_fdo_destroy, wpe.exportable); #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(1, 1, 0) - wpe_view_backend_add_activity_state(wpeViewBackend, wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window); + wpe_view_backend_add_activity_state(wpeViewBackend, wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window); #endif - view.webkit.view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, - "backend", viewBackend, nullptr)); + webkit.view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", web_context, "backend", viewBackend, nullptr)); - gst_wpe_src_configure_web_view(initializeContext.src, view.webkit.view); + g_signal_connect(webkit.view, "load-failed", G_CALLBACK(s_loadFailed), src); + g_signal_connect(webkit.view, "load-failed-with-tls-errors", G_CALLBACK(s_loadFailed), src); - initializeContext.loadFailedHandler = g_signal_connect(view.webkit.view, - "load-failed", G_CALLBACK(s_loadFailed), &initializeContext); + gst_wpe_src_configure_web_view(src, webkit.view); - const gchar* location; - gboolean drawBackground = TRUE; - g_object_get(initializeContext.src, "location", &location, "draw-background", &drawBackground, nullptr); - view.setDrawBackground(drawBackground); - if (location) - view.loadUriUnlocked(location); - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - &initializeContext, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); + const gchar* location; + gboolean drawBackground = TRUE; + g_object_get(src, "location", &location, "draw-background", &drawBackground, nullptr); + setDrawBackground(drawBackground); + if (location) + loadUriUnlocked(location); +} +WPEView::~WPEView() +{ { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); + GMutexHolder lock(images_mutex); + + if (egl.pending) { + gst_egl_image_unref(egl.pending); + egl.pending = nullptr; + } + if (egl.committed) { + gst_egl_image_unref(egl.committed); + egl.committed = nullptr; + } } - g_source_unref(source); + WPEContextThread::singleton().dispatch([&]() { + if (webkit.view) { + g_object_unref(webkit.view); + webkit.view = nullptr; + } + }); - if (initializeContext.result && webkit.uri) { - GST_DEBUG("waiting load to finish"); - GMutexHolder lock(threading.ready_mutex); - while (!threading.ready) - g_cond_wait(&threading.ready_cond, &threading.ready_mutex); - GST_DEBUG("done"); + if (gst.display) { + gst_object_unref(gst.display); + gst.display = nullptr; } - if (initializeContext.loadFailedHandler) - g_signal_handler_disconnect (webkit.view, initializeContext.loadFailedHandler); + if (gst.context) { + gst_object_unref(gst.context); + gst.context = nullptr; + } + if (webkit.uri) { + g_free(webkit.uri); + webkit.uri = nullptr; + } - return initializeContext.result; + g_mutex_clear(&images_mutex); } -GstEGLImage* WPEThreadedView::image() +GstEGLImage* WPEView::image() { GstEGLImage* ret = nullptr; bool dispatchFrameComplete = false; @@ -339,7 +346,7 @@ GstEGLImage* WPEThreadedView::image() return ret; } -GstBuffer* WPEThreadedView::buffer() +GstBuffer* WPEView::buffer() { GstBuffer* ret = nullptr; bool dispatchFrameComplete = false; @@ -372,66 +379,28 @@ GstBuffer* WPEThreadedView::buffer() return ret; } -void WPEThreadedView::resize(int width, int height) +void WPEView::resize(int width, int height) { GST_DEBUG("resize to %dx%d", width, height); wpe.width = width; wpe.height = height; - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - auto& view = *static_cast(data); - GMutexHolder lock(view.threading.mutex); - - GST_DEBUG("dispatching"); - if (view.wpe.exportable && wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable)) - wpe_view_backend_dispatch_set_size(wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), view.wpe.width, view.wpe.height); - - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - this, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - } - - g_source_unref(source); + s_view->dispatch([&]() { + if (wpe.exportable && wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable)) + wpe_view_backend_dispatch_set_size(wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable), wpe.width, wpe.height); + }); } -void WPEThreadedView::frameComplete() +void WPEView::frameComplete() { GST_TRACE("frame complete"); - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - auto& view = *static_cast(data); - GMutexHolder lock(view.threading.mutex); - - GST_TRACE("dispatching"); - wpe_view_backend_exportable_fdo_dispatch_frame_complete(view.wpe.exportable); - - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - this, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - } - - g_source_unref(source); + s_view->dispatch([&]() { + GST_TRACE("dispatching"); + wpe_view_backend_exportable_fdo_dispatch_frame_complete(wpe.exportable); + }); } -void WPEThreadedView::loadUriUnlocked(const gchar* uri) +void WPEView::loadUriUnlocked(const gchar* uri) { if (webkit.uri) g_free(webkit.uri); @@ -441,74 +410,22 @@ void WPEThreadedView::loadUriUnlocked(const gchar* uri) webkit_web_view_load_uri(webkit.view, webkit.uri); } -void WPEThreadedView::loadUri(const gchar* uri) +void WPEView::loadUri(const gchar* uri) { - struct UriContext { - WPEThreadedView& view; - const gchar* uri; - } uriContext { *this, uri }; - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - GST_DEBUG("on view thread"); - auto& uriContext = *static_cast(data); - auto& view = uriContext.view; - GMutexHolder lock(view.threading.mutex); - - view.loadUriUnlocked(uriContext.uri); - - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - &uriContext, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - GST_DEBUG("done"); - } - - g_source_unref(source); + s_view->dispatch([&]() { + loadUriUnlocked(uri); + }); } -void WPEThreadedView::loadData(GBytes* bytes) +void WPEView::loadData(GBytes* bytes) { - struct DataContext { - WPEThreadedView& view; - GBytes* bytes; - } dataContext { *this, g_bytes_ref(bytes) }; - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - GST_DEBUG("on view thread"); - auto& dataContext = *static_cast(data); - auto& view = dataContext.view; - GMutexHolder lock(view.threading.mutex); - - webkit_web_view_load_bytes(view.webkit.view, dataContext.bytes, nullptr, nullptr, nullptr); - g_bytes_unref(dataContext.bytes); - - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - &dataContext, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - GST_DEBUG("done"); - } - - g_source_unref(source); + s_view->dispatch([this, bytes = g_bytes_ref(bytes)]() { + webkit_web_view_load_bytes(webkit.view, bytes, nullptr, nullptr, nullptr); + g_bytes_unref(bytes); + }); } -void WPEThreadedView::setDrawBackground(gboolean drawsBackground) +void WPEView::setDrawBackground(gboolean drawsBackground) { #if WEBKIT_CHECK_VERSION(2, 24, 0) GST_DEBUG("%s background rendering", drawsBackground ? "Enabling" : "Disabling"); @@ -520,49 +437,26 @@ void WPEThreadedView::setDrawBackground(gboolean drawsBackground) #endif } -void WPEThreadedView::releaseImage(gpointer imagePointer) +void WPEView::releaseImage(gpointer imagePointer) { - struct ReleaseImageContext { - WPEThreadedView& view; - gpointer imagePointer; - } releaseImageContext{ *this, imagePointer }; - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - auto& releaseImageContext = *static_cast(data); - auto& view = releaseImageContext.view; - GMutexHolder lock(view.threading.mutex); - - GST_TRACE("Dispatch release exported image %p", releaseImageContext.imagePointer); + s_view->dispatch([&]() { + GST_TRACE("Dispatch release exported image %p", imagePointer); #if USE_DEPRECATED_FDO_EGL_IMAGE - wpe_view_backend_exportable_fdo_egl_dispatch_release_image(releaseImageContext.view.wpe.exportable, - static_cast(releaseImageContext.imagePointer)); + wpe_view_backend_exportable_fdo_egl_dispatch_release_image(wpe.exportable, + static_cast(imagePointer)); #else - wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(releaseImageContext.view.wpe.exportable, - static_cast(releaseImageContext.imagePointer)); + wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(wpe.exportable, + static_cast(imagePointer)); #endif - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - &releaseImageContext, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - } - - g_source_unref(source); + }); } struct ImageContext { - WPEThreadedView* view; + WPEView* view; gpointer image; }; -void WPEThreadedView::handleExportedImage(gpointer image) +void WPEView::handleExportedImage(gpointer image) { ImageContext* imageContext = g_slice_new(ImageContext); imageContext->view = this; @@ -581,63 +475,34 @@ void WPEThreadedView::handleExportedImage(gpointer image) GST_TRACE("EGLImage %p wrapped in GstEGLImage %" GST_PTR_FORMAT, eglImage, gstImage); egl.pending = gstImage; - { - GMutexHolder lock(threading.ready_mutex); - if (!threading.ready) { - threading.ready = TRUE; - g_cond_signal(&threading.ready_cond); - } - } + s_view->notifyLoadFinished(); } } #if ENABLE_SHM_BUFFER_SUPPORT struct SHMBufferContext { - WPEThreadedView* view; - struct wpe_fdo_shm_exported_buffer* buffer; + WPEView* view; + struct wpe_fdo_shm_exported_buffer* buffer; }; -void WPEThreadedView::releaseSHMBuffer(gpointer data) +void WPEView::releaseSHMBuffer(gpointer data) { SHMBufferContext* context = static_cast(data); - struct ReleaseBufferContext { - WPEThreadedView& view; - SHMBufferContext* context; - } releaseImageContext{ *this, context }; - - GSource* source = g_idle_source_new(); - g_source_set_callback(source, - [](gpointer data) -> gboolean { - auto& releaseBufferContext = *static_cast(data); - auto& view = releaseBufferContext.view; - GMutexHolder lock(view.threading.mutex); - - struct wpe_fdo_shm_exported_buffer* buffer = static_cast(releaseBufferContext.context->buffer); - GST_TRACE("Dispatch release exported buffer %p", buffer); - wpe_view_backend_exportable_fdo_dispatch_release_shm_exported_buffer(view.wpe.exportable, buffer); - g_cond_signal(&view.threading.cond); - return G_SOURCE_REMOVE; - }, - &releaseImageContext, nullptr); - g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY); - - { - GMutexHolder lock(threading.mutex); - g_source_attach(source, glib.context); - g_cond_wait(&threading.cond, &threading.mutex); - } - - g_source_unref(source); + s_view->dispatch([&]() { + auto* buffer = static_cast(context->buffer); + GST_TRACE("Dispatch release exported buffer %p", buffer); + wpe_view_backend_exportable_fdo_dispatch_release_shm_exported_buffer(wpe.exportable, buffer); + }); } -void WPEThreadedView::s_releaseSHMBuffer(gpointer data) +void WPEView::s_releaseSHMBuffer(gpointer data) { SHMBufferContext* context = static_cast(data); context->view->releaseSHMBuffer(data); g_slice_free(SHMBufferContext, context); } -void WPEThreadedView::handleExportedBuffer(struct wpe_fdo_shm_exported_buffer* buffer) +void WPEView::handleExportedBuffer(struct wpe_fdo_shm_exported_buffer* buffer) { struct wl_shm_buffer* shmBuffer = wpe_fdo_shm_exported_buffer_get_shm_buffer(buffer); auto format = wl_shm_buffer_get_format(shmBuffer); @@ -667,22 +532,16 @@ void WPEThreadedView::handleExportedBuffer(struct wpe_fdo_shm_exported_buffer* b GMutexHolder lock(images_mutex); GST_TRACE("SHM buffer %p wrapped in buffer %" GST_PTR_FORMAT, buffer, gstBuffer); shm.pending = gstBuffer; - { - GMutexHolder lock(threading.ready_mutex); - if (!threading.ready) { - threading.ready = TRUE; - g_cond_signal(&threading.ready_cond); - } - } + s_view->notifyLoadFinished(); } } #endif -struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableEGLClient = { +struct wpe_view_backend_exportable_fdo_egl_client WPEView::s_exportableEGLClient = { #if USE_DEPRECATED_FDO_EGL_IMAGE // export_egl_image [](void* data, EGLImageKHR image) { - auto& view = *static_cast(data); + auto& view = *static_cast(data); view.handleExportedImage(static_cast(image)); }, nullptr, nullptr, @@ -690,7 +549,7 @@ struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableE // export_egl_image nullptr, [](void* data, struct wpe_fdo_egl_exported_image* image) { - auto& view = *static_cast(data); + auto& view = *static_cast(data); view.handleExportedImage(static_cast(image)); }, nullptr, @@ -700,12 +559,12 @@ struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableE }; #if ENABLE_SHM_BUFFER_SUPPORT -struct wpe_view_backend_exportable_fdo_client WPEThreadedView::s_exportableClient = { +struct wpe_view_backend_exportable_fdo_client WPEView::s_exportableClient = { nullptr, nullptr, // export_shm_buffer [](void* data, struct wpe_fdo_shm_exported_buffer* buffer) { - auto& view = *static_cast(data); + auto& view = *static_cast(data); view.handleExportedBuffer(buffer); }, nullptr, @@ -713,9 +572,35 @@ struct wpe_view_backend_exportable_fdo_client WPEThreadedView::s_exportableClien }; #endif -void WPEThreadedView::s_releaseImage(GstEGLImage* image, gpointer data) +void WPEView::s_releaseImage(GstEGLImage* image, gpointer data) { ImageContext* context = static_cast(data); context->view->releaseImage(context->image); g_slice_free(ImageContext, context); } + +struct wpe_view_backend* WPEView::backend() const +{ + return wpe.exportable ? wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable) : nullptr; +} + +void WPEView::dispatchKeyboardEvent(struct wpe_input_keyboard_event& wpe_event) +{ + s_view->dispatch([&]() { + wpe_view_backend_dispatch_keyboard_event(backend(), &wpe_event); + }); +} + +void WPEView::dispatchPointerEvent(struct wpe_input_pointer_event& wpe_event) +{ + s_view->dispatch([&]() { + wpe_view_backend_dispatch_pointer_event(backend(), &wpe_event); + }); +} + +void WPEView::dispatchAxisEvent(struct wpe_input_axis_event& wpe_event) +{ + s_view->dispatch([&]() { + wpe_view_backend_dispatch_axis_event(backend(), &wpe_event); + }); +} diff --git a/ext/wpe/WPEThreadedView.h b/ext/wpe/WPEThreadedView.h index ca5a66d..359e80e 100644 --- a/ext/wpe/WPEThreadedView.h +++ b/ext/wpe/WPEThreadedView.h @@ -37,22 +37,28 @@ typedef struct _GstEGLImage GstEGLImage; #define ENABLE_SHM_BUFFER_SUPPORT 0 #endif -class WPEThreadedView { +class WPEView { public: - WPEThreadedView(); - ~WPEThreadedView(); + WPEView(WebKitWebContext*, GstWpeSrc*, GstGLContext*, GstGLDisplay*, int width, int height); + ~WPEView(); - bool initialize(GstWpeSrc*, GstGLContext*, GstGLDisplay*, int width, int height); + bool operator!() const { return m_isValid; } + /* Used by wpesrc */ void resize(int width, int height); void loadUri(const gchar*); void loadData(GBytes*); void setDrawBackground(gboolean); - GstEGLImage* image(); GstBuffer* buffer(); - struct wpe_view_backend* backend() const; + void dispatchKeyboardEvent(struct wpe_input_keyboard_event&); + void dispatchPointerEvent(struct wpe_input_pointer_event&); + void dispatchAxisEvent(struct wpe_input_axis_event&); + + /* Used by WPEContextThread */ + bool hasUri() const { return webkit.uri; } + void disconnectLoadFailedSignal(); protected: void handleExportedImage(gpointer); @@ -61,6 +67,7 @@ protected: #endif private: + struct wpe_view_backend* backend() const; void frameComplete(); void loadUriUnlocked(const gchar*); @@ -70,23 +77,6 @@ private: static void s_releaseSHMBuffer(gpointer); #endif - static void s_loadFailed(WebKitWebView*, WebKitLoadEvent, gchar*, GError*, gpointer); - - static gpointer s_viewThread(gpointer); - struct { - GMutex mutex; - GCond cond; - GMutex ready_mutex; - GCond ready_cond; - gboolean ready; - GThread* thread { nullptr }; - } threading; - - struct { - GMainContext* context; - GMainLoop* loop; - } glib { nullptr, nullptr }; - struct { GstGLContext* context; GstGLDisplay* display; @@ -109,6 +99,8 @@ private: WebKitWebView* view; } webkit = { nullptr, nullptr }; + bool m_isValid { false }; + // This mutex guards access to either egl or shm resources declared below, // depending on the runtime behavior. GMutex images_mutex; @@ -122,4 +114,37 @@ private: GstBuffer* pending; GstBuffer* committed; } shm { nullptr, nullptr }; + +}; + +class WPEContextThread { +public: + static WPEContextThread& singleton(); + + WPEContextThread(); + ~WPEContextThread(); + + WPEView* createWPEView(GstWpeSrc*, GstGLContext*, GstGLDisplay*, int width, int height); + + template + void dispatch(Function); + + void notifyLoadFinished(); + +private: + static gpointer s_viewThread(gpointer); + struct { + GMutex mutex; + GCond cond; + GMutex ready_mutex; + GCond ready_cond; + gboolean ready; + GThread* thread { nullptr }; + } threading; + + struct { + GMainContext* context; + GMainLoop* loop; + WebKitWebContext* web_context; + } glib { nullptr, nullptr, nullptr }; }; diff --git a/ext/wpe/gstwpesrc.cpp b/ext/wpe/gstwpesrc.cpp index 0670983..f303a3d 100644 --- a/ext/wpe/gstwpesrc.cpp +++ b/ext/wpe/gstwpesrc.cpp @@ -108,8 +108,6 @@ struct _GstWpeSrc { GstGLBaseSrc parent; - WPEThreadedView *view; - /* properties */ gchar *location; gboolean draw_background; @@ -118,6 +116,8 @@ struct _GstWpeSrc gboolean gl_enabled; gint64 n_frames; /* total frames sent */ + + WPEView *view; }; static void gst_wpe_src_uri_handler_init (gpointer iface, gpointer data); @@ -237,7 +237,6 @@ static gboolean gst_wpe_src_gl_start (GstGLBaseSrc * base_src) { GstWpeSrc *src = GST_WPE_SRC (base_src); - gboolean result = TRUE; GstCapsFeatures *caps_features; GstGLContext *context = NULL; GstGLDisplay *display = NULL; @@ -256,10 +255,17 @@ gst_wpe_src_gl_start (GstGLBaseSrc * base_src) GST_DEBUG_OBJECT (src, "Will fill GLMemories: %d\n", src->gl_enabled); - src->view = new WPEThreadedView; - result = src->view->initialize (src, context, display, - GST_VIDEO_INFO_WIDTH (&base_src->out_info), - GST_VIDEO_INFO_HEIGHT (&base_src->out_info)); + auto & thread = WPEContextThread::singleton (); + src->view = thread.createWPEView (src, context, display, + GST_VIDEO_INFO_WIDTH (&base_src->out_info), + GST_VIDEO_INFO_HEIGHT (&base_src->out_info)); + + if (!src->view) { + GST_OBJECT_UNLOCK (src); + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("WPEBackend-FDO EGL display initialisation failed"), (NULL)); + return FALSE; + } if (src->bytes != NULL) { src->view->loadData (src->bytes); @@ -268,13 +274,8 @@ gst_wpe_src_gl_start (GstGLBaseSrc * base_src) } src->n_frames = 0; - GST_OBJECT_UNLOCK (src); - if (!result) { - GST_ELEMENT_ERROR (src, RESOURCE, FAILED, - ("WPEBackend-FDO EGL display initialisation failed"), (NULL)); - } - return result; + return TRUE; } static void @@ -441,8 +442,7 @@ gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) wpe_event.pressed = gst_navigation_event_get_type (event) == GST_NAVIGATION_EVENT_KEY_PRESS; - wpe_view_backend_dispatch_keyboard_event (src->view->backend (), - &wpe_event); + src->view->dispatchKeyboardEvent (wpe_event); ret = TRUE; } break; @@ -470,8 +470,7 @@ gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) wpe_event.state = gst_navigation_event_get_type (event) == GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS; - wpe_view_backend_dispatch_pointer_event (src->view->backend (), - &wpe_event); + src->view->dispatchPointerEvent (wpe_event); ret = TRUE; } break; @@ -482,8 +481,7 @@ gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) wpe_event.type = wpe_input_pointer_event_type_motion; wpe_event.x = (int) x; wpe_event.y = (int) y; - wpe_view_backend_dispatch_pointer_event (src->view->backend (), - &wpe_event); + src->view->dispatchPointerEvent (wpe_event); ret = TRUE; } break; @@ -500,12 +498,12 @@ gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) wpe_event.base.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); wpe_event.base.type = - static_cast(wpe_input_axis_event_type_mask_2d | + static_cast < wpe_input_axis_event_type > + (wpe_input_axis_event_type_mask_2d | wpe_input_axis_event_type_motion_smooth); wpe_event.base.x = (int) x; wpe_event.base.y = (int) y; - wpe_view_backend_dispatch_axis_event (src->view->backend (), - &wpe_event.base); + src->view->dispatchAxisEvent (wpe_event.base); #else struct wpe_input_axis_event wpe_event; if (delta_x) { @@ -519,8 +517,7 @@ gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) wpe_event.type = wpe_input_axis_event_type_motion; wpe_event.x = (int) x; wpe_event.y = (int) y; - wpe_view_backend_dispatch_axis_event (src->view->backend (), - &wpe_event); + src->view->dispatchAxisEvent (wpe_event); #endif ret = TRUE; } -- 2.7.4