|
1 /* GStreamer |
|
2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> |
|
3 * |
|
4 * This library is free software; you can redistribute it and/or |
|
5 * modify it under the terms of the GNU Library General Public |
|
6 * License as published by the Free Software Foundation; either |
|
7 * version 2 of the License, or (at your option) any later version. |
|
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 GNU |
|
12 * Library General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Library General Public |
|
15 * License along with this library; if not, write to the |
|
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
17 * Boston, MA 02111-1307, USA. |
|
18 */ |
|
19 |
|
20 #ifdef HAVE_CONFIG_H |
|
21 #include "config.h" |
|
22 #endif |
|
23 |
|
24 #include <string.h> |
|
25 #include <gst/gst.h> |
|
26 |
|
27 #include <gst/gst-i18n-plugin.h> |
|
28 #include <gst/pbutils/pbutils.h> |
|
29 |
|
30 #include "gstplaysink.h" |
|
31 |
|
32 #ifdef __SYMBIAN32__ |
|
33 #include <glib_global.h> |
|
34 #endif |
|
35 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug); |
|
36 #define GST_CAT_DEFAULT gst_play_sink_debug |
|
37 |
|
38 #define VOLUME_MAX_DOUBLE 10.0 |
|
39 |
|
40 /* holds the common data fields for the audio and video pipelines. We keep them |
|
41 * in a structure to more easily have all the info available. */ |
|
42 typedef struct |
|
43 { |
|
44 GstPlaySink *playsink; |
|
45 GstPad *sinkpad; |
|
46 GstElement *bin; |
|
47 gboolean added; |
|
48 gboolean activated; |
|
49 } GstPlayChain; |
|
50 |
|
51 typedef struct |
|
52 { |
|
53 GstPlayChain chain; |
|
54 GstElement *queue; |
|
55 GstElement *conv; |
|
56 GstElement *resample; |
|
57 GstElement *volume; /* element with the volume property */ |
|
58 GstElement *mute; /* element with the mute property */ |
|
59 GstElement *sink; |
|
60 } GstPlayAudioChain; |
|
61 |
|
62 typedef struct |
|
63 { |
|
64 GstPlayChain chain; |
|
65 GstElement *queue; |
|
66 GstElement *conv; |
|
67 GstElement *scale; |
|
68 GstElement *sink; |
|
69 gboolean async; |
|
70 } GstPlayVideoChain; |
|
71 |
|
72 typedef struct |
|
73 { |
|
74 GstPlayChain chain; |
|
75 GstElement *queue; |
|
76 GstElement *conv; |
|
77 GstElement *resample; |
|
78 GstPad *blockpad; /* srcpad of resample, used for switching the vis */ |
|
79 GstPad *vissinkpad; /* visualisation sinkpad, */ |
|
80 GstElement *vis; |
|
81 GstPad *vissrcpad; /* visualisation srcpad, */ |
|
82 GstPad *srcpad; /* outgoing srcpad, used to connect to the next |
|
83 * chain */ |
|
84 } GstPlayVisChain; |
|
85 |
|
86 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock) |
|
87 #define GST_PLAY_SINK_LOCK(playsink) g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)) |
|
88 #define GST_PLAY_SINK_UNLOCK(playsink) g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)) |
|
89 |
|
90 struct _GstPlaySink |
|
91 { |
|
92 GstBin bin; |
|
93 |
|
94 GMutex *lock; |
|
95 |
|
96 GstPlayFlags flags; |
|
97 |
|
98 GstPlayChain *audiochain; |
|
99 GstPlayChain *videochain; |
|
100 GstPlayChain *vischain; |
|
101 |
|
102 GstPad *audio_pad; |
|
103 gboolean audio_pad_raw; |
|
104 GstElement *audio_tee; |
|
105 GstPad *audio_tee_sink; |
|
106 GstPad *audio_tee_asrc; |
|
107 GstPad *audio_tee_vissrc; |
|
108 |
|
109 GstPad *video_pad; |
|
110 gboolean video_pad_raw; |
|
111 |
|
112 GstPad *text_pad; |
|
113 |
|
114 /* properties */ |
|
115 GstElement *audio_sink; |
|
116 GstElement *video_sink; |
|
117 GstElement *visualisation; |
|
118 gfloat volume; |
|
119 gboolean mute; |
|
120 gchar *font_desc; /* font description */ |
|
121 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */ |
|
122 |
|
123 /* internal elements */ |
|
124 GstElement *textoverlay_element; |
|
125 }; |
|
126 |
|
127 struct _GstPlaySinkClass |
|
128 { |
|
129 GstBinClass parent_class; |
|
130 }; |
|
131 |
|
132 |
|
133 /* props */ |
|
134 enum |
|
135 { |
|
136 PROP_0, |
|
137 PROP_AUDIO_SINK, |
|
138 PROP_VIDEO_SINK, |
|
139 PROP_VIS_PLUGIN, |
|
140 PROP_VOLUME, |
|
141 PROP_FRAME, |
|
142 PROP_FONT_DESC, |
|
143 PROP_LAST |
|
144 }; |
|
145 |
|
146 /* signals */ |
|
147 enum |
|
148 { |
|
149 LAST_SIGNAL |
|
150 }; |
|
151 |
|
152 static void gst_play_sink_class_init (GstPlaySinkClass * klass); |
|
153 static void gst_play_sink_init (GstPlaySink * playsink); |
|
154 static void gst_play_sink_dispose (GObject * object); |
|
155 static void gst_play_sink_finalize (GObject * object); |
|
156 |
|
157 static void gst_play_sink_set_property (GObject * object, guint prop_id, |
|
158 const GValue * value, GParamSpec * spec); |
|
159 static void gst_play_sink_get_property (GObject * object, guint prop_id, |
|
160 GValue * value, GParamSpec * spec); |
|
161 |
|
162 static gboolean gst_play_sink_send_event (GstElement * element, |
|
163 GstEvent * event); |
|
164 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element, |
|
165 GstStateChange transition); |
|
166 |
|
167 static GstElementClass *parent_class; |
|
168 |
|
169 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */ |
|
170 |
|
171 static const GstElementDetails gst_play_sink_details = |
|
172 GST_ELEMENT_DETAILS ("Player Sink", |
|
173 "Generic/Bin/Player", |
|
174 "Autoplug and play media from an uri", |
|
175 "Wim Taymans <wim.taymans@gmail.com>"); |
|
176 #ifdef __SYMBIAN32__ |
|
177 EXPORT_C |
|
178 #endif |
|
179 |
|
180 |
|
181 GType |
|
182 gst_play_sink_get_type (void) |
|
183 { |
|
184 static GType gst_play_sink_type = 0; |
|
185 |
|
186 if (!gst_play_sink_type) { |
|
187 static const GTypeInfo gst_play_sink_info = { |
|
188 sizeof (GstPlaySinkClass), |
|
189 NULL, |
|
190 NULL, |
|
191 (GClassInitFunc) gst_play_sink_class_init, |
|
192 NULL, |
|
193 NULL, |
|
194 sizeof (GstPlaySink), |
|
195 0, |
|
196 (GInstanceInitFunc) gst_play_sink_init, |
|
197 NULL |
|
198 }; |
|
199 |
|
200 gst_play_sink_type = g_type_register_static (GST_TYPE_BIN, |
|
201 "GstPlaySink", &gst_play_sink_info, 0); |
|
202 } |
|
203 |
|
204 return gst_play_sink_type; |
|
205 } |
|
206 |
|
207 static void |
|
208 gst_play_sink_class_init (GstPlaySinkClass * klass) |
|
209 { |
|
210 GObjectClass *gobject_klass; |
|
211 GstElementClass *gstelement_klass; |
|
212 GstBinClass *gstbin_klass; |
|
213 |
|
214 gobject_klass = (GObjectClass *) klass; |
|
215 gstelement_klass = (GstElementClass *) klass; |
|
216 gstbin_klass = (GstBinClass *) klass; |
|
217 |
|
218 parent_class = g_type_class_peek_parent (klass); |
|
219 |
|
220 gobject_klass->set_property = gst_play_sink_set_property; |
|
221 gobject_klass->get_property = gst_play_sink_get_property; |
|
222 |
|
223 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose); |
|
224 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize); |
|
225 |
|
226 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK, |
|
227 g_param_spec_object ("video-sink", "Video Sink", |
|
228 "the video output element to use (NULL = default sink)", |
|
229 GST_TYPE_ELEMENT, G_PARAM_READWRITE)); |
|
230 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK, |
|
231 g_param_spec_object ("audio-sink", "Audio Sink", |
|
232 "the audio output element to use (NULL = default sink)", |
|
233 GST_TYPE_ELEMENT, G_PARAM_READWRITE)); |
|
234 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN, |
|
235 g_param_spec_object ("vis-plugin", "Vis plugin", |
|
236 "the visualization element to use (NULL = none)", |
|
237 GST_TYPE_ELEMENT, G_PARAM_READWRITE)); |
|
238 g_object_class_install_property (gobject_klass, PROP_VOLUME, |
|
239 g_param_spec_double ("volume", "volume", "volume", |
|
240 0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE)); |
|
241 g_object_class_install_property (gobject_klass, PROP_FRAME, |
|
242 gst_param_spec_mini_object ("frame", "Frame", |
|
243 "The last video frame (NULL = no video available)", |
|
244 GST_TYPE_BUFFER, G_PARAM_READABLE)); |
|
245 g_object_class_install_property (gobject_klass, PROP_FONT_DESC, |
|
246 g_param_spec_string ("subtitle-font-desc", |
|
247 "Subtitle font description", |
|
248 "Pango font description of font " |
|
249 "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE)); |
|
250 |
|
251 gst_element_class_set_details (gstelement_klass, &gst_play_sink_details); |
|
252 |
|
253 gstelement_klass->change_state = |
|
254 GST_DEBUG_FUNCPTR (gst_play_sink_change_state); |
|
255 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event); |
|
256 |
|
257 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin"); |
|
258 } |
|
259 |
|
260 static void |
|
261 gst_play_sink_init (GstPlaySink * playsink) |
|
262 { |
|
263 /* init groups */ |
|
264 playsink->video_sink = NULL; |
|
265 playsink->audio_sink = NULL; |
|
266 playsink->visualisation = NULL; |
|
267 playsink->textoverlay_element = NULL; |
|
268 playsink->volume = 1.0; |
|
269 playsink->font_desc = NULL; |
|
270 playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME; |
|
271 |
|
272 playsink->lock = g_mutex_new (); |
|
273 } |
|
274 |
|
275 static void |
|
276 gst_play_sink_dispose (GObject * object) |
|
277 { |
|
278 GstPlaySink *playsink; |
|
279 |
|
280 playsink = GST_PLAY_SINK (object); |
|
281 |
|
282 if (playsink->audio_sink != NULL) { |
|
283 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL); |
|
284 gst_object_unref (playsink->audio_sink); |
|
285 playsink->audio_sink = NULL; |
|
286 } |
|
287 if (playsink->video_sink != NULL) { |
|
288 gst_element_set_state (playsink->video_sink, GST_STATE_NULL); |
|
289 gst_object_unref (playsink->video_sink); |
|
290 playsink->video_sink = NULL; |
|
291 } |
|
292 if (playsink->visualisation != NULL) { |
|
293 gst_element_set_state (playsink->visualisation, GST_STATE_NULL); |
|
294 gst_object_unref (playsink->visualisation); |
|
295 playsink->visualisation = NULL; |
|
296 } |
|
297 if (playsink->textoverlay_element != NULL) { |
|
298 gst_object_unref (playsink->textoverlay_element); |
|
299 playsink->textoverlay_element = NULL; |
|
300 } |
|
301 g_free (playsink->font_desc); |
|
302 playsink->font_desc = NULL; |
|
303 |
|
304 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
305 } |
|
306 |
|
307 static void |
|
308 gst_play_sink_finalize (GObject * object) |
|
309 { |
|
310 GstPlaySink *playsink; |
|
311 |
|
312 playsink = GST_PLAY_SINK (object); |
|
313 |
|
314 g_mutex_free (playsink->lock); |
|
315 |
|
316 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
317 } |
|
318 #ifdef __SYMBIAN32__ |
|
319 EXPORT_C |
|
320 #endif |
|
321 |
|
322 |
|
323 void |
|
324 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink) |
|
325 { |
|
326 GST_OBJECT_LOCK (playsink); |
|
327 if (playsink->video_sink) |
|
328 gst_object_unref (playsink->video_sink); |
|
329 |
|
330 if (sink) { |
|
331 gst_object_ref (sink); |
|
332 gst_object_sink (sink); |
|
333 } |
|
334 playsink->video_sink = sink; |
|
335 GST_OBJECT_UNLOCK (playsink); |
|
336 } |
|
337 #ifdef __SYMBIAN32__ |
|
338 EXPORT_C |
|
339 #endif |
|
340 |
|
341 |
|
342 void |
|
343 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink) |
|
344 { |
|
345 GST_OBJECT_LOCK (playsink); |
|
346 if (playsink->audio_sink) |
|
347 gst_object_unref (playsink->audio_sink); |
|
348 |
|
349 if (sink) { |
|
350 gst_object_ref (sink); |
|
351 gst_object_sink (sink); |
|
352 } |
|
353 playsink->audio_sink = sink; |
|
354 GST_OBJECT_UNLOCK (playsink); |
|
355 } |
|
356 |
|
357 static void |
|
358 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked, |
|
359 gpointer user_data) |
|
360 { |
|
361 GstPlaySink *playsink; |
|
362 |
|
363 playsink = GST_PLAY_SINK (user_data); |
|
364 /* nothing to do here, we need a dummy callback here to make the async call |
|
365 * non-blocking. */ |
|
366 GST_DEBUG_OBJECT (playsink, "vis pad unblocked"); |
|
367 } |
|
368 |
|
369 static void |
|
370 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked, |
|
371 gpointer user_data) |
|
372 { |
|
373 GstPlaySink *playsink; |
|
374 GstPlayVisChain *chain; |
|
375 |
|
376 playsink = GST_PLAY_SINK (user_data); |
|
377 |
|
378 GST_PLAY_SINK_LOCK (playsink); |
|
379 GST_DEBUG_OBJECT (playsink, "vis pad blocked"); |
|
380 /* now try to change the plugin in the running vis chain */ |
|
381 if (!(chain = (GstPlayVisChain *) playsink->vischain)) |
|
382 goto done; |
|
383 |
|
384 /* unlink the old plugin and unghost the pad */ |
|
385 gst_pad_unlink (chain->blockpad, chain->vissinkpad); |
|
386 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL); |
|
387 |
|
388 /* set the old plugin to NULL and remove */ |
|
389 gst_element_set_state (chain->vis, GST_STATE_NULL); |
|
390 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis); |
|
391 |
|
392 /* add new plugin and set state to playing */ |
|
393 chain->vis = gst_object_ref (playsink->visualisation); |
|
394 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis); |
|
395 gst_element_set_state (chain->vis, GST_STATE_PLAYING); |
|
396 |
|
397 /* get pads */ |
|
398 chain->vissinkpad = gst_element_get_pad (chain->vis, "sink"); |
|
399 chain->vissrcpad = gst_element_get_pad (chain->vis, "src"); |
|
400 |
|
401 /* link pads */ |
|
402 gst_pad_link (chain->blockpad, chain->vissinkpad); |
|
403 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), |
|
404 chain->vissrcpad); |
|
405 |
|
406 done: |
|
407 /* Unblock the pad */ |
|
408 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked, |
|
409 playsink); |
|
410 GST_PLAY_SINK_UNLOCK (playsink); |
|
411 } |
|
412 #ifdef __SYMBIAN32__ |
|
413 EXPORT_C |
|
414 #endif |
|
415 |
|
416 |
|
417 void |
|
418 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis) |
|
419 { |
|
420 GstPlayVisChain *chain; |
|
421 |
|
422 GST_PLAY_SINK_LOCK (playsink); |
|
423 /* first store the new vis */ |
|
424 if (playsink->visualisation) |
|
425 gst_object_unref (playsink->visualisation); |
|
426 playsink->visualisation = gst_object_ref (vis); |
|
427 |
|
428 /* now try to change the plugin in the running vis chain, if we have no chain, |
|
429 * we don't bother, any future vis chain will be created with the new vis |
|
430 * plugin. */ |
|
431 if (!(chain = (GstPlayVisChain *) playsink->vischain)) |
|
432 goto done; |
|
433 |
|
434 /* block the pad, the next time the callback is called we can change the |
|
435 * visualisation. It's possible that this never happens or that the pad was |
|
436 * already blocked. */ |
|
437 GST_DEBUG_OBJECT (playsink, "blocking vis pad"); |
|
438 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked, |
|
439 playsink); |
|
440 done: |
|
441 GST_PLAY_SINK_UNLOCK (playsink); |
|
442 |
|
443 return; |
|
444 } |
|
445 #ifdef __SYMBIAN32__ |
|
446 EXPORT_C |
|
447 #endif |
|
448 |
|
449 |
|
450 void |
|
451 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume) |
|
452 { |
|
453 GstPlayAudioChain *chain; |
|
454 |
|
455 GST_PLAY_SINK_LOCK (playsink); |
|
456 playsink->volume = volume; |
|
457 chain = (GstPlayAudioChain *) playsink->audiochain; |
|
458 if (chain && chain->volume) { |
|
459 g_object_set (chain->volume, "volume", volume, NULL); |
|
460 } |
|
461 GST_PLAY_SINK_UNLOCK (playsink); |
|
462 } |
|
463 #ifdef __SYMBIAN32__ |
|
464 EXPORT_C |
|
465 #endif |
|
466 |
|
467 |
|
468 gdouble |
|
469 gst_play_sink_get_volume (GstPlaySink * playsink) |
|
470 { |
|
471 gdouble result; |
|
472 GstPlayAudioChain *chain; |
|
473 |
|
474 GST_PLAY_SINK_LOCK (playsink); |
|
475 chain = (GstPlayAudioChain *) playsink->audiochain; |
|
476 if (chain && chain->volume) { |
|
477 g_object_get (chain->volume, "volume", &result, NULL); |
|
478 playsink->volume = result; |
|
479 } else { |
|
480 result = playsink->volume; |
|
481 } |
|
482 GST_PLAY_SINK_UNLOCK (playsink); |
|
483 |
|
484 return result; |
|
485 } |
|
486 #ifdef __SYMBIAN32__ |
|
487 EXPORT_C |
|
488 #endif |
|
489 |
|
490 |
|
491 void |
|
492 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute) |
|
493 { |
|
494 GstPlayAudioChain *chain; |
|
495 |
|
496 GST_PLAY_SINK_LOCK (playsink); |
|
497 playsink->mute = mute; |
|
498 chain = (GstPlayAudioChain *) playsink->audiochain; |
|
499 if (chain && chain->mute) { |
|
500 g_object_set (chain->mute, "mute", mute, NULL); |
|
501 } |
|
502 GST_PLAY_SINK_UNLOCK (playsink); |
|
503 } |
|
504 #ifdef __SYMBIAN32__ |
|
505 EXPORT_C |
|
506 #endif |
|
507 |
|
508 |
|
509 gboolean |
|
510 gst_play_sink_get_mute (GstPlaySink * playsink) |
|
511 { |
|
512 gboolean result; |
|
513 GstPlayAudioChain *chain; |
|
514 |
|
515 GST_PLAY_SINK_LOCK (playsink); |
|
516 chain = (GstPlayAudioChain *) playsink->audiochain; |
|
517 if (chain && chain->mute) { |
|
518 g_object_get (chain->mute, "mute", &result, NULL); |
|
519 playsink->mute = result; |
|
520 } else { |
|
521 result = playsink->mute; |
|
522 } |
|
523 GST_PLAY_SINK_UNLOCK (playsink); |
|
524 |
|
525 return result; |
|
526 } |
|
527 |
|
528 static void |
|
529 gst_play_sink_set_property (GObject * object, guint prop_id, |
|
530 const GValue * value, GParamSpec * pspec) |
|
531 { |
|
532 GstPlaySink *playsink; |
|
533 |
|
534 playsink = GST_PLAY_SINK (object); |
|
535 |
|
536 switch (prop_id) { |
|
537 case PROP_VIDEO_SINK: |
|
538 gst_play_sink_set_video_sink (playsink, g_value_get_object (value)); |
|
539 break; |
|
540 case PROP_AUDIO_SINK: |
|
541 gst_play_sink_set_audio_sink (playsink, g_value_get_object (value)); |
|
542 break; |
|
543 case PROP_VIS_PLUGIN: |
|
544 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value)); |
|
545 break; |
|
546 case PROP_VOLUME: |
|
547 gst_play_sink_set_volume (playsink, g_value_get_double (value)); |
|
548 break; |
|
549 case PROP_FONT_DESC: |
|
550 GST_OBJECT_LOCK (playsink); |
|
551 g_free (playsink->font_desc); |
|
552 playsink->font_desc = g_strdup (g_value_get_string (value)); |
|
553 if (playsink->textoverlay_element) { |
|
554 g_object_set (G_OBJECT (playsink->textoverlay_element), |
|
555 "font-desc", g_value_get_string (value), NULL); |
|
556 } |
|
557 GST_OBJECT_UNLOCK (playsink); |
|
558 break; |
|
559 default: |
|
560 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
561 break; |
|
562 } |
|
563 } |
|
564 |
|
565 static void |
|
566 gst_play_sink_get_property (GObject * object, guint prop_id, GValue * value, |
|
567 GParamSpec * pspec) |
|
568 { |
|
569 GstPlaySink *playsink; |
|
570 |
|
571 playsink = GST_PLAY_SINK (object); |
|
572 |
|
573 switch (prop_id) { |
|
574 case PROP_VIDEO_SINK: |
|
575 GST_OBJECT_LOCK (playsink); |
|
576 g_value_set_object (value, playsink->video_sink); |
|
577 GST_OBJECT_UNLOCK (playsink); |
|
578 break; |
|
579 case PROP_AUDIO_SINK: |
|
580 GST_OBJECT_LOCK (playsink); |
|
581 g_value_set_object (value, playsink->audio_sink); |
|
582 GST_OBJECT_UNLOCK (playsink); |
|
583 break; |
|
584 case PROP_VIS_PLUGIN: |
|
585 GST_OBJECT_LOCK (playsink); |
|
586 g_value_set_object (value, playsink->visualisation); |
|
587 GST_OBJECT_UNLOCK (playsink); |
|
588 break; |
|
589 case PROP_VOLUME: |
|
590 g_value_set_double (value, gst_play_sink_get_volume (playsink)); |
|
591 break; |
|
592 case PROP_FRAME: |
|
593 { |
|
594 break; |
|
595 } |
|
596 default: |
|
597 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
598 break; |
|
599 } |
|
600 } |
|
601 |
|
602 static void |
|
603 post_missing_element_message (GstPlaySink * playsink, const gchar * name) |
|
604 { |
|
605 GstMessage *msg; |
|
606 |
|
607 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name); |
|
608 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg); |
|
609 } |
|
610 |
|
611 static void |
|
612 free_chain (GstPlayChain * chain) |
|
613 { |
|
614 if (chain->bin) |
|
615 gst_object_unref (chain->bin); |
|
616 gst_object_unref (chain->playsink); |
|
617 g_free (chain); |
|
618 } |
|
619 |
|
620 static gboolean |
|
621 add_chain (GstPlayChain * chain, gboolean add) |
|
622 { |
|
623 if (chain->added == add) |
|
624 return TRUE; |
|
625 |
|
626 if (add) |
|
627 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin); |
|
628 else |
|
629 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin); |
|
630 |
|
631 chain->added = add; |
|
632 |
|
633 return TRUE; |
|
634 } |
|
635 |
|
636 static gboolean |
|
637 activate_chain (GstPlayChain * chain, gboolean activate) |
|
638 { |
|
639 if (chain->activated == activate) |
|
640 return TRUE; |
|
641 |
|
642 if (activate) |
|
643 gst_element_set_state (chain->bin, GST_STATE_PAUSED); |
|
644 else |
|
645 gst_element_set_state (chain->bin, GST_STATE_NULL); |
|
646 |
|
647 chain->activated = activate; |
|
648 |
|
649 return TRUE; |
|
650 } |
|
651 |
|
652 static gint |
|
653 find_property (GstElement * element, const gchar * name) |
|
654 { |
|
655 gint res; |
|
656 |
|
657 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) { |
|
658 res = 0; |
|
659 GST_DEBUG_OBJECT (element, "found %s property", name); |
|
660 } else { |
|
661 GST_DEBUG_OBJECT (element, "did not find %s property", name); |
|
662 res = 1; |
|
663 gst_object_unref (element); |
|
664 } |
|
665 return res; |
|
666 } |
|
667 |
|
668 /* find an object in the hierarchy with a property named @name */ |
|
669 static GstElement * |
|
670 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj, |
|
671 const gchar * name) |
|
672 { |
|
673 GstElement *result = NULL; |
|
674 GstIterator *it; |
|
675 |
|
676 if (GST_IS_BIN (obj)) { |
|
677 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj)); |
|
678 result = gst_iterator_find_custom (it, |
|
679 (GCompareFunc) find_property, (gpointer) name); |
|
680 gst_iterator_free (it); |
|
681 } else { |
|
682 if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) { |
|
683 result = obj; |
|
684 gst_object_ref (obj); |
|
685 } |
|
686 } |
|
687 return result; |
|
688 } |
|
689 |
|
690 /* make the element (bin) that contains the elements needed to perform |
|
691 * video display. |
|
692 * |
|
693 * +------------------------------------------------------------+ |
|
694 * | vbin | |
|
695 * | +-------+ +----------+ +----------+ +---------+ | |
|
696 * | | queue | |colorspace| |videoscale| |videosink| | |
|
697 * | +-sink src-sink src-sink src-sink | | |
|
698 * | | +-------+ +----------+ +----------+ +---------+ | |
|
699 * sink-+ | |
|
700 * +------------------------------------------------------------+ |
|
701 * |
|
702 */ |
|
703 static GstPlayChain * |
|
704 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async) |
|
705 { |
|
706 GstPlayVideoChain *chain; |
|
707 GstBin *bin; |
|
708 GstPad *pad; |
|
709 |
|
710 chain = g_new0 (GstPlayVideoChain, 1); |
|
711 chain->chain.playsink = gst_object_ref (playsink); |
|
712 |
|
713 if (playsink->video_sink) { |
|
714 chain->sink = playsink->video_sink; |
|
715 } else { |
|
716 chain->sink = gst_element_factory_make ("autovideosink", "videosink"); |
|
717 if (chain->sink == NULL) { |
|
718 chain->sink = gst_element_factory_make ("xvimagesink", "videosink"); |
|
719 } |
|
720 if (chain->sink == NULL) |
|
721 goto no_sinks; |
|
722 } |
|
723 |
|
724 /* if we can disable async behaviour of the sink, we can avoid adding a |
|
725 * queue for the audio chain. We can't use the deep property here because the |
|
726 * sink might change it's internal sink element later. */ |
|
727 if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "async")) { |
|
728 GST_DEBUG_OBJECT (playsink, "setting async property to %d on video sink", |
|
729 async); |
|
730 g_object_set (chain->sink, "async", async, NULL); |
|
731 chain->async = async; |
|
732 } else |
|
733 chain->async = TRUE; |
|
734 |
|
735 /* create a bin to hold objects, as we create them we add them to this bin so |
|
736 * that when something goes wrong we only need to unref the bin */ |
|
737 chain->chain.bin = gst_bin_new ("vbin"); |
|
738 bin = GST_BIN_CAST (chain->chain.bin); |
|
739 gst_object_ref (bin); |
|
740 gst_object_sink (bin); |
|
741 gst_bin_add (bin, chain->sink); |
|
742 |
|
743 if (raw) { |
|
744 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); |
|
745 if (chain->conv == NULL) |
|
746 goto no_colorspace; |
|
747 gst_bin_add (bin, chain->conv); |
|
748 |
|
749 chain->scale = gst_element_factory_make ("videoscale", "vscale"); |
|
750 if (chain->scale == NULL) |
|
751 goto no_videoscale; |
|
752 gst_bin_add (bin, chain->scale); |
|
753 } |
|
754 |
|
755 /* decouple decoder from sink, this improves playback quite a lot since the |
|
756 * decoder can continue while the sink blocks for synchronisation. We don't |
|
757 * need a lot of buffers as this consumes a lot of memory and we don't want |
|
758 * too little because else we would be context switching too quickly. */ |
|
759 chain->queue = gst_element_factory_make ("queue", "vqueue"); |
|
760 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3, |
|
761 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL); |
|
762 gst_bin_add (bin, chain->queue); |
|
763 |
|
764 if (raw) { |
|
765 gst_element_link_pads (chain->queue, "src", chain->conv, "sink"); |
|
766 gst_element_link_pads (chain->conv, "src", chain->scale, "sink"); |
|
767 /* be more careful with the pad from the custom sink element, it might not |
|
768 * be named 'sink' */ |
|
769 if (!gst_element_link_pads (chain->scale, "src", chain->sink, NULL)) |
|
770 goto link_failed; |
|
771 |
|
772 pad = gst_element_get_pad (chain->queue, "sink"); |
|
773 } else { |
|
774 if (!gst_element_link_pads (chain->queue, "src", chain->sink, NULL)) |
|
775 goto link_failed; |
|
776 pad = gst_element_get_pad (chain->queue, "sink"); |
|
777 } |
|
778 |
|
779 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad); |
|
780 gst_object_unref (pad); |
|
781 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad); |
|
782 |
|
783 return (GstPlayChain *) chain; |
|
784 |
|
785 /* ERRORS */ |
|
786 no_sinks: |
|
787 { |
|
788 post_missing_element_message (playsink, "autovideosink"); |
|
789 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
790 (_("Both autovideosink and xvimagesink elements are missing.")), |
|
791 (NULL)); |
|
792 free_chain ((GstPlayChain *) chain); |
|
793 return NULL; |
|
794 } |
|
795 no_colorspace: |
|
796 { |
|
797 post_missing_element_message (playsink, "ffmpegcolorspace"); |
|
798 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
799 (_("Missing element '%s' - check your GStreamer installation."), |
|
800 "ffmpegcolorspace"), (NULL)); |
|
801 free_chain ((GstPlayChain *) chain); |
|
802 return NULL; |
|
803 } |
|
804 |
|
805 no_videoscale: |
|
806 { |
|
807 post_missing_element_message (playsink, "videoscale"); |
|
808 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
809 (_("Missing element '%s' - check your GStreamer installation."), |
|
810 "videoscale"), ("possibly a liboil version mismatch?")); |
|
811 free_chain ((GstPlayChain *) chain); |
|
812 return NULL; |
|
813 } |
|
814 link_failed: |
|
815 { |
|
816 GST_ELEMENT_ERROR (playsink, CORE, PAD, |
|
817 (NULL), ("Failed to configure the video sink.")); |
|
818 free_chain ((GstPlayChain *) chain); |
|
819 return NULL; |
|
820 } |
|
821 } |
|
822 |
|
823 #if 0 |
|
824 /* make an element for playback of video with subtitles embedded. |
|
825 * |
|
826 * +--------------------------------------------------+ |
|
827 * | tbin +-------------+ | |
|
828 * | +-----+ | textoverlay | +------+ | |
|
829 * | | csp | +--video_sink | | vbin | | |
|
830 * video_sink-sink src+ +-text_sink src-sink | | |
|
831 * | +-----+ | +-------------+ +------+ | |
|
832 * text_sink-------------+ | |
|
833 * +--------------------------------------------------+ |
|
834 * |
|
835 * If there is no subtitle renderer this function will simply return the |
|
836 * videosink without the text_sink pad. |
|
837 */ |
|
838 static GstElement * |
|
839 gen_text_element (GstPlaySink * playsink) |
|
840 { |
|
841 GstElement *element, *csp, *overlay, *vbin; |
|
842 GstPad *pad; |
|
843 |
|
844 /* Create the video rendering bin, error is posted when this fails. */ |
|
845 vbin = gen_video_element (playsink); |
|
846 if (!vbin) |
|
847 return NULL; |
|
848 |
|
849 /* Text overlay */ |
|
850 overlay = gst_element_factory_make ("textoverlay", "overlay"); |
|
851 |
|
852 /* If no overlay return the video bin without subtitle support. */ |
|
853 if (!overlay) |
|
854 goto no_overlay; |
|
855 |
|
856 /* Create our bin */ |
|
857 element = gst_bin_new ("textbin"); |
|
858 |
|
859 /* Set some parameters */ |
|
860 g_object_set (G_OBJECT (overlay), |
|
861 "halign", "center", "valign", "bottom", NULL); |
|
862 if (playsink->font_desc) { |
|
863 g_object_set (G_OBJECT (overlay), "font-desc", playsink->font_desc, NULL); |
|
864 } |
|
865 |
|
866 /* Take a ref */ |
|
867 playsink->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay)); |
|
868 |
|
869 /* we know this will succeed, as the video bin already created one before */ |
|
870 csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp"); |
|
871 |
|
872 /* Add our elements */ |
|
873 gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL); |
|
874 |
|
875 /* Link */ |
|
876 gst_element_link_pads (csp, "src", overlay, "video_sink"); |
|
877 gst_element_link_pads (overlay, "src", vbin, "sink"); |
|
878 |
|
879 /* Add ghost pads on the subtitle bin */ |
|
880 pad = gst_element_get_pad (overlay, "text_sink"); |
|
881 gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad)); |
|
882 gst_object_unref (pad); |
|
883 |
|
884 pad = gst_element_get_pad (csp, "sink"); |
|
885 gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); |
|
886 gst_object_unref (pad); |
|
887 |
|
888 /* Set state to READY */ |
|
889 gst_element_set_state (element, GST_STATE_READY); |
|
890 |
|
891 return element; |
|
892 |
|
893 /* ERRORS */ |
|
894 no_overlay: |
|
895 { |
|
896 post_missing_element_message (playsink, "textoverlay"); |
|
897 GST_WARNING_OBJECT (playsink, |
|
898 "No overlay (pango) element, subtitles disabled"); |
|
899 return vbin; |
|
900 } |
|
901 } |
|
902 #endif |
|
903 |
|
904 |
|
905 /* make the chain that contains the elements needed to perform |
|
906 * audio playback. |
|
907 * |
|
908 * We add a tee as the first element so that we can link the visualisation chain |
|
909 * to it when requested. |
|
910 * |
|
911 * +-------------------------------------------------------------+ |
|
912 * | abin | |
|
913 * | +---------+ +----------+ +---------+ +---------+ | |
|
914 * | |audioconv| |audioscale| | volume | |audiosink| | |
|
915 * | +-srck src-sink src-sink src-sink | | |
|
916 * | | +---------+ +----------+ +---------+ +---------+ | |
|
917 * sink-+ | |
|
918 * +-------------------------------------------------------------+ |
|
919 */ |
|
920 static GstPlayChain * |
|
921 gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) |
|
922 { |
|
923 GstPlayAudioChain *chain; |
|
924 GstBin *bin; |
|
925 gboolean res; |
|
926 GstPad *pad; |
|
927 |
|
928 chain = g_new0 (GstPlayAudioChain, 1); |
|
929 chain->chain.playsink = gst_object_ref (playsink); |
|
930 |
|
931 if (playsink->audio_sink) { |
|
932 chain->sink = playsink->audio_sink; |
|
933 } else { |
|
934 chain->sink = gst_element_factory_make ("autoaudiosink", "audiosink"); |
|
935 if (chain->sink == NULL) { |
|
936 chain->sink = gst_element_factory_make ("alsasink", "audiosink"); |
|
937 } |
|
938 if (chain->sink == NULL) |
|
939 goto no_sinks; |
|
940 } |
|
941 chain->chain.bin = gst_bin_new ("abin"); |
|
942 bin = GST_BIN_CAST (chain->chain.bin); |
|
943 gst_object_ref (bin); |
|
944 gst_object_sink (bin); |
|
945 gst_bin_add (bin, chain->sink); |
|
946 |
|
947 if (queue) { |
|
948 /* we have to add a queue when we need to decouple for the video sink in |
|
949 * visualisations */ |
|
950 GST_DEBUG_OBJECT (playsink, "adding audio queue"); |
|
951 chain->queue = gst_element_factory_make ("queue", "aqueue"); |
|
952 gst_bin_add (bin, chain->queue); |
|
953 } |
|
954 |
|
955 if (raw) { |
|
956 chain->conv = gst_element_factory_make ("audioconvert", "aconv"); |
|
957 if (chain->conv == NULL) |
|
958 goto no_audioconvert; |
|
959 gst_bin_add (bin, chain->conv); |
|
960 |
|
961 chain->resample = gst_element_factory_make ("audioresample", "aresample"); |
|
962 if (chain->resample == NULL) |
|
963 goto no_audioresample; |
|
964 gst_bin_add (bin, chain->resample); |
|
965 |
|
966 res = gst_element_link_pads (chain->conv, "src", chain->resample, "sink"); |
|
967 |
|
968 /* FIXME check if the sink has the volume property */ |
|
969 |
|
970 if (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) { |
|
971 chain->volume = gst_element_factory_make ("volume", "volume"); |
|
972 if (chain->volume == NULL) |
|
973 goto no_volume; |
|
974 |
|
975 /* volume also has the mute property */ |
|
976 chain->mute = gst_object_ref (chain->volume); |
|
977 |
|
978 /* configure with the latest volume */ |
|
979 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL); |
|
980 gst_bin_add (bin, chain->volume); |
|
981 |
|
982 res &= |
|
983 gst_element_link_pads (chain->resample, "src", chain->volume, "sink"); |
|
984 res &= gst_element_link_pads (chain->volume, "src", chain->sink, NULL); |
|
985 } else { |
|
986 res &= gst_element_link_pads (chain->resample, "src", chain->sink, NULL); |
|
987 } |
|
988 if (!res) |
|
989 goto link_failed; |
|
990 |
|
991 if (queue) { |
|
992 res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink"); |
|
993 pad = gst_element_get_pad (chain->queue, "sink"); |
|
994 } else { |
|
995 pad = gst_element_get_pad (chain->conv, "sink"); |
|
996 } |
|
997 } else { |
|
998 if (queue) { |
|
999 res = gst_element_link_pads (chain->queue, "src", chain->sink, "sink"); |
|
1000 pad = gst_element_get_pad (chain->queue, "sink"); |
|
1001 } else { |
|
1002 pad = gst_element_get_pad (chain->sink, "sink"); |
|
1003 } |
|
1004 } |
|
1005 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad); |
|
1006 gst_object_unref (pad); |
|
1007 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad); |
|
1008 |
|
1009 return (GstPlayChain *) chain; |
|
1010 |
|
1011 /* ERRORS */ |
|
1012 no_sinks: |
|
1013 { |
|
1014 post_missing_element_message (playsink, "autoaudiosink"); |
|
1015 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1016 (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); |
|
1017 free_chain ((GstPlayChain *) chain); |
|
1018 return NULL; |
|
1019 } |
|
1020 no_audioconvert: |
|
1021 { |
|
1022 post_missing_element_message (playsink, "audioconvert"); |
|
1023 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1024 (_("Missing element '%s' - check your GStreamer installation."), |
|
1025 "audioconvert"), ("possibly a liboil version mismatch?")); |
|
1026 free_chain ((GstPlayChain *) chain); |
|
1027 return NULL; |
|
1028 } |
|
1029 no_audioresample: |
|
1030 { |
|
1031 post_missing_element_message (playsink, "audioresample"); |
|
1032 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1033 (_("Missing element '%s' - check your GStreamer installation."), |
|
1034 "audioresample"), ("possibly a liboil version mismatch?")); |
|
1035 free_chain ((GstPlayChain *) chain); |
|
1036 return NULL; |
|
1037 } |
|
1038 no_volume: |
|
1039 { |
|
1040 post_missing_element_message (playsink, "volume"); |
|
1041 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1042 (_("Missing element '%s' - check your GStreamer installation."), |
|
1043 "volume"), ("possibly a liboil version mismatch?")); |
|
1044 free_chain ((GstPlayChain *) chain); |
|
1045 return NULL; |
|
1046 } |
|
1047 link_failed: |
|
1048 { |
|
1049 GST_ELEMENT_ERROR (playsink, CORE, PAD, |
|
1050 (NULL), ("Failed to configure the audio sink.")); |
|
1051 free_chain ((GstPlayChain *) chain); |
|
1052 return NULL; |
|
1053 } |
|
1054 } |
|
1055 |
|
1056 /* |
|
1057 * +-------------------------------------------------------------------+ |
|
1058 * | visbin | |
|
1059 * | +----------+ +------------+ +----------+ +-------+ | |
|
1060 * | | visqueue | | audioconv | | audiores | | vis | | |
|
1061 * | +-sink src-sink + samp src-sink src-sink src-+ | |
|
1062 * | | +----------+ +------------+ +----------+ +-------+ | | |
|
1063 * sink-+ +-src |
|
1064 * +-------------------------------------------------------------------+ |
|
1065 * |
|
1066 */ |
|
1067 static GstPlayChain * |
|
1068 gen_vis_chain (GstPlaySink * playsink) |
|
1069 { |
|
1070 GstPlayVisChain *chain; |
|
1071 GstBin *bin; |
|
1072 gboolean res; |
|
1073 GstPad *pad; |
|
1074 |
|
1075 chain = g_new0 (GstPlayVisChain, 1); |
|
1076 chain->chain.playsink = gst_object_ref (playsink); |
|
1077 |
|
1078 chain->chain.bin = gst_bin_new ("visbin"); |
|
1079 bin = GST_BIN_CAST (chain->chain.bin); |
|
1080 gst_object_ref (bin); |
|
1081 gst_object_sink (bin); |
|
1082 |
|
1083 /* we're queuing raw audio here, we can remove this queue when we can disable |
|
1084 * async behaviour in the video sink. */ |
|
1085 chain->queue = gst_element_factory_make ("queue", "visqueue"); |
|
1086 gst_bin_add (bin, chain->queue); |
|
1087 |
|
1088 chain->conv = gst_element_factory_make ("audioconvert", "aconv"); |
|
1089 if (chain->conv == NULL) |
|
1090 goto no_audioconvert; |
|
1091 gst_bin_add (bin, chain->conv); |
|
1092 |
|
1093 chain->resample = gst_element_factory_make ("audioresample", "aresample"); |
|
1094 if (chain->resample == NULL) |
|
1095 goto no_audioresample; |
|
1096 gst_bin_add (bin, chain->resample); |
|
1097 |
|
1098 /* this pad will be used for blocking the dataflow and switching the vis |
|
1099 * plugin */ |
|
1100 chain->blockpad = gst_element_get_pad (chain->resample, "src"); |
|
1101 |
|
1102 if (playsink->visualisation) { |
|
1103 chain->vis = gst_object_ref (playsink->visualisation); |
|
1104 } else { |
|
1105 chain->vis = gst_element_factory_make ("goom", "vis"); |
|
1106 if (!chain->vis) |
|
1107 goto no_goom; |
|
1108 } |
|
1109 gst_bin_add (bin, chain->vis); |
|
1110 |
|
1111 res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink"); |
|
1112 res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink"); |
|
1113 res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink"); |
|
1114 if (!res) |
|
1115 goto link_failed; |
|
1116 |
|
1117 chain->vissinkpad = gst_element_get_pad (chain->vis, "sink"); |
|
1118 chain->vissrcpad = gst_element_get_pad (chain->vis, "src"); |
|
1119 |
|
1120 pad = gst_element_get_pad (chain->queue, "sink"); |
|
1121 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad); |
|
1122 gst_object_unref (pad); |
|
1123 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad); |
|
1124 |
|
1125 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad); |
|
1126 gst_element_add_pad (chain->chain.bin, chain->srcpad); |
|
1127 |
|
1128 return (GstPlayChain *) chain; |
|
1129 |
|
1130 /* ERRORS */ |
|
1131 no_audioconvert: |
|
1132 { |
|
1133 post_missing_element_message (playsink, "audioconvert"); |
|
1134 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1135 (_("Missing element '%s' - check your GStreamer installation."), |
|
1136 "audioconvert"), ("possibly a liboil version mismatch?")); |
|
1137 free_chain ((GstPlayChain *) chain); |
|
1138 return NULL; |
|
1139 } |
|
1140 no_audioresample: |
|
1141 { |
|
1142 post_missing_element_message (playsink, "audioresample"); |
|
1143 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1144 (_("Missing element '%s' - check your GStreamer installation."), |
|
1145 "audioresample"), (NULL)); |
|
1146 free_chain ((GstPlayChain *) chain); |
|
1147 return NULL; |
|
1148 } |
|
1149 no_goom: |
|
1150 { |
|
1151 post_missing_element_message (playsink, "goom"); |
|
1152 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
|
1153 (_("Missing element '%s' - check your GStreamer installation."), |
|
1154 "goom"), (NULL)); |
|
1155 free_chain ((GstPlayChain *) chain); |
|
1156 return NULL; |
|
1157 } |
|
1158 link_failed: |
|
1159 { |
|
1160 GST_ELEMENT_ERROR (playsink, CORE, PAD, |
|
1161 (NULL), ("Failed to configure the visualisation element.")); |
|
1162 free_chain ((GstPlayChain *) chain); |
|
1163 return NULL; |
|
1164 } |
|
1165 } |
|
1166 |
|
1167 #if 0 |
|
1168 static gboolean |
|
1169 activate_vis (GstPlaySink * playsink, gboolean activate) |
|
1170 { |
|
1171 /* need to have an audio chain */ |
|
1172 if (!playsink->audiochain || !playsink->vischain) |
|
1173 return FALSE; |
|
1174 |
|
1175 if (playsink->vischain->activated == activate) |
|
1176 return TRUE; |
|
1177 |
|
1178 if (activate) { |
|
1179 /* activation: Add the vis chain to the sink bin . Take a new srcpad from |
|
1180 * the tee of the audio chain and link it to the sinkpad of the vis chain. |
|
1181 */ |
|
1182 |
|
1183 } else { |
|
1184 /* deactivation: release the srcpad from the tee of the audio chain. Set the |
|
1185 * vis chain to NULL and remove it from the sink bin */ |
|
1186 |
|
1187 } |
|
1188 return TRUE; |
|
1189 } |
|
1190 #endif |
|
1191 |
|
1192 /* this function is called when all the request pads are requested and when we |
|
1193 * have to construct the final pipeline. |
|
1194 */ |
|
1195 #ifdef __SYMBIAN32__ |
|
1196 EXPORT_C |
|
1197 #endif |
|
1198 |
|
1199 gboolean |
|
1200 gst_play_sink_reconfigure (GstPlaySink * playsink) |
|
1201 { |
|
1202 GstPlayFlags flags; |
|
1203 gboolean need_audio, need_video, need_vis; |
|
1204 |
|
1205 GST_DEBUG_OBJECT (playsink, "reconfiguring"); |
|
1206 |
|
1207 /* assume we need nothing */ |
|
1208 need_audio = need_video = need_vis = FALSE; |
|
1209 |
|
1210 GST_PLAY_SINK_LOCK (playsink); |
|
1211 GST_OBJECT_LOCK (playsink); |
|
1212 /* get flags, there are protected with the object lock */ |
|
1213 flags = playsink->flags; |
|
1214 GST_OBJECT_UNLOCK (playsink); |
|
1215 |
|
1216 /* figure out which components we need */ |
|
1217 if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) { |
|
1218 /* we have video and we are requested to show it */ |
|
1219 need_video = TRUE; |
|
1220 } |
|
1221 if (playsink->audio_pad) { |
|
1222 if (flags & GST_PLAY_FLAG_AUDIO) { |
|
1223 need_audio = TRUE; |
|
1224 } |
|
1225 if (flags & GST_PLAY_FLAG_VIS && !need_video) { |
|
1226 /* also add video when we add visualisation */ |
|
1227 need_video = TRUE; |
|
1228 need_vis = TRUE; |
|
1229 } |
|
1230 } |
|
1231 |
|
1232 if (need_video) { |
|
1233 GST_DEBUG_OBJECT (playsink, "adding video, raw %d", |
|
1234 playsink->video_pad_raw); |
|
1235 if (!playsink->videochain) { |
|
1236 gboolean raw, async; |
|
1237 |
|
1238 /* we need a raw sink when we do vis or when we have a raw pad */ |
|
1239 raw = need_vis ? TRUE : playsink->video_pad_raw; |
|
1240 /* we try to set the sink async=FALSE when we need vis, this way we can |
|
1241 * avoid a queue in the audio chain. */ |
|
1242 async = !need_vis; |
|
1243 |
|
1244 playsink->videochain = gen_video_chain (playsink, raw, async); |
|
1245 } |
|
1246 add_chain (playsink->videochain, TRUE); |
|
1247 activate_chain (playsink->videochain, TRUE); |
|
1248 if (!need_vis) |
|
1249 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), |
|
1250 playsink->videochain->sinkpad); |
|
1251 } else { |
|
1252 if (playsink->videochain) { |
|
1253 add_chain (playsink->videochain, FALSE); |
|
1254 activate_chain (playsink->videochain, FALSE); |
|
1255 } |
|
1256 if (playsink->video_pad) |
|
1257 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); |
|
1258 } |
|
1259 |
|
1260 if (need_audio) { |
|
1261 GST_DEBUG_OBJECT (playsink, "adding audio"); |
|
1262 if (!playsink->audiochain) { |
|
1263 gboolean raw, queue; |
|
1264 |
|
1265 /* get a raw sink if we are asked for a raw pad */ |
|
1266 raw = playsink->audio_pad_raw; |
|
1267 if (need_vis) { |
|
1268 /* If we are dealing with visualisations, we need to add a queue to |
|
1269 * decouple the audio from the video part. We only have to do this when |
|
1270 * the video part is async=true */ |
|
1271 queue = ((GstPlayVideoChain *) playsink->videochain)->async; |
|
1272 GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue); |
|
1273 } else { |
|
1274 /* no vis, we can avoid a queue */ |
|
1275 GST_DEBUG_OBJECT (playsink, "don't need audio queue"); |
|
1276 queue = FALSE; |
|
1277 } |
|
1278 |
|
1279 playsink->audiochain = gen_audio_chain (playsink, raw, queue); |
|
1280 } |
|
1281 add_chain (playsink->audiochain, TRUE); |
|
1282 gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad); |
|
1283 activate_chain (playsink->audiochain, TRUE); |
|
1284 } else { |
|
1285 /* we have no audio or we are requested to not play audio */ |
|
1286 if (playsink->audiochain) { |
|
1287 gst_pad_unlink (playsink->audio_tee_asrc, playsink->audiochain->sinkpad); |
|
1288 add_chain (playsink->audiochain, FALSE); |
|
1289 activate_chain (playsink->audiochain, FALSE); |
|
1290 } |
|
1291 } |
|
1292 |
|
1293 if (need_vis) { |
|
1294 GstPad *srcpad; |
|
1295 |
|
1296 if (!playsink->vischain) |
|
1297 playsink->vischain = gen_vis_chain (playsink); |
|
1298 |
|
1299 GST_DEBUG_OBJECT (playsink, "adding visualisation"); |
|
1300 |
|
1301 srcpad = |
|
1302 gst_element_get_pad (GST_ELEMENT_CAST (playsink->vischain->bin), "src"); |
|
1303 add_chain (playsink->vischain, TRUE); |
|
1304 gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad); |
|
1305 gst_pad_link (srcpad, playsink->videochain->sinkpad); |
|
1306 gst_object_unref (srcpad); |
|
1307 activate_chain (playsink->vischain, TRUE); |
|
1308 } else { |
|
1309 if (playsink->vischain) { |
|
1310 add_chain (playsink->vischain, FALSE); |
|
1311 activate_chain (playsink->vischain, FALSE); |
|
1312 } |
|
1313 } |
|
1314 GST_PLAY_SINK_UNLOCK (playsink); |
|
1315 |
|
1316 return TRUE; |
|
1317 } |
|
1318 #ifdef __SYMBIAN32__ |
|
1319 EXPORT_C |
|
1320 #endif |
|
1321 |
|
1322 |
|
1323 gboolean |
|
1324 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags) |
|
1325 { |
|
1326 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE); |
|
1327 |
|
1328 GST_OBJECT_LOCK (playsink); |
|
1329 playsink->flags = flags; |
|
1330 GST_OBJECT_UNLOCK (playsink); |
|
1331 |
|
1332 return TRUE; |
|
1333 } |
|
1334 #ifdef __SYMBIAN32__ |
|
1335 EXPORT_C |
|
1336 #endif |
|
1337 |
|
1338 |
|
1339 GstPlayFlags |
|
1340 gst_play_sink_get_flags (GstPlaySink * playsink) |
|
1341 { |
|
1342 GstPlayFlags res; |
|
1343 |
|
1344 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0); |
|
1345 |
|
1346 GST_OBJECT_LOCK (playsink); |
|
1347 res = playsink->flags; |
|
1348 GST_OBJECT_UNLOCK (playsink); |
|
1349 |
|
1350 return res; |
|
1351 } |
|
1352 #ifdef __SYMBIAN32__ |
|
1353 EXPORT_C |
|
1354 #endif |
|
1355 |
|
1356 |
|
1357 GstBuffer * |
|
1358 gst_play_sink_get_last_frame (GstPlaySink * playsink) |
|
1359 { |
|
1360 GstBuffer *result = NULL; |
|
1361 GstPlayVideoChain *chain; |
|
1362 |
|
1363 GST_PLAY_SINK_LOCK (playsink); |
|
1364 GST_DEBUG_OBJECT (playsink, "taking last frame"); |
|
1365 /* get the video chain if we can */ |
|
1366 if ((chain = (GstPlayVideoChain *) playsink->videochain)) { |
|
1367 GST_DEBUG_OBJECT (playsink, "found video chain"); |
|
1368 /* see if the chain is active */ |
|
1369 if (chain->chain.activated && chain->sink) { |
|
1370 GstElement *elem; |
|
1371 |
|
1372 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink"); |
|
1373 |
|
1374 /* find and get the last-buffer property now */ |
|
1375 if ((elem = |
|
1376 gst_play_sink_find_property (playsink, chain->sink, |
|
1377 "last-buffer"))) { |
|
1378 GST_DEBUG_OBJECT (playsink, "getting last-buffer property"); |
|
1379 g_object_get (elem, "last-buffer", &result, NULL); |
|
1380 gst_object_unref (elem); |
|
1381 } |
|
1382 } |
|
1383 } |
|
1384 GST_PLAY_SINK_UNLOCK (playsink); |
|
1385 |
|
1386 return result; |
|
1387 } |
|
1388 #ifdef __SYMBIAN32__ |
|
1389 EXPORT_C |
|
1390 #endif |
|
1391 |
|
1392 |
|
1393 GstPad * |
|
1394 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type) |
|
1395 { |
|
1396 GstPad *res = NULL; |
|
1397 gboolean created = FALSE; |
|
1398 gboolean raw = FALSE; |
|
1399 |
|
1400 GST_PLAY_SINK_LOCK (playsink); |
|
1401 switch (type) { |
|
1402 case GST_PLAY_SINK_TYPE_AUDIO_RAW: |
|
1403 raw = TRUE; |
|
1404 case GST_PLAY_SINK_TYPE_AUDIO: |
|
1405 if (!playsink->audio_tee) { |
|
1406 /* create tee when needed. This element will feed the audio sink chain |
|
1407 * and the vis chain. */ |
|
1408 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee"); |
|
1409 playsink->audio_tee_sink = |
|
1410 gst_element_get_pad (playsink->audio_tee, "sink"); |
|
1411 /* get two request pads */ |
|
1412 playsink->audio_tee_vissrc = |
|
1413 gst_element_get_request_pad (playsink->audio_tee, "src%d"); |
|
1414 playsink->audio_tee_asrc = |
|
1415 gst_element_get_request_pad (playsink->audio_tee, "src%d"); |
|
1416 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee); |
|
1417 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED); |
|
1418 } |
|
1419 if (!playsink->audio_pad) { |
|
1420 playsink->audio_pad = |
|
1421 gst_ghost_pad_new ("audio_sink", playsink->audio_tee_sink); |
|
1422 created = TRUE; |
|
1423 } |
|
1424 playsink->audio_pad_raw = raw; |
|
1425 res = playsink->audio_pad; |
|
1426 break; |
|
1427 case GST_PLAY_SINK_TYPE_VIDEO_RAW: |
|
1428 raw = TRUE; |
|
1429 case GST_PLAY_SINK_TYPE_VIDEO: |
|
1430 if (!playsink->video_pad) { |
|
1431 playsink->video_pad = |
|
1432 gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK); |
|
1433 created = TRUE; |
|
1434 } |
|
1435 playsink->video_pad_raw = raw; |
|
1436 res = playsink->video_pad; |
|
1437 break; |
|
1438 case GST_PLAY_SINK_TYPE_TEXT: |
|
1439 if (!playsink->text_pad) { |
|
1440 playsink->text_pad = |
|
1441 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK); |
|
1442 created = TRUE; |
|
1443 } |
|
1444 res = playsink->text_pad; |
|
1445 break; |
|
1446 default: |
|
1447 res = NULL; |
|
1448 break; |
|
1449 } |
|
1450 GST_PLAY_SINK_UNLOCK (playsink); |
|
1451 |
|
1452 if (created && res) { |
|
1453 gst_pad_set_active (res, TRUE); |
|
1454 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res); |
|
1455 } |
|
1456 |
|
1457 return res; |
|
1458 } |
|
1459 #ifdef __SYMBIAN32__ |
|
1460 EXPORT_C |
|
1461 #endif |
|
1462 |
|
1463 |
|
1464 void |
|
1465 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad) |
|
1466 { |
|
1467 GstPad **res = NULL; |
|
1468 |
|
1469 GST_PLAY_SINK_LOCK (playsink); |
|
1470 if (pad == playsink->video_pad) { |
|
1471 res = &playsink->video_pad; |
|
1472 } else if (pad == playsink->audio_pad) { |
|
1473 res = &playsink->audio_pad; |
|
1474 } else if (pad == playsink->text_pad) { |
|
1475 res = &playsink->text_pad; |
|
1476 } |
|
1477 GST_PLAY_SINK_UNLOCK (playsink); |
|
1478 |
|
1479 if (*res) { |
|
1480 gst_pad_set_active (*res, FALSE); |
|
1481 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res); |
|
1482 *res = NULL; |
|
1483 } |
|
1484 } |
|
1485 |
|
1486 /* Send an event to our sinks until one of them works; don't then send to the |
|
1487 * remaining sinks (unlike GstBin) |
|
1488 */ |
|
1489 static gboolean |
|
1490 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event) |
|
1491 { |
|
1492 gboolean res = TRUE; |
|
1493 |
|
1494 if (playsink->audiochain) { |
|
1495 gst_event_ref (event); |
|
1496 if ((res = gst_element_send_event (playsink->audiochain->bin, event))) { |
|
1497 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink"); |
|
1498 goto done; |
|
1499 } |
|
1500 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink"); |
|
1501 } |
|
1502 if (playsink->videochain) { |
|
1503 gst_event_ref (event); |
|
1504 if ((res = gst_element_send_event (playsink->videochain->bin, event))) { |
|
1505 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink"); |
|
1506 goto done; |
|
1507 } |
|
1508 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink"); |
|
1509 } |
|
1510 done: |
|
1511 gst_event_unref (event); |
|
1512 return res; |
|
1513 } |
|
1514 |
|
1515 /* We only want to send the event to a single sink (overriding GstBin's |
|
1516 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek |
|
1517 * events appropriately. So, this is a messy duplication of code. */ |
|
1518 static gboolean |
|
1519 gst_play_sink_send_event (GstElement * element, GstEvent * event) |
|
1520 { |
|
1521 gboolean res = FALSE; |
|
1522 GstEventType event_type = GST_EVENT_TYPE (event); |
|
1523 |
|
1524 switch (event_type) { |
|
1525 case GST_EVENT_SEEK: |
|
1526 GST_DEBUG_OBJECT (element, "Sending seek event to a sink"); |
|
1527 res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event); |
|
1528 break; |
|
1529 default: |
|
1530 res = parent_class->send_event (element, event); |
|
1531 break; |
|
1532 } |
|
1533 return res; |
|
1534 } |
|
1535 |
|
1536 static GstStateChangeReturn |
|
1537 gst_play_sink_change_state (GstElement * element, GstStateChange transition) |
|
1538 { |
|
1539 GstStateChangeReturn ret; |
|
1540 GstPlaySink *playsink; |
|
1541 |
|
1542 playsink = GST_PLAY_SINK (element); |
|
1543 |
|
1544 switch (transition) { |
|
1545 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
1546 break; |
|
1547 default: |
|
1548 break; |
|
1549 } |
|
1550 |
|
1551 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
1552 if (ret == GST_STATE_CHANGE_FAILURE) |
|
1553 return ret; |
|
1554 |
|
1555 switch (transition) { |
|
1556 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
1557 break; |
|
1558 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
|
1559 /* FIXME Release audio device when we implement that */ |
|
1560 break; |
|
1561 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
1562 /* remove sinks we added */ |
|
1563 if (playsink->videochain) { |
|
1564 activate_chain (playsink->videochain, FALSE); |
|
1565 add_chain (playsink->videochain, FALSE); |
|
1566 } |
|
1567 if (playsink->audiochain) { |
|
1568 activate_chain (playsink->audiochain, FALSE); |
|
1569 add_chain (playsink->audiochain, FALSE); |
|
1570 } |
|
1571 break; |
|
1572 default: |
|
1573 break; |
|
1574 } |
|
1575 |
|
1576 return ret; |
|
1577 } |