src/3rdparty/phonon/gstreamer/medianode.cpp
changeset 0 1918ee327afb
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5     This library is free software: you can redistribute it and/or modify
       
     6     it under the terms of the GNU Lesser General Public License as published by
       
     7     the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9     This library is distributed in the hope that it will be useful,
       
    10     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12     GNU Lesser General Public License for more details.
       
    13 
       
    14     You should have received a copy of the GNU Lesser General Public License
       
    15     along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include "common.h"
       
    19 #include "medianode.h"
       
    20 #include "mediaobject.h"
       
    21 #include "message.h"
       
    22 #include "backend.h"
       
    23 #include "gsthelper.h"
       
    24 
       
    25 QT_BEGIN_NAMESPACE
       
    26 
       
    27 namespace Phonon
       
    28 {
       
    29 namespace Gstreamer
       
    30 {
       
    31 
       
    32 MediaNode::MediaNode(Backend *backend, NodeDescription description) :
       
    33         m_isValid(false),
       
    34         m_root(0),
       
    35         m_audioTee(0),
       
    36         m_videoTee(0),
       
    37         m_fakeAudioSink(0),
       
    38         m_fakeVideoSink(0),
       
    39         m_backend(backend),
       
    40         m_description(description)
       
    41 {
       
    42     if ((description & AudioSink) && (description & VideoSink)) {
       
    43         Q_ASSERT(0); // A node cannot accept both audio and video
       
    44     }
       
    45 
       
    46     if (description & AudioSource) {
       
    47         m_audioTee = gst_element_factory_make("tee", NULL);
       
    48         gst_object_ref (GST_OBJECT (m_audioTee));
       
    49         gst_object_sink (GST_OBJECT (m_audioTee));     
       
    50 
       
    51         // Fake audio sink to swallow unconnected audio pads
       
    52         m_fakeAudioSink = gst_element_factory_make("fakesink", NULL);
       
    53         g_object_set (G_OBJECT (m_fakeAudioSink), "sync", TRUE, (const char*)NULL);
       
    54         gst_object_ref (GST_OBJECT (m_fakeAudioSink));
       
    55         gst_object_sink (GST_OBJECT (m_fakeAudioSink));
       
    56     }
       
    57 
       
    58     if (description & VideoSource) {
       
    59         m_videoTee = gst_element_factory_make("tee", NULL);
       
    60         gst_object_ref (GST_OBJECT (m_videoTee));
       
    61         gst_object_sink (GST_OBJECT (m_videoTee));     
       
    62 
       
    63         // Fake video sink to swallow unconnected video pads
       
    64         m_fakeVideoSink = gst_element_factory_make("fakesink", NULL);
       
    65         g_object_set (G_OBJECT (m_fakeVideoSink), "sync", TRUE, (const char*)NULL);
       
    66         gst_object_ref (GST_OBJECT (m_fakeVideoSink));
       
    67         gst_object_sink (GST_OBJECT (m_fakeVideoSink));
       
    68     }
       
    69 }
       
    70 
       
    71 MediaNode::~MediaNode()
       
    72 {
       
    73     if (m_videoTee) {
       
    74         gst_element_set_state(m_videoTee, GST_STATE_NULL);
       
    75         gst_object_unref(m_videoTee);
       
    76     }
       
    77 
       
    78     if (m_audioTee) {
       
    79         gst_element_set_state(m_audioTee, GST_STATE_NULL);
       
    80         gst_object_unref(m_audioTee);
       
    81     }
       
    82 
       
    83     if (m_fakeAudioSink) {
       
    84         gst_element_set_state(m_fakeAudioSink, GST_STATE_NULL);
       
    85         gst_object_unref(m_fakeAudioSink);
       
    86     }
       
    87 
       
    88     if (m_fakeVideoSink) {
       
    89         gst_element_set_state(m_fakeVideoSink, GST_STATE_NULL);
       
    90         gst_object_unref(m_fakeVideoSink);
       
    91     }
       
    92 }
       
    93 
       
    94 
       
    95 /**
       
    96  * Connects children recursively from a mediaobject root
       
    97  */
       
    98 bool MediaNode::buildGraph()
       
    99 {
       
   100     Q_ASSERT(root()); //We cannot build the graph without a root element source
       
   101 
       
   102     bool success = link();
       
   103 
       
   104     if (success) {
       
   105         // connect children recursively
       
   106         for (int i=0; i< m_audioSinkList.size(); ++i) {
       
   107             if (MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i])) {
       
   108                 node->setRoot(root());
       
   109                 if (!node->buildGraph())
       
   110                     success = false;
       
   111             }
       
   112         }
       
   113 
       
   114         for (int i=0; i < m_videoSinkList.size(); ++i) {
       
   115             if (MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i])) {
       
   116                 node->setRoot(root());
       
   117                 if (!node->buildGraph())
       
   118                     success = false;
       
   119             }
       
   120         }
       
   121     }
       
   122 
       
   123     if (!success)
       
   124         unlink();
       
   125 
       
   126     return success;
       
   127 }
       
   128 
       
   129 /**
       
   130  *  Disconnects children recursively
       
   131  */
       
   132 bool MediaNode::breakGraph()
       
   133 {
       
   134     for (int i=0; i<m_audioSinkList.size(); ++i) {
       
   135         MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i]);
       
   136         if (!node || !node->breakGraph())
       
   137             return false;
       
   138         node->setRoot(0);
       
   139     }
       
   140 
       
   141     for (int i=0; i <m_videoSinkList.size(); ++i) {
       
   142         MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i]);
       
   143         if (!node || !node->breakGraph())
       
   144             return false;
       
   145         node->setRoot(0);
       
   146     }
       
   147     unlink();
       
   148     return true;
       
   149 }
       
   150 
       
   151 bool MediaNode::connectNode(QObject *obj)
       
   152 {
       
   153     MediaNode *sink = qobject_cast<MediaNode*>(obj);
       
   154 
       
   155     bool success = false;
       
   156 
       
   157     if (sink) {
       
   158 
       
   159         if (!sink->isValid()) {
       
   160             m_backend->logMessage(QString("Trying to link to an invalid node (%0)").arg(sink->name()), Backend::Warning);
       
   161             return false;
       
   162         }
       
   163 
       
   164         if (sink->root()) {
       
   165             m_backend->logMessage("Trying to link a node that is already linked to a different mediasource ", Backend::Warning);
       
   166             return false;
       
   167         }
       
   168 
       
   169         if ((m_description & AudioSource) && (sink->m_description & AudioSink)) {
       
   170             m_audioSinkList << obj;
       
   171             MediaNodeEvent event(MediaNodeEvent::AudioSinkAdded, sink);
       
   172             root()->mediaNodeEvent(&event);
       
   173             success = true;
       
   174         }
       
   175 
       
   176         if ((m_description & VideoSource) && (sink->m_description & VideoSink)) {
       
   177             m_videoSinkList << obj;
       
   178             MediaNodeEvent event(MediaNodeEvent::VideoSinkAdded, sink);
       
   179             root()->mediaNodeEvent(&event);
       
   180             success = true;
       
   181         }
       
   182 
       
   183         // If we have a root source, and we are connected
       
   184         // try to link the gstreamer elements
       
   185         if (success && root()) {
       
   186             MediaNodeEvent mediaObjectConnected(MediaNodeEvent::MediaObjectConnected, root());
       
   187             notify(&mediaObjectConnected);
       
   188             root()->buildGraph();
       
   189         }
       
   190     }
       
   191     return success;
       
   192 }
       
   193 
       
   194 bool MediaNode::disconnectNode(QObject *obj)
       
   195 {
       
   196     MediaNode *sink = qobject_cast<MediaNode*>(obj);
       
   197     if (root()) {
       
   198         // Disconnecting elements while playing or paused seems to cause
       
   199         // potential deadlock. Hence we force the pipeline into ready state
       
   200         // before any nodes are disconnected.
       
   201         gst_element_set_state(root()->pipeline(), GST_STATE_READY);    
       
   202 
       
   203         Q_ASSERT(sink->root()); //sink has to have a root since it is onnected
       
   204 
       
   205         if (sink->description() & (AudioSink)) {
       
   206             GstPad *sinkPad = gst_element_get_pad(sink->audioElement(), "sink");
       
   207             // Release requested src pad from tee
       
   208             GstPad *requestedPad = gst_pad_get_peer(sinkPad);
       
   209             if (requestedPad) {
       
   210                 gst_element_release_request_pad(m_audioTee, requestedPad);
       
   211                 gst_object_unref(requestedPad);
       
   212             }
       
   213             if (GST_ELEMENT_PARENT(sink->audioElement()))
       
   214                 gst_bin_remove(GST_BIN(root()->audioGraph()), sink->audioElement());
       
   215             gst_object_unref(sinkPad);
       
   216         }
       
   217 
       
   218         if (sink->description() & (VideoSink)) {
       
   219             GstPad *sinkPad = gst_element_get_pad(sink->videoElement(), "sink");
       
   220             // Release requested src pad from tee
       
   221             GstPad *requestedPad = gst_pad_get_peer(sinkPad);
       
   222             if (requestedPad) {
       
   223                 gst_element_release_request_pad(m_videoTee, requestedPad);
       
   224                 gst_object_unref(requestedPad);
       
   225             }
       
   226             if (GST_ELEMENT_PARENT(sink->videoElement()))
       
   227                 gst_bin_remove(GST_BIN(root()->videoGraph()), sink->videoElement());
       
   228             gst_object_unref(sinkPad);
       
   229         }
       
   230 
       
   231         sink->breakGraph();
       
   232         sink->setRoot(0);
       
   233     }
       
   234 
       
   235     m_videoSinkList.removeAll(obj);
       
   236     m_audioSinkList.removeAll(obj);
       
   237 
       
   238     if (sink->m_description & AudioSink) {
       
   239         // Remove sink from graph
       
   240         MediaNodeEvent event(MediaNodeEvent::AudioSinkRemoved, sink);
       
   241         mediaNodeEvent(&event);
       
   242         return true;
       
   243     }
       
   244 
       
   245     if ((m_description & VideoSource) && (sink->m_description & VideoSink)) {
       
   246         // Remove sink from graph
       
   247         MediaNodeEvent event(MediaNodeEvent::VideoSinkRemoved, sink);
       
   248         mediaNodeEvent(&event);
       
   249         return true;
       
   250     }
       
   251 
       
   252     return false;
       
   253 }
       
   254 
       
   255 void MediaNode::mediaNodeEvent(const MediaNodeEvent *) {}
       
   256 
       
   257 /**
       
   258  * Propagates an event down the graph
       
   259  * sender is responsible for deleting the event
       
   260  */
       
   261 void MediaNode::notify(const MediaNodeEvent *event)
       
   262 {
       
   263     Q_ASSERT(event);
       
   264     mediaNodeEvent(event);
       
   265     for (int i=0; i<m_audioSinkList.size(); ++i) {
       
   266         MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i]);
       
   267         node->notify(event);
       
   268     }
       
   269 
       
   270     for (int i=0; i<m_videoSinkList.size(); ++i) {
       
   271         MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i]);
       
   272         node->notify(event);
       
   273     }
       
   274 }
       
   275 
       
   276 /*
       
   277  * Requests a new tee pad and connects a node to it
       
   278  */
       
   279 bool MediaNode::addOutput(MediaNode *output, GstElement *tee)
       
   280 {
       
   281     Q_ASSERT(root());
       
   282 
       
   283     bool success = true;
       
   284 
       
   285     GstElement *sinkElement = 0;
       
   286     if (output->description() & AudioSink)
       
   287         sinkElement = output->audioElement();
       
   288     else if (output->description() & VideoSink)
       
   289         sinkElement = output->videoElement();
       
   290 
       
   291     Q_ASSERT(sinkElement);
       
   292 
       
   293     if (!sinkElement)
       
   294         return false;
       
   295 
       
   296     GstState state = GST_STATE (root()->pipeline());
       
   297     GstPad *srcPad = gst_element_get_request_pad (tee, "src%d");
       
   298     GstPad *sinkPad = gst_element_get_pad (sinkElement, "sink");
       
   299 
       
   300     if (!sinkPad) {
       
   301         success = false;
       
   302     } else if (gst_pad_is_linked(sinkPad)) {
       
   303         gst_object_unref (GST_OBJECT (sinkPad));
       
   304         gst_object_unref (GST_OBJECT (srcPad));
       
   305         return true;
       
   306     }
       
   307 
       
   308     if (success) {
       
   309         if (output->description() & AudioSink)
       
   310             gst_bin_add(GST_BIN(root()->audioGraph()), sinkElement);
       
   311         else if (output->description() & VideoSink)
       
   312             gst_bin_add(GST_BIN(root()->videoGraph()), sinkElement);
       
   313     }
       
   314 
       
   315     if (success) {
       
   316         gst_pad_link(srcPad, sinkPad);
       
   317         gst_element_set_state(sinkElement, state);
       
   318     } else {
       
   319         gst_element_release_request_pad(tee, srcPad);
       
   320     }
       
   321 
       
   322     gst_object_unref (GST_OBJECT (srcPad));
       
   323     gst_object_unref (GST_OBJECT (sinkPad));
       
   324 
       
   325     return success;
       
   326 }
       
   327 
       
   328 // Used to seal up unconnected source nodes by connecting unconnected src pads to fake sinks
       
   329 bool MediaNode::connectToFakeSink(GstElement *tee, GstElement *sink, GstElement *bin)
       
   330 {
       
   331     bool success = true;
       
   332     GstPad *sinkPad = gst_element_get_pad (sink, "sink");
       
   333 
       
   334     if (GST_PAD_IS_LINKED (sinkPad)) {
       
   335         //This fakesink is already connected
       
   336         gst_object_unref (sinkPad);
       
   337         return true;
       
   338     }
       
   339 
       
   340     GstPad *srcPad = gst_element_get_request_pad (tee, "src%d");
       
   341     gst_bin_add(GST_BIN(bin), sink);
       
   342     if (success)
       
   343         success = (gst_pad_link (srcPad, sinkPad) == GST_PAD_LINK_OK);
       
   344     if (success)
       
   345         success = (gst_element_set_state(sink, GST_STATE(bin)) != GST_STATE_CHANGE_FAILURE);
       
   346     gst_object_unref (srcPad);
       
   347     gst_object_unref (sinkPad);
       
   348     return success;
       
   349 }
       
   350 
       
   351 // Used to seal up unconnected source nodes by connecting unconnected src pads to fake sinks
       
   352 bool MediaNode::releaseFakeSinkIfConnected(GstElement *tee, GstElement *fakesink, GstElement *bin)
       
   353 {
       
   354     if (GST_ELEMENT_PARENT(fakesink) == GST_ELEMENT(bin)) {
       
   355         GstPad *sinkPad = gst_element_get_pad(fakesink, "sink");
       
   356 
       
   357         // Release requested src pad from tee
       
   358         GstPad *requestedPad = gst_pad_get_peer(sinkPad);
       
   359         if (requestedPad) {
       
   360             gst_element_release_request_pad(tee, requestedPad);
       
   361             gst_object_unref(requestedPad);
       
   362         }
       
   363         gst_object_unref(sinkPad);
       
   364 
       
   365         gst_element_set_state(fakesink, GST_STATE_NULL);
       
   366         gst_bin_remove(GST_BIN(bin), fakesink);
       
   367         Q_ASSERT(!GST_ELEMENT_PARENT(fakesink));
       
   368     }
       
   369     return true;
       
   370 }
       
   371 
       
   372 bool MediaNode::linkMediaNodeList(QList<QObject *> &list, GstElement *bin, GstElement *tee, GstElement *fakesink, GstElement *src)
       
   373 {
       
   374     if (!GST_ELEMENT_PARENT(tee)) {
       
   375         gst_bin_add(GST_BIN(bin), tee);
       
   376         if (!gst_element_link_pads(src, "src", tee, "sink"))
       
   377             return false;
       
   378         gst_element_set_state(tee, GST_STATE(bin));
       
   379     }
       
   380     if (list.isEmpty()) {
       
   381         //connect node to a fake sink to avoid clogging the pipeline
       
   382         if (!connectToFakeSink(tee, fakesink, bin))
       
   383             return false;
       
   384     } else {
       
   385         // Remove fake sink if previously connected
       
   386         if (!releaseFakeSinkIfConnected(tee, fakesink, bin))
       
   387             return false;
       
   388 
       
   389         for (int i = 0 ; i < list.size() ; ++i) {
       
   390             QObject *sink = list[i];
       
   391             if (MediaNode *output = qobject_cast<MediaNode*>(sink)) {
       
   392                 if (!addOutput(output, tee))
       
   393                     return false;
       
   394             }
       
   395         }
       
   396     }
       
   397     return true;
       
   398 }
       
   399 
       
   400 bool MediaNode::link()
       
   401 {
       
   402     // Rewire everything
       
   403     if ((description() & AudioSource)) {
       
   404         if (!linkMediaNodeList(m_audioSinkList, root()->audioGraph(), m_audioTee, m_fakeAudioSink, audioElement()))
       
   405             return false;
       
   406     }
       
   407 
       
   408     if ((description() & VideoSource)) {
       
   409         if (!linkMediaNodeList(m_videoSinkList, root()->videoGraph(), m_videoTee, m_fakeVideoSink, videoElement()))
       
   410             return false;
       
   411     }
       
   412     return true;
       
   413 }
       
   414 
       
   415 bool MediaNode::unlink()
       
   416 {
       
   417     Q_ASSERT(root());
       
   418     if (description() & AudioSource) {
       
   419         if (GST_ELEMENT_PARENT(m_audioTee) == GST_ELEMENT(root()->audioGraph())) {
       
   420            gst_element_set_state(m_audioTee, GST_STATE_NULL);    
       
   421            gst_bin_remove(GST_BIN(root()->audioGraph()), m_audioTee);
       
   422        }
       
   423         for (int i=0; i<m_audioSinkList.size(); ++i) {
       
   424             QObject *audioSink = m_audioSinkList[i];
       
   425             if (MediaNode *output = qobject_cast<MediaNode*>(audioSink)) {
       
   426                 GstElement *element = output->audioElement();
       
   427                 if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->audioGraph())) {
       
   428                     gst_element_set_state(element, GST_STATE_NULL);    
       
   429                     gst_bin_remove(GST_BIN(root()->audioGraph()), element);
       
   430                 }
       
   431             }
       
   432         }
       
   433     } else if (description() & VideoSource) {
       
   434         if (GST_ELEMENT_PARENT(m_videoTee) == GST_ELEMENT(root()->videoGraph())) {
       
   435            gst_element_set_state(m_videoTee, GST_STATE_NULL);    
       
   436            gst_bin_remove(GST_BIN(root()->videoGraph()), m_videoTee);
       
   437         }
       
   438         for (int i=0; i <m_videoSinkList.size(); ++i) {
       
   439             QObject *videoSink = m_videoSinkList[i];
       
   440             if (MediaNode *vw = qobject_cast<MediaNode*>(videoSink)) {
       
   441                 GstElement *element = vw->videoElement();
       
   442                 if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->videoGraph())) {
       
   443                     gst_element_set_state(element, GST_STATE_NULL);    
       
   444                     gst_bin_remove(GST_BIN(root()->videoGraph()), element);
       
   445                 }
       
   446             }
       
   447         }
       
   448     }
       
   449     return true;
       
   450 }
       
   451 
       
   452 
       
   453 } // ns Gstreamer
       
   454 } // ns Phonon
       
   455 
       
   456 QT_END_NAMESPACE