qtmobility/plugins/multimedia/gstreamer/mediaplayer/qgstreamerplayersession.cpp
changeset 14 6fbed849b4f4
parent 11 06b8e2af4411
child 15 1f895d8a5b2b
--- a/qtmobility/plugins/multimedia/gstreamer/mediaplayer/qgstreamerplayersession.cpp	Fri Jun 11 14:26:25 2010 +0300
+++ b/qtmobility/plugins/multimedia/gstreamer/mediaplayer/qgstreamerplayersession.cpp	Wed Jun 23 19:08:38 2010 +0300
@@ -58,8 +58,11 @@
      m_state(QMediaPlayer::StoppedState),
      m_busHelper(0),
      m_playbin(0),
-     m_nullVideoOutput(0),
+     m_videoSink(0),
+     m_pendingVideoSink(0),
+     m_nullVideoSink(0),
      m_bus(0),
+     m_videoOutput(0),
      m_renderer(0),
      m_volume(100),
      m_playbackRate(1.0),
@@ -82,6 +85,24 @@
     m_playbin = gst_element_factory_make("playbin", NULL);
 #endif
 
+    m_videoOutputBin = gst_bin_new("video-output-bin");
+    gst_object_ref(GST_OBJECT(m_videoOutputBin));
+
+    m_videoIdentity = gst_element_factory_make("identity", "identity-vo");
+    m_colorSpace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-vo");
+    m_videoScale = gst_element_factory_make("videoscale","videoscale-vo");
+    m_nullVideoSink = gst_element_factory_make("fakesink", NULL);
+    gst_object_ref(GST_OBJECT(m_nullVideoSink));
+    gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_colorSpace, m_videoScale, m_nullVideoSink, NULL);
+    gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoScale, m_nullVideoSink, NULL);
+
+    m_videoSink = m_nullVideoSink;
+
+    // add ghostpads
+    GstPad *pad = gst_element_get_static_pad(m_videoIdentity,"sink");
+    gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("videosink", pad));
+    gst_object_unref(GST_OBJECT(pad));
+
     if (m_playbin != 0) {
         // Sort out messages
         m_bus = gst_element_get_bus(m_playbin);
@@ -89,9 +110,7 @@
         connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage)));
         m_busHelper->installSyncEventFilter(this);
 
-        m_nullVideoOutput = gst_element_factory_make("fakesink", NULL);
-        gst_object_ref(GST_OBJECT(m_nullVideoOutput));
-        g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL);
+        g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, NULL);
 
         // Initial volume
         double volume = 1.0;
@@ -108,7 +127,8 @@
         delete m_busHelper;
         gst_object_unref(GST_OBJECT(m_bus));
         gst_object_unref(GST_OBJECT(m_playbin));
-        gst_object_unref(GST_OBJECT(m_nullVideoOutput));
+        gst_object_unref(GST_OBJECT(m_nullVideoSink));
+        gst_object_unref(GST_OBJECT(m_videoOutputBin));
     }
 }
 
@@ -158,13 +178,16 @@
         m_playbackRate = rate;
         if (m_playbin) {
             gst_element_seek(m_playbin, rate, GST_FORMAT_TIME,
-                             GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT),
+                             GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH),
                              GST_SEEK_TYPE_NONE,0,
                              GST_SEEK_TYPE_NONE,0 );
         }
     }
 }
 
+//#define DEBUG_VO_BIN_DUMP
+//#define DEBUG_PLAYBIN_STATES
+
 
 int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const
 {
@@ -241,13 +264,161 @@
     return m_audioAvailable;
 }
 
