diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index 615348c1..31ac94e8 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -222,6 +222,17 @@ bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) } } +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_flush = true; + m_renderBuffer = 0; + m_renderCondition.wakeAll(); + + notify(); +} + GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) { QMutexLocker locker(&m_mutex); @@ -388,6 +399,8 @@ QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface * sink->delegate = new QVideoSurfaceGstDelegate(surface); + g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); + return sink; } @@ -472,13 +485,41 @@ void QGstVideoRendererSink::finalize(GObject *object) G_OBJECT_CLASS(sink_parent_class)->finalize(object); } +void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstVideoRendererSink *sink = reinterpret_cast(d); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, NULL); + + if (!showPrerollFrame) { + GstState state = GST_STATE_VOID_PENDING; + gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); + // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means + // the QMediaPlayer was stopped from the paused state. + // We need to flush the current frame. + if (state == GST_STATE_PAUSED) + sink->delegate->flush(); + } +} + GstStateChangeReturn QGstVideoRendererSink::change_state( GstElement *element, GstStateChange transition) { - Q_UNUSED(element); + QGstVideoRendererSink *sink = reinterpret_cast(element); - return GST_ELEMENT_CLASS(sink_parent_class)->change_state( - element, transition); + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, NULL); + + // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to + // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. + // We need to flush the current frame. + if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) + sink->delegate->flush(); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); } GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 4a786ea1..737bc648 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -184,6 +184,21 @@ void QVideoSurfaceGstDelegate::clearPoolBuffers() m_pool->clear(); } +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_frame = QVideoFrame(); + m_renderCondition.wakeAll(); + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->present(m_frame); + } else { + QMetaObject::invokeMethod(this, "queuedFlush", Qt::QueuedConnection); + } +} + GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) { if (!m_surface) { @@ -244,6 +259,14 @@ void QVideoSurfaceGstDelegate::queuedStop() m_setupCondition.wakeAll(); } +void QVideoSurfaceGstDelegate::queuedFlush() +{ + QMutexLocker locker(&m_mutex); + + if (!m_surface.isNull()) + m_surface->present(QVideoFrame()); +} + void QVideoSurfaceGstDelegate::queuedRender() { QMutexLocker locker(&m_mutex); @@ -316,6 +339,8 @@ QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *su sink->delegate = new QVideoSurfaceGstDelegate(surface); + g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); + return sink; } @@ -420,13 +445,40 @@ void QVideoSurfaceGstSink::finalize(GObject *object) G_OBJECT_CLASS(sink_parent_class)->finalize(object); } -GstStateChangeReturn QVideoSurfaceGstSink::change_state( - GstElement *element, GstStateChange transition) +void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) { - Q_UNUSED(element); + Q_UNUSED(o); + Q_UNUSED(p); + QVideoSurfaceGstSink *sink = reinterpret_cast(d); - return GST_ELEMENT_CLASS(sink_parent_class)->change_state( - element, transition); + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, NULL); + + if (!showPrerollFrame) { + GstState state = GST_STATE_VOID_PENDING; + gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); + // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means + // the QMediaPlayer was stopped from the paused state. + // We need to flush the current frame. + if (state == GST_STATE_PAUSED) + sink->delegate->flush(); + } +} + +GstStateChangeReturn QVideoSurfaceGstSink::change_state(GstElement *element, GstStateChange transition) +{ + QVideoSurfaceGstSink *sink = reinterpret_cast(element); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, NULL); + + // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to + // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. + // We need to flush the current frame. + if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) + sink->delegate->flush(); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); } GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) diff --git a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h index 18670887..78bdf8cb 100644 --- a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h +++ b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h @@ -99,6 +99,8 @@ public: void unlock(); bool proposeAllocation(GstQuery *query); + void flush(); + GstFlowReturn render(GstBuffer *buffer); bool event(QEvent *event); @@ -145,6 +147,8 @@ private: static void finalize(GObject *object); + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h index e8f61afe..a1ef5616 100644 --- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h @@ -96,11 +96,14 @@ public: QMutex *poolMutex() { return &m_poolMutex; } void clearPoolBuffers(); + void flush(); + GstFlowReturn render(GstBuffer *buffer); private slots: void queuedStart(); void queuedStop(); + void queuedFlush(); void queuedRender(); void updateSupportedFormats(); @@ -139,6 +142,8 @@ private: static void finalize(GObject *object); + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); static GstCaps *get_caps(GstBaseSink *sink);