source: trunk/src/3rdparty/phonon/gstreamer/medianode.cpp@ 134

Last change on this file since 134 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.5 KB
Line 
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
25QT_BEGIN_NAMESPACE
26
27namespace Phonon
28{
29namespace Gstreamer
30{
31
32MediaNode::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
71MediaNode::~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 */
98bool 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 */
132bool 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
151bool 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
194bool 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
255void MediaNode::mediaNodeEvent(const MediaNodeEvent *) {}
256
257/**
258 * Propagates an event down the graph
259 * sender is responsible for deleting the event
260 */
261void 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 */
279bool 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
329bool 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
352bool 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
372bool 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
400bool 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
415bool 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
456QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.