-void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput)
+static void block_pad_cb(GstPad *pad, gboolean blocked, gpointer user_data)
+{
+    Q_UNUSED(pad);
+    //qDebug() << "block_pad_cb" << blocked;
+
+    if (blocked && user_data) {
+        QGstreamerPlayerSession *session = reinterpret_cast<QGstreamerPlayerSession*>(user_data);
+        QMetaObject::invokeMethod(session, "finishVideoOutputChange", Qt::QueuedConnection);
+    }
+}
+
+#ifdef DEBUG_VO_BIN_DUMP
+    static int dumpNum = 0;
+#endif
+
+void QGstreamerPlayerSession::updateVideoRenderer()
 {
-    m_renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput);
-    if (m_renderer)
-        g_object_set(G_OBJECT(m_playbin), "video-sink", m_renderer->videoSink(), NULL);
-    else
-        g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL);
+    if (m_videoOutput)
+        setVideoRenderer(m_videoOutput);
+}
+
+void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput)
+{    
+    if (m_videoOutput != videoOutput) {
+        if (m_videoOutput)
+            disconnect(m_videoOutput, SIGNAL(sinkChanged()),
+                       this, SLOT(updateVideoRenderer()));
+        if (videoOutput)
+            connect(videoOutput, SIGNAL(sinkChanged()),
+                    this, SLOT(updateVideoRenderer()));
+
+        m_videoOutput = videoOutput;
+    }
+
+    QGstreamerVideoRendererInterface* renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput);
+
+    if (m_renderer == renderer) {
+        //return only if video sink is still the same
+        if (!m_renderer || m_renderer->videoSink() == m_videoSink)
+            return;
+    }
+
+    m_renderer = renderer;
+
+#ifdef DEBUG_VO_BIN_DUMP
+    dumpNum++;
+
+    _gst_debug_bin_to_dot_file(GST_BIN(m_videoOutputBin),
+                                  GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/),
+                                  QString("video_output_change_%1_set").arg(dumpNum).toAscii().constData());
+#endif
+
+    GstElement *videoSink = m_renderer ? m_renderer->videoSink() : m_nullVideoSink;
+
+    if (m_state == QMediaPlayer::StoppedState) {
+        m_pendingVideoSink = 0;
+        gst_element_unlink(m_videoScale, m_videoSink);
+
+        gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink);
+
+        m_videoSink = videoSink;
+
+        gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink);
+        gst_element_link(m_videoScale, m_videoSink);
+
+    } else {
+        if (m_pendingVideoSink) {
+            m_pendingVideoSink = videoSink;
+            return;
+        }
+
+        m_pendingVideoSink = videoSink;
+
+        //block pads, async to avoid locking in paused state
+        GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src");
+        gst_pad_set_blocked_async(srcPad, true, &block_pad_cb, this);
+        gst_object_unref(GST_OBJECT(srcPad));
+    }
+}
+
+void QGstreamerPlayerSession::finishVideoOutputChange()
+{
+    if (!m_pendingVideoSink)
+        return;
+
+    GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src");
+
+    if (!gst_pad_is_blocked(srcPad)) {
+        //pad is not blocked, it's possible to swap outputs only in the null state
+        GstState identityElementState = GST_STATE_NULL;
+        gst_element_get_state(m_videoIdentity, &identityElementState, NULL, GST_CLOCK_TIME_NONE);
+        if (identityElementState != GST_STATE_NULL) {
+            gst_object_unref(GST_OBJECT(srcPad));
+            return; //can't change vo yet, received async call from the previous change
+        }
+
+    }
+
+    if (m_pendingVideoSink == m_videoSink) {
+        //video output was change back to the current one,
+        //no need to torment the pipeline, just unblock the pad
+        if (gst_pad_is_blocked(srcPad))
+            gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0);
+
+        m_pendingVideoSink = 0;
+        gst_object_unref(GST_OBJECT(srcPad));
+        return;
+    }  
+
+    gst_element_set_state(m_colorSpace, GST_STATE_NULL);
+    gst_element_set_state(m_videoScale, GST_STATE_NULL);
+    gst_element_set_state(m_videoSink, GST_STATE_NULL);
+
+    gst_element_unlink(m_videoScale, m_videoSink);
+
+    gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink);
+
+    m_videoSink = m_pendingVideoSink;
+    m_pendingVideoSink = 0;
+
+    gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink);
+    if (!gst_element_link(m_videoScale, m_videoSink))
+        qWarning() << "Linking video output element failed";
+
+    GstState state;
+
+    switch (m_state) {
+    case QMediaPlayer::StoppedState:
+        state = GST_STATE_NULL;
+        break;
+    case QMediaPlayer::PausedState:
+        state = GST_STATE_PAUSED;
+        break;
+    case QMediaPlayer::PlayingState:
+        state = GST_STATE_PLAYING;
+        break;
+    }
+
+    gst_element_set_state(m_colorSpace, state);
+    gst_element_set_state(m_videoScale, state);
+    gst_element_set_state(m_videoSink, state);
+
+    //don't have to wait here, it will unblock eventually
+    if (gst_pad_is_blocked(srcPad))
+        gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0);
+    gst_object_unref(GST_OBJECT(srcPad));
+
+#ifdef DEBUG_VO_BIN_DUMP
+    dumpNum++;
+
+    _gst_debug_bin_to_dot_file(GST_BIN(m_videoOutputBin),
+                                  GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL */ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES),
+                                  QString("video_output_change_%1_finish").arg(dumpNum).toAscii().constData());
+#endif
+
 }
 
 bool QGstreamerPlayerSession::isVideoAvailable() const
