| 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 connected
 | 
|---|
| 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
 | 
|---|