Changes to QMediaPlayer GStreamer backend to allow setPosition before pause

Do not display prerolled frames in stopped state.
Instead store prerolled frame and display it only after switching to
pause or playback state.
Added new unit test with a sample video file to check this functionality.

Change-Id: I3fd159a199b65ca10fdf9843af5675c5ae9dad05
Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
This commit is contained in:
Lev Zelenskiy
2012-02-06 13:52:56 +10:00
committed by Qt by Nokia
parent 7415e4c879
commit 39c96db2b3
5 changed files with 199 additions and 6 deletions

View File

@@ -66,6 +66,7 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
: m_surface(surface)
, m_pool(0)
, m_renderReturn(GST_FLOW_ERROR)
, m_lastPrerolledBuffer(0)
, m_bytesPerLine(0)
, m_startCanceled(false)
{
@@ -87,6 +88,7 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate()
{
qDeleteAll(m_pools);
setLastPrerolledBuffer(0);
}
QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
@@ -222,6 +224,23 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer)
return m_renderReturn;
}
void QVideoSurfaceGstDelegate::setLastPrerolledBuffer(GstBuffer *prerolledBuffer)
{
// discard previously stored buffer
if (m_lastPrerolledBuffer) {
gst_buffer_unref(m_lastPrerolledBuffer);
m_lastPrerolledBuffer = 0;
}
if (!prerolledBuffer)
return;
// store a reference to the buffer
Q_ASSERT(!m_lastPrerolledBuffer);
m_lastPrerolledBuffer = prerolledBuffer;
gst_buffer_ref(m_lastPrerolledBuffer);
}
void QVideoSurfaceGstDelegate::queuedStart()
{
if (!m_startCanceled) {
@@ -393,6 +412,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;
}
@@ -435,7 +456,7 @@ void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data)
base_sink_class->start = QVideoSurfaceGstSink::start;
base_sink_class->stop = QVideoSurfaceGstSink::stop;
// base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented.
// base_sink_class->event = QVideoSurfaceGstSink::event; // Not implemented.
base_sink_class->event = QVideoSurfaceGstSink::event;
base_sink_class->preroll = QVideoSurfaceGstSink::preroll;
base_sink_class->render = QVideoSurfaceGstSink::render;
@@ -664,6 +685,26 @@ QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *byte
return QVideoSurfaceFormat();
}
void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d)
{
Q_UNUSED(o);
Q_UNUSED(p);
QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d);
gboolean value = true; // "show-preroll-frame" property is true by default
g_object_get(G_OBJECT(sink), "show-preroll-frame", &value, NULL);
GstBuffer *buffer = sink->delegate->lastPrerolledBuffer();
// Render the stored prerolled buffer if requested.
// e.g. player is in stopped mode, then seek operation is requested,
// surface now stores a prerolled frame, but doesn't display it until
// "show-preroll-frame" property is set to "true"
// when switching to pause or playing state.
if (value && buffer) {
sink->delegate->render(buffer);
sink->delegate->setLastPrerolledBuffer(0);
}
}
GstFlowReturn QVideoSurfaceGstSink::buffer_alloc(
GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer)
@@ -781,8 +822,11 @@ gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base)
gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event)
{
Q_UNUSED(base);
Q_UNUSED(event);
// discard prerolled frame
if (event->type == GST_EVENT_FLUSH_START) {
VO_SINK(base);
sink->delegate->setLastPrerolledBuffer(0);
}
return TRUE;
}
@@ -790,12 +834,23 @@ gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event)
GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer)
{
VO_SINK(base);
return sink->delegate->render(buffer);
gboolean value = true; // "show-preroll-frame" property is true by default
g_object_get(G_OBJECT(base), "show-preroll-frame", &value, NULL);
if (value) {
sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer
return sink->delegate->render(buffer); // display frame
}
// otherwise keep a reference to the buffer to display it later
sink->delegate->setLastPrerolledBuffer(buffer);
return GST_FLOW_OK;
}
GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer)
{
VO_SINK(base);
sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer
return sink->delegate->render(buffer);
}

View File

@@ -97,6 +97,9 @@ public:
GstFlowReturn render(GstBuffer *buffer);
GstBuffer *lastPrerolledBuffer() const { return m_lastPrerolledBuffer; }
void setLastPrerolledBuffer(GstBuffer *lastPrerolledBuffer); // set prerolledBuffer to 0 to discard prerolled buffer
private slots:
void queuedStart();
void queuedStop();
@@ -118,6 +121,8 @@ private:
QVideoSurfaceFormat m_format;
QVideoFrame m_frame;
GstFlowReturn m_renderReturn;
// this pointer is not 0 when there is a prerolled buffer waiting to be displayed
GstBuffer *m_lastPrerolledBuffer;
int m_bytesPerLine;
bool m_started;
bool m_startCanceled;
@@ -131,6 +136,8 @@ public:
static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface);
static QVideoSurfaceFormat formatForCaps(GstCaps *caps, int *bytesPerLine = 0);
static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d);
private:
static GType get_type();
static void class_init(gpointer g_class, gpointer class_data);

View File

@@ -259,6 +259,10 @@ void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState)
bool ok = false;
// show prerolled frame if switching from stopped state
if (newState != QMediaPlayer::StoppedState && m_state == QMediaPlayer::StoppedState && m_pendingSeekPosition == -1)
m_session->showPrerollFrames(true);
//To prevent displaying the first video frame when playback is resumed
//the pipeline is paused instead of playing, seeked to requested position,
//and after seeking is finished (position updated) playback is restarted
@@ -299,6 +303,7 @@ void QGstreamerPlayerControl::stop()
if (m_state != QMediaPlayer::StoppedState) {
m_state = QMediaPlayer::StoppedState;
m_session->showPrerollFrames(false); // stop showing prerolled frames in stop state
if (m_resources->isGranted())
m_session->pause();
@@ -342,7 +347,7 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
m_state = QMediaPlayer::StoppedState;
QMediaContent oldMedia = m_currentResource;
m_pendingSeekPosition = -1;
m_session->showPrerollFrames(true);
m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called
if (!content.isNull() || stream) {
if (!m_resources->isRequested() && !m_resources->isGranted())
@@ -767,7 +772,8 @@ void QGstreamerPlayerControl::updatePosition(qint64 pos)
//seek request is complete, it's safe to resume playback
//with prerolled frame displayed
m_pendingSeekPosition = -1;
m_session->showPrerollFrames(true);
if (m_state != QMediaPlayer::StoppedState)
m_session->showPrerollFrames(true);
if (m_state == QMediaPlayer::PlayingState) {
m_session->play();
}