@@ -297,17 +468,30 @@
     if (m_playbin) {
         gst_element_set_state(m_playbin, GST_STATE_NULL);
 
+        QMediaPlayer::State oldState = QMediaPlayer::StoppedState;
+        m_state = QMediaPlayer::StoppedState;
+
+        finishVideoOutputChange();
+
         //we have to do it here, since gstreamer will not emit bus messages any more
-        if (m_state != QMediaPlayer::StoppedState)
-            emit stateChanged(m_state = QMediaPlayer::StoppedState);
+        if (oldState != m_state)
+            emit stateChanged(m_state);
     }
 }
 
 bool QGstreamerPlayerSession::seek(qint64 ms)
 {
-    if (m_playbin && m_state != QMediaPlayer::StoppedState) {
-        gint64  position = (gint64)ms * 1000000;
-        return gst_element_seek_simple(m_playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position);
+    //seek locks when the video output sink is changing and pad is blocked
+    if (m_playbin && !m_pendingVideoSink && m_state != QMediaPlayer::StoppedState) {
+        gint64  position = qMax(ms,qint64(0)) * 1000000;
+        return gst_element_seek(m_playbin,
+                                m_playbackRate,
+                                GST_FORMAT_TIME,
+                                GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH),
+                                GST_SEEK_TYPE_SET,
+                                position,
+                                GST_SEEK_TYPE_NONE,
+                                0);
     }
 
     return false;
@@ -471,13 +655,15 @@
 
                     gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
 
-                    /*QStringList states;
+#ifdef DEBUG_PLAYBIN_STATES
+                    QStringList states;
                     states << "GST_STATE_VOID_PENDING" <<  "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING";
 
                     qDebug() << QString("state changed: old: %1  new: %2  pending: %3") \
                             .arg(states[oldState]) \
                             .arg(states[newState]) \
-                            .arg(states[pending]);*/
+                            .arg(states[pending]);
+#endif
 
                     switch (newState) {
                     case GST_STATE_VOID_PENDING:
@@ -512,8 +698,11 @@
 
                             setSeekable(true);
 
-                            if (!qFuzzyCompare(m_playbackRate, qreal(1.0)))
-                                setPlaybackRate(m_playbackRate);
+                            if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) {
+                                qreal rate = m_playbackRate;
+                                m_playbackRate = 1.0;
+                                setPlaybackRate(rate);
+                            }
 
                             if (m_renderer)
                                 m_renderer->precessNewStream();
@@ -658,7 +847,7 @@
 
     for (int i=0; i<m_streamTypes.count(); i++) {
         QMediaStreamsControl::StreamType streamType = m_streamTypes[i];
-        QMap<QtMediaServices::MetaData, QVariant> streamProperties;
+        QMap<QtMultimedia::MetaData, QVariant> streamProperties;
 
         int streamIndex = i - m_playbin2StreamOffset[streamType];
 
@@ -680,7 +869,7 @@
         if (tags && gst_is_tag_list(tags)) {
             gchar *languageCode = 0;
             if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode))
-                streamProperties[QtMediaServices::Language] = QString::fromUtf8(languageCode);
+                streamProperties[QtMultimedia::Language] = QString::fromUtf8(languageCode);
 
             //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode);
             g_free (languageCode);
@@ -733,8 +922,8 @@
             break;
         }
 
-        QMap<QtMediaServices::MetaData, QVariant> streamProperties;
-        streamProperties[QtMediaServices::Language] = QString::fromUtf8(languageCode);
+        QMap<QtMultimedia::MetaData, QVariant> streamProperties;
+        streamProperties[QtMultimedia::Language] = QString::fromUtf8(languageCode);
 
         m_streamProperties.append(streamProperties);
         m_streamTypes.append(streamType);