diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp | 340 |
1 files changed, 176 insertions, 164 deletions
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp index 7dd894e28..9adaaa0bb 100644 --- a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp @@ -1,7 +1,8 @@ /* * Copyright (C) 2007 OpenedHand * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L + * Copyright (C) 2009, 2010, 2011, 2012, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -35,8 +36,8 @@ #include <glib.h> #include <gst/gst.h> #include <gst/video/gstvideometa.h> -#include <wtf/OwnPtr.h> -#include <wtf/gobject/GMutexLocker.h> +#include <wtf/Condition.h> +#include <wtf/RunLoop.h> using namespace WebCore; @@ -46,13 +47,8 @@ using namespace WebCore; #else #define GST_CAPS_FORMAT "{ xRGB, ARGB }" #endif -#if GST_CHECK_VERSION(1, 1, 0) -#define GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";" -#else -#define GST_FEATURED_CAPS -#endif -#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT) +#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";" GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT) static GstStaticPadTemplate s_sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(WEBKIT_VIDEO_SINK_PAD_CAPS)); @@ -62,35 +58,123 @@ GST_DEBUG_CATEGORY_STATIC(webkitVideoSinkDebug); enum { REPAINT_REQUESTED, + REPAINT_CANCELLED, LAST_SIGNAL }; -enum { - PROP_0, - PROP_CAPS -}; - static guint webkitVideoSinkSignals[LAST_SIGNAL] = { 0, }; -struct _WebKitVideoSinkPrivate { - GstBuffer* buffer; - guint timeoutId; - GMutex* bufferMutex; - GCond* dataCondition; +static void webkitVideoSinkRepaintRequested(WebKitVideoSink*, GstSample*); +static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink*, GstBuffer*); - GstVideoInfo info; +class VideoRenderRequestScheduler { +public: + VideoRenderRequestScheduler() +#if !USE(COORDINATED_GRAPHICS_THREADED) + : m_timer(RunLoop::main(), this, &VideoRenderRequestScheduler::render) +#endif + { +#if PLATFORM(GTK) && !USE(COORDINATED_GRAPHICS_THREADED) + // Use a higher priority than WebCore timers (G_PRIORITY_HIGH_IDLE + 20). + m_timer.setPriority(G_PRIORITY_HIGH_IDLE + 19); +#endif + } - GstCaps* currentCaps; + void start() + { + LockHolder locker(m_sampleMutex); + m_unlocked = false; + } + + void stop() + { + LockHolder locker(m_sampleMutex); + m_sample = nullptr; + m_unlocked = true; +#if !USE(COORDINATED_GRAPHICS_THREADED) + m_timer.stop(); + m_dataCondition.notifyOne(); +#endif + } + + void drain() + { + LockHolder locker(m_sampleMutex); + m_sample = nullptr; + } + + bool requestRender(WebKitVideoSink* sink, GstBuffer* buffer) + { + LockHolder locker(m_sampleMutex); + if (m_unlocked) + return true; + + m_sample = webkitVideoSinkRequestRender(sink, buffer); + if (!m_sample) + return false; + +#if USE(COORDINATED_GRAPHICS_THREADED) + auto sample = WTFMove(m_sample); + locker.unlockEarly(); + if (LIKELY(GST_IS_SAMPLE(sample.get()))) + webkitVideoSinkRepaintRequested(sink, sample.get()); +#else + m_sink = sink; + m_timer.startOneShot(0); + m_dataCondition.wait(m_sampleMutex); +#endif + return true; + } - // If this is TRUE all processing should finish ASAP +private: + +#if !USE(COORDINATED_GRAPHICS_THREADED) + void render() + { + LockHolder locker(m_sampleMutex); + GRefPtr<GstSample> sample = WTFMove(m_sample); + GRefPtr<WebKitVideoSink> sink = WTFMove(m_sink); + if (sample && !m_unlocked && LIKELY(GST_IS_SAMPLE(sample.get()))) + webkitVideoSinkRepaintRequested(sink.get(), sample.get()); + m_dataCondition.notifyOne(); + } +#endif + + Lock m_sampleMutex; + GRefPtr<GstSample> m_sample; + +#if !USE(COORDINATED_GRAPHICS_THREADED) + RunLoop::Timer<VideoRenderRequestScheduler> m_timer; + Condition m_dataCondition; + GRefPtr<WebKitVideoSink> m_sink; +#endif + + // If this is true all processing should finish ASAP // This is necessary because there could be a race between // unlock() and render(), where unlock() wins, signals the - // GCond, then render() tries to render a frame although + // Condition, then render() tries to render a frame although // everything else isn't running anymore. This will lead // to deadlocks because render() holds the stream lock. // - // Protected by the buffer mutex - bool unlocked; + // Protected by the sample mutex + bool m_unlocked { false }; +}; + +struct _WebKitVideoSinkPrivate { + _WebKitVideoSinkPrivate() + { + gst_video_info_init(&info); + } + + ~_WebKitVideoSinkPrivate() + { + if (currentCaps) + gst_caps_unref(currentCaps); + } + + VideoRenderRequestScheduler scheduler; + GstVideoInfo info; + GstCaps* currentCaps; }; #define webkit_video_sink_parent_class parent_class @@ -100,59 +184,29 @@ G_DEFINE_TYPE_WITH_CODE(WebKitVideoSink, webkit_video_sink, GST_TYPE_VIDEO_SINK, static void webkit_video_sink_init(WebKitVideoSink* sink) { sink->priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate); -#if GLIB_CHECK_VERSION(2, 31, 0) - sink->priv->dataCondition = new GCond; - g_cond_init(sink->priv->dataCondition); - sink->priv->bufferMutex = new GMutex; - g_mutex_init(sink->priv->bufferMutex); -#else - sink->priv->dataCondition = g_cond_new(); - sink->priv->bufferMutex = g_mutex_new(); -#endif - - gst_video_info_init(&sink->priv->info); + g_object_set(GST_BASE_SINK(sink), "enable-last-sample", FALSE, nullptr); + new (sink->priv) WebKitVideoSinkPrivate(); } -static gboolean webkitVideoSinkTimeoutCallback(gpointer data) +static void webkitVideoSinkRepaintRequested(WebKitVideoSink* sink, GstSample* sample) { - WebKitVideoSink* sink = reinterpret_cast<WebKitVideoSink*>(data); - WebKitVideoSinkPrivate* priv = sink->priv; - - WTF::GMutexLocker lock(priv->bufferMutex); - GstBuffer* buffer = priv->buffer; - priv->buffer = 0; - priv->timeoutId = 0; - - if (!buffer || priv->unlocked || UNLIKELY(!GST_IS_BUFFER(buffer))) { - g_cond_signal(priv->dataCondition); - return FALSE; - } - - g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, buffer); - gst_buffer_unref(buffer); - g_cond_signal(priv->dataCondition); + g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, sample); +} - return FALSE; +static void webkitVideoSinkRepaintCancelled(WebKitVideoSink* sink) +{ + g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_CANCELLED], 0); } -static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer) +static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink* sink, GstBuffer* buffer) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); WebKitVideoSinkPrivate* priv = sink->priv; - - WTF::GMutexLocker lock(priv->bufferMutex); - - if (priv->unlocked) - return GST_FLOW_OK; - - priv->buffer = gst_buffer_ref(buffer); + GRefPtr<GstSample> sample = adoptGRef(gst_sample_new(buffer, priv->currentCaps, nullptr, nullptr)); // The video info structure is valid only if the sink handled an allocation query. GstVideoFormat format = GST_VIDEO_INFO_FORMAT(&priv->info); - if (format == GST_VIDEO_FORMAT_UNKNOWN) { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } + if (format == GST_VIDEO_FORMAT_UNKNOWN) + return nullptr; #if !(USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)) // Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't. @@ -166,10 +220,8 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf GstBuffer* newBuffer = WebCore::createGstBuffer(buffer); // Check if allocation failed. - if (UNLIKELY(!newBuffer)) { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } + if (UNLIKELY(!newBuffer)) + return nullptr; // We don't use Color::premultipliedARGBFromColor() here because // one function call per video pixel is just too expensive: @@ -179,15 +231,13 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf GstVideoFrame destinationFrame; if (!gst_video_frame_map(&sourceFrame, &priv->info, buffer, GST_MAP_READ)) { - gst_buffer_unref(buffer); gst_buffer_unref(newBuffer); - return GST_FLOW_ERROR; + return nullptr; } if (!gst_video_frame_map(&destinationFrame, &priv->info, newBuffer, GST_MAP_WRITE)) { gst_video_frame_unmap(&sourceFrame); - gst_buffer_unref(buffer); gst_buffer_unref(newBuffer); - return GST_FLOW_ERROR; + return nullptr; } const guint8* source = static_cast<guint8*>(GST_VIDEO_FRAME_PLANE_DATA(&sourceFrame, 0)); @@ -215,87 +265,32 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf gst_video_frame_unmap(&sourceFrame); gst_video_frame_unmap(&destinationFrame); - gst_buffer_unref(buffer); - buffer = priv->buffer = newBuffer; - } -#endif - - // This should likely use a lower priority, but glib currently starves - // lower priority sources. - // See: https://bugzilla.gnome.org/show_bug.cgi?id=610830. - priv->timeoutId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, webkitVideoSinkTimeoutCallback, - gst_object_ref(sink), reinterpret_cast<GDestroyNotify>(gst_object_unref)); - g_source_set_name_by_id(priv->timeoutId, "[WebKit] webkitVideoSinkTimeoutCallback"); - - g_cond_wait(priv->dataCondition, priv->bufferMutex); - return GST_FLOW_OK; -} - -static void webkitVideoSinkDispose(GObject* object) -{ - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - WebKitVideoSinkPrivate* priv = sink->priv; - - if (priv->dataCondition) { -#if GLIB_CHECK_VERSION(2, 31, 0) - g_cond_clear(priv->dataCondition); - delete priv->dataCondition; -#else - g_cond_free(priv->dataCondition); -#endif - priv->dataCondition = 0; + sample = adoptGRef(gst_sample_new(newBuffer, priv->currentCaps, nullptr, nullptr)); + gst_buffer_unref(newBuffer); } - - if (priv->bufferMutex) { -#if GLIB_CHECK_VERSION(2, 31, 0) - g_mutex_clear(priv->bufferMutex); - delete priv->bufferMutex; -#else - g_mutex_free(priv->bufferMutex); #endif - priv->bufferMutex = 0; - } - G_OBJECT_CLASS(parent_class)->dispose(object); + return sample; } -static void webkitVideoSinkGetProperty(GObject* object, guint propertyId, GValue* value, GParamSpec* parameterSpec) +static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - WebKitVideoSinkPrivate* priv = sink->priv; - - switch (propertyId) { - case PROP_CAPS: { - GstCaps* caps = priv->currentCaps; - if (caps) - gst_caps_ref(caps); - g_value_take_boxed(value, caps); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, parameterSpec); - } + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + return sink->priv->scheduler.requestRender(sink, buffer) ? GST_FLOW_OK : GST_FLOW_ERROR; } -static void unlockBufferMutex(WebKitVideoSinkPrivate* priv) +static void webkitVideoSinkFinalize(GObject* object) { - WTF::GMutexLocker lock(priv->bufferMutex); - - if (priv->buffer) { - gst_buffer_unref(priv->buffer); - priv->buffer = 0; - } - - priv->unlocked = true; - - g_cond_signal(priv->dataCondition); + WEBKIT_VIDEO_SINK(object)->priv->~WebKitVideoSinkPrivate(); + G_OBJECT_CLASS(parent_class)->finalize(object); } static gboolean webkitVideoSinkUnlock(GstBaseSink* baseSink) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - unlockBufferMutex(sink->priv); + priv->scheduler.stop(); + webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink)); return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock, (baseSink), TRUE); } @@ -304,10 +299,7 @@ static gboolean webkitVideoSinkUnlockStop(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - { - WTF::GMutexLocker lock(priv->bufferMutex); - priv->unlocked = false; - } + priv->scheduler.start(); return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop, (baseSink), TRUE); } @@ -316,11 +308,11 @@ static gboolean webkitVideoSinkStop(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - unlockBufferMutex(priv); - + priv->scheduler.stop(); + webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink)); if (priv->currentCaps) { gst_caps_unref(priv->currentCaps); - priv->currentCaps = 0; + priv->currentCaps = nullptr; } return TRUE; @@ -330,8 +322,8 @@ static gboolean webkitVideoSinkStart(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - WTF::GMutexLocker lock(priv->bufferMutex); - priv->unlocked = false; + priv->scheduler.start(); + return TRUE; } @@ -357,7 +349,7 @@ static gboolean webkitVideoSinkSetCaps(GstBaseSink* baseSink, GstCaps* caps) static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery* query) { GstCaps* caps; - gst_query_parse_allocation(query, &caps, 0); + gst_query_parse_allocation(query, &caps, nullptr); if (!caps) return FALSE; @@ -365,14 +357,27 @@ static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery if (!gst_video_info_from_caps(&sink->priv->info, caps)) return FALSE; - gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, 0); - gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, 0); -#if GST_CHECK_VERSION(1, 1, 0) - gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, 0); -#endif + gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr); + gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, nullptr); + gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, nullptr); return TRUE; } +static gboolean webkitVideoSinkEvent(GstBaseSink* baseSink, GstEvent* event) +{ + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_FLUSH_START: { + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + sink->priv->scheduler.drain(); + + GST_DEBUG_OBJECT(sink, "Flush-start, releasing m_sample"); + } + FALLTHROUGH; + default: + return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, event, (baseSink, event), TRUE); + } +} + static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) { GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); @@ -384,8 +389,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) g_type_class_add_private(klass, sizeof(WebKitVideoSinkPrivate)); - gobjectClass->dispose = webkitVideoSinkDispose; - gobjectClass->get_property = webkitVideoSinkGetProperty; + gobjectClass->finalize = webkitVideoSinkFinalize; baseSinkClass->unlock = webkitVideoSinkUnlock; baseSinkClass->unlock_stop = webkitVideoSinkUnlockStop; @@ -395,9 +399,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) baseSinkClass->start = webkitVideoSinkStart; baseSinkClass->set_caps = webkitVideoSinkSetCaps; baseSinkClass->propose_allocation = webkitVideoSinkProposeAllocation; - - g_object_class_install_property(gobjectClass, PROP_CAPS, - g_param_spec_boxed("current-caps", "Current-Caps", "Current caps", GST_TYPE_CAPS, G_PARAM_READABLE)); + baseSinkClass->event = webkitVideoSinkEvent; webkitVideoSinkSignals[REPAINT_REQUESTED] = g_signal_new("repaint-requested", G_TYPE_FROM_CLASS(klass), @@ -408,13 +410,23 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) g_cclosure_marshal_generic, G_TYPE_NONE, // Return type 1, // Only one parameter - GST_TYPE_BUFFER); + GST_TYPE_SAMPLE); + webkitVideoSinkSignals[REPAINT_CANCELLED] = g_signal_new("repaint-cancelled", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, // Class offset + nullptr, // Accumulator + nullptr, // Accumulator data + g_cclosure_marshal_generic, + G_TYPE_NONE, // Return type + 0, // No parameters + G_TYPE_NONE); } GstElement* webkitVideoSinkNew() { - return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, 0)); + return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, nullptr)); } #endif // ENABLE(VIDEO) && USE(GSTREAMER) |