GStreamer: flush the current frame when stopping a media player.
When stopping, we don't actually stop the GStreamer pipeline, we just pause it and prevent preroll frames from being shown. We also need to make sure the last presented frame is cleared in that case, otherwise it stays on screen. Fixed for both 0.10 and 1.0. Change-Id: Ibe26a7567f271ae0c3d8819eb9d35d6a95da1c6a Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
@@ -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)
|
GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
@@ -388,6 +399,8 @@ QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface *
|
|||||||
|
|
||||||
sink->delegate = new QVideoSurfaceGstDelegate(surface);
|
sink->delegate = new QVideoSurfaceGstDelegate(surface);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink);
|
||||||
|
|
||||||
return sink;
|
return sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,13 +485,41 @@ void QGstVideoRendererSink::finalize(GObject *object)
|
|||||||
G_OBJECT_CLASS(sink_parent_class)->finalize(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<QGstVideoRendererSink *>(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(
|
GstStateChangeReturn QGstVideoRendererSink::change_state(
|
||||||
GstElement *element, GstStateChange transition)
|
GstElement *element, GstStateChange transition)
|
||||||
{
|
{
|
||||||
Q_UNUSED(element);
|
QGstVideoRendererSink *sink = reinterpret_cast<QGstVideoRendererSink *>(element);
|
||||||
|
|
||||||
return GST_ELEMENT_CLASS(sink_parent_class)->change_state(
|
gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default
|
||||||
element, transition);
|
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)
|
GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter)
|
||||||
|
|||||||
@@ -184,6 +184,21 @@ void QVideoSurfaceGstDelegate::clearPoolBuffers()
|
|||||||
m_pool->clear();
|
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)
|
GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer)
|
||||||
{
|
{
|
||||||
if (!m_surface) {
|
if (!m_surface) {
|
||||||
@@ -244,6 +259,14 @@ void QVideoSurfaceGstDelegate::queuedStop()
|
|||||||
m_setupCondition.wakeAll();
|
m_setupCondition.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QVideoSurfaceGstDelegate::queuedFlush()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (!m_surface.isNull())
|
||||||
|
m_surface->present(QVideoFrame());
|
||||||
|
}
|
||||||
|
|
||||||
void QVideoSurfaceGstDelegate::queuedRender()
|
void QVideoSurfaceGstDelegate::queuedRender()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
@@ -316,6 +339,8 @@ QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *su
|
|||||||
|
|
||||||
sink->delegate = new QVideoSurfaceGstDelegate(surface);
|
sink->delegate = new QVideoSurfaceGstDelegate(surface);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink);
|
||||||
|
|
||||||
return sink;
|
return sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,13 +445,40 @@ void QVideoSurfaceGstSink::finalize(GObject *object)
|
|||||||
G_OBJECT_CLASS(sink_parent_class)->finalize(object);
|
G_OBJECT_CLASS(sink_parent_class)->finalize(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
GstStateChangeReturn QVideoSurfaceGstSink::change_state(
|
void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d)
|
||||||
GstElement *element, GstStateChange transition)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(element);
|
Q_UNUSED(o);
|
||||||
|
Q_UNUSED(p);
|
||||||
|
QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d);
|
||||||
|
|
||||||
return GST_ELEMENT_CLASS(sink_parent_class)->change_state(
|
gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default
|
||||||
element, transition);
|
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<QVideoSurfaceGstSink *>(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)
|
GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base)
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ public:
|
|||||||
void unlock();
|
void unlock();
|
||||||
bool proposeAllocation(GstQuery *query);
|
bool proposeAllocation(GstQuery *query);
|
||||||
|
|
||||||
|
void flush();
|
||||||
|
|
||||||
GstFlowReturn render(GstBuffer *buffer);
|
GstFlowReturn render(GstBuffer *buffer);
|
||||||
|
|
||||||
bool event(QEvent *event);
|
bool event(QEvent *event);
|
||||||
@@ -145,6 +147,8 @@ private:
|
|||||||
|
|
||||||
static void finalize(GObject *object);
|
static void finalize(GObject *object);
|
||||||
|
|
||||||
|
static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d);
|
||||||
|
|
||||||
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
|
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
|
||||||
|
|
||||||
static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter);
|
static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter);
|
||||||
|
|||||||
@@ -96,11 +96,14 @@ public:
|
|||||||
QMutex *poolMutex() { return &m_poolMutex; }
|
QMutex *poolMutex() { return &m_poolMutex; }
|
||||||
void clearPoolBuffers();
|
void clearPoolBuffers();
|
||||||
|
|
||||||
|
void flush();
|
||||||
|
|
||||||
GstFlowReturn render(GstBuffer *buffer);
|
GstFlowReturn render(GstBuffer *buffer);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void queuedStart();
|
void queuedStart();
|
||||||
void queuedStop();
|
void queuedStop();
|
||||||
|
void queuedFlush();
|
||||||
void queuedRender();
|
void queuedRender();
|
||||||
|
|
||||||
void updateSupportedFormats();
|
void updateSupportedFormats();
|
||||||
@@ -139,6 +142,8 @@ private:
|
|||||||
|
|
||||||
static void finalize(GObject *object);
|
static void finalize(GObject *object);
|
||||||
|
|
||||||
|
static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d);
|
||||||
|
|
||||||
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
|
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
|
||||||
|
|
||||||
static GstCaps *get_caps(GstBaseSink *sink);
|
static GstCaps *get_caps(GstBaseSink *sink);
|
||||||
|
|||||||
Reference in New Issue
Block a user