216 |
213 |
217 gobject_klass = (GObjectClass *) klass; |
214 gobject_klass = (GObjectClass *) klass; |
218 gstelement_klass = (GstElementClass *) klass; |
215 gstelement_klass = (GstElementClass *) klass; |
219 gstbin_klass = (GstBinClass *) klass; |
216 gstbin_klass = (GstBinClass *) klass; |
220 |
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 |
221 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose); |
223 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose); |
222 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize); |
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)); |
223 |
250 |
224 gst_element_class_set_details (gstelement_klass, &gst_play_sink_details); |
251 gst_element_class_set_details (gstelement_klass, &gst_play_sink_details); |
225 |
252 |
226 gstelement_klass->change_state = |
253 gstelement_klass->change_state = |
227 GST_DEBUG_FUNCPTR (gst_play_sink_change_state); |
254 GST_DEBUG_FUNCPTR (gst_play_sink_change_state); |
228 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event); |
255 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event); |
229 |
|
230 gstbin_klass->handle_message = |
|
231 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message); |
|
232 |
256 |
233 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin"); |
257 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin"); |
234 } |
258 } |
235 |
259 |
236 static void |
260 static void |
329 |
311 |
330 playsink = GST_PLAY_SINK (object); |
312 playsink = GST_PLAY_SINK (object); |
331 |
313 |
332 g_mutex_free (playsink->lock); |
314 g_mutex_free (playsink->lock); |
333 |
315 |
334 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object); |
316 G_OBJECT_CLASS (parent_class)->finalize (object); |
335 } |
317 } |
336 #ifdef __SYMBIAN32__ |
318 #ifdef __SYMBIAN32__ |
337 EXPORT_C |
319 EXPORT_C |
338 #endif |
320 #endif |
339 |
321 |
|
322 |
340 void |
323 void |
341 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type, |
324 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink) |
342 GstElement * sink) |
325 { |
343 { |
326 GST_OBJECT_LOCK (playsink); |
344 GstElement **elem = NULL, *old = NULL; |
327 if (playsink->video_sink) |
345 |
328 gst_object_unref (playsink->video_sink); |
346 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type); |
329 |
347 |
330 if (sink) { |
348 GST_PLAY_SINK_LOCK (playsink); |
331 gst_object_ref (sink); |
349 switch (type) { |
332 gst_object_sink (sink); |
350 case GST_PLAY_SINK_TYPE_AUDIO: |
333 } |
351 case GST_PLAY_SINK_TYPE_AUDIO_RAW: |
334 playsink->video_sink = sink; |
352 elem = &playsink->audio_sink; |
335 GST_OBJECT_UNLOCK (playsink); |
353 break; |
336 } |
354 case GST_PLAY_SINK_TYPE_VIDEO: |
|
355 case GST_PLAY_SINK_TYPE_VIDEO_RAW: |
|
356 elem = &playsink->video_sink; |
|
357 break; |
|
358 case GST_PLAY_SINK_TYPE_TEXT: |
|
359 elem = &playsink->text_sink; |
|
360 break; |
|
361 case GST_PLAY_SINK_TYPE_SUBPIC: |
|
362 elem = &playsink->subp_sink; |
|
363 break; |
|
364 default: |
|
365 break; |
|
366 } |
|
367 if (elem) { |
|
368 old = *elem; |
|
369 if (sink) |
|
370 gst_object_ref (sink); |
|
371 *elem = sink; |
|
372 } |
|
373 GST_PLAY_SINK_UNLOCK (playsink); |
|
374 |
|
375 if (old) |
|
376 gst_object_unref (old); |
|
377 } |
|
378 |
|
379 #ifdef __SYMBIAN32__ |
337 #ifdef __SYMBIAN32__ |
380 EXPORT_C |
338 EXPORT_C |
381 #endif |
339 #endif |
382 |
340 |
383 GstElement * |
341 |
384 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type) |
342 void |
385 { |
343 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink) |
386 GstElement *result = NULL; |
344 { |
387 GstElement *elem = NULL, *chainp = NULL; |
345 GST_OBJECT_LOCK (playsink); |
388 |
346 if (playsink->audio_sink) |
389 GST_PLAY_SINK_LOCK (playsink); |
347 gst_object_unref (playsink->audio_sink); |
390 switch (type) { |
348 |
391 case GST_PLAY_SINK_TYPE_AUDIO: |
349 if (sink) { |
392 { |
350 gst_object_ref (sink); |
393 GstPlayAudioChain *chain; |
351 gst_object_sink (sink); |
394 if ((chain = (GstPlayAudioChain *) playsink->audiochain)) |
352 } |
395 chainp = chain->sink; |
353 playsink->audio_sink = sink; |
396 elem = playsink->audio_sink; |
354 GST_OBJECT_UNLOCK (playsink); |
397 break; |
|
398 } |
|
399 case GST_PLAY_SINK_TYPE_VIDEO: |
|
400 { |
|
401 GstPlayVideoChain *chain; |
|
402 if ((chain = (GstPlayVideoChain *) playsink->videochain)) |
|
403 chainp = chain->sink; |
|
404 elem = playsink->video_sink; |
|
405 break; |
|
406 } |
|
407 case GST_PLAY_SINK_TYPE_TEXT: |
|
408 { |
|
409 GstPlayTextChain *chain; |
|
410 if ((chain = (GstPlayTextChain *) playsink->textchain)) |
|
411 chainp = chain->sink; |
|
412 elem = playsink->text_sink; |
|
413 break; |
|
414 } |
|
415 case GST_PLAY_SINK_TYPE_SUBPIC: |
|
416 { |
|
417 GstPlaySubpChain *chain; |
|
418 if ((chain = (GstPlaySubpChain *) playsink->subpchain)) |
|
419 chainp = chain->sink; |
|
420 elem = playsink->subp_sink; |
|
421 break; |
|
422 } |
|
423 default: |
|
424 break; |
|
425 } |
|
426 if (chainp) { |
|
427 /* we have an active chain with a sink, get the sink */ |
|
428 result = gst_object_ref (chainp); |
|
429 } |
|
430 /* nothing found, return last configured sink */ |
|
431 if (result == NULL && elem) |
|
432 result = gst_object_ref (elem); |
|
433 GST_PLAY_SINK_UNLOCK (playsink); |
|
434 |
|
435 return result; |
|
436 } |
355 } |
437 |
356 |
438 static void |
357 static void |
439 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked, |
358 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked, |
440 gpointer user_data) |
359 gpointer user_data) |
859 * | | +-------+ +----------+ +----------+ +---------+ | |
698 * | | +-------+ +----------+ +----------+ +---------+ | |
860 * sink-+ | |
699 * sink-+ | |
861 * +------------------------------------------------------------+ |
700 * +------------------------------------------------------------+ |
862 * |
701 * |
863 */ |
702 */ |
864 static GstPlayVideoChain * |
703 static GstPlayChain * |
865 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, |
704 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async) |
866 gboolean queue) |
|
867 { |
705 { |
868 GstPlayVideoChain *chain; |
706 GstPlayVideoChain *chain; |
869 GstBin *bin; |
707 GstBin *bin; |
870 GstPad *pad; |
708 GstPad *pad; |
871 GstElement *head, *prev, *elem; |
|
872 |
709 |
873 chain = g_new0 (GstPlayVideoChain, 1); |
710 chain = g_new0 (GstPlayVideoChain, 1); |
874 chain->chain.playsink = playsink; |
711 chain->chain.playsink = gst_object_ref (playsink); |
875 chain->chain.raw = raw; |
|
876 |
|
877 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain); |
|
878 |
712 |
879 if (playsink->video_sink) { |
713 if (playsink->video_sink) { |
880 GST_DEBUG_OBJECT (playsink, "trying configured videosink"); |
714 chain->sink = playsink->video_sink; |
881 chain->sink = try_element (playsink, playsink->video_sink, FALSE); |
715 } else { |
882 } |
716 chain->sink = gst_element_factory_make ("autovideosink", "videosink"); |
883 if (chain->sink == NULL) { |
717 if (chain->sink == NULL) { |
884 GST_DEBUG_OBJECT (playsink, "trying autovideosink"); |
718 chain->sink = gst_element_factory_make ("xvimagesink", "videosink"); |
885 elem = gst_element_factory_make ("autovideosink", "videosink"); |
719 } |
886 chain->sink = try_element (playsink, elem, TRUE); |
720 if (chain->sink == NULL) |
887 } |
721 goto no_sinks; |
888 /* FIXME: if DEFAULT_VIDEOSINK != "autovideosink" try this now */ |
722 } |
889 if (chain->sink == NULL) { |
|
890 GST_DEBUG_OBJECT (playsink, "trying xvimagesink"); |
|
891 elem = gst_element_factory_make ("xvimagesink", "videosink"); |
|
892 chain->sink = try_element (playsink, elem, TRUE); |
|
893 } |
|
894 if (chain->sink == NULL) |
|
895 goto no_sinks; |
|
896 |
723 |
897 /* if we can disable async behaviour of the sink, we can avoid adding a |
724 /* if we can disable async behaviour of the sink, we can avoid adding a |
898 * queue for the audio chain. */ |
725 * queue for the audio chain. We can't use the deep property here because the |
899 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async"); |
726 * sink might change it's internal sink element later. */ |
900 if (elem) { |
727 if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "async")) { |
901 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s", |
728 GST_DEBUG_OBJECT (playsink, "setting async property to %d on video sink", |
902 async, GST_ELEMENT_NAME (elem)); |
729 async); |
903 g_object_set (elem, "async", async, NULL); |
730 g_object_set (chain->sink, "async", async, NULL); |
904 chain->async = async; |
731 chain->async = async; |
905 } else { |
732 } else |
906 GST_DEBUG_OBJECT (playsink, "no async property on the sink"); |
|
907 chain->async = TRUE; |
733 chain->async = TRUE; |
908 } |
|
909 |
734 |
910 /* create a bin to hold objects, as we create them we add them to this bin so |
735 /* create a bin to hold objects, as we create them we add them to this bin so |
911 * that when something goes wrong we only need to unref the bin */ |
736 * that when something goes wrong we only need to unref the bin */ |
912 chain->chain.bin = gst_bin_new ("vbin"); |
737 chain->chain.bin = gst_bin_new ("vbin"); |
913 bin = GST_BIN_CAST (chain->chain.bin); |
738 bin = GST_BIN_CAST (chain->chain.bin); |
914 gst_object_ref (bin); |
739 gst_object_ref (bin); |
915 gst_object_sink (bin); |
740 gst_object_sink (bin); |
916 gst_bin_add (bin, chain->sink); |
741 gst_bin_add (bin, chain->sink); |
917 |
742 |
918 if (queue) { |
743 if (raw) { |
919 /* decouple decoder from sink, this improves playback quite a lot since the |
744 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); |
920 * decoder can continue while the sink blocks for synchronisation. We don't |
745 if (chain->conv == NULL) |
921 * need a lot of buffers as this consumes a lot of memory and we don't want |
746 goto no_colorspace; |
922 * too little because else we would be context switching too quickly. */ |
747 gst_bin_add (bin, chain->conv); |
923 chain->queue = gst_element_factory_make ("queue", "vqueue"); |
748 |
924 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3, |
749 chain->scale = gst_element_factory_make ("videoscale", "vscale"); |
925 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL); |
750 if (chain->scale == NULL) |
926 gst_bin_add (bin, chain->queue); |
751 goto no_videoscale; |
927 head = prev = chain->queue; |
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"); |
928 } else { |
773 } else { |
929 head = chain->sink; |
774 if (!gst_element_link_pads (chain->queue, "src", chain->sink, NULL)) |
930 prev = NULL; |
|
931 } |
|
932 |
|
933 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { |
|
934 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace"); |
|
935 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); |
|
936 if (chain->conv == NULL) { |
|
937 post_missing_element_message (playsink, "ffmpegcolorspace"); |
|
938 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
939 (_("Missing element '%s' - check your GStreamer installation."), |
|
940 "ffmpegcolorspace"), ("video rendering might fail")); |
|
941 } else { |
|
942 gst_bin_add (bin, chain->conv); |
|
943 if (prev) { |
|
944 if (!gst_element_link_pads (prev, "src", chain->conv, "sink")) |
|
945 goto link_failed; |
|
946 } else { |
|
947 head = chain->conv; |
|
948 } |
|
949 prev = chain->conv; |
|
950 } |
|
951 |
|
952 GST_DEBUG_OBJECT (playsink, "creating videoscale"); |
|
953 chain->scale = gst_element_factory_make ("videoscale", "vscale"); |
|
954 if (chain->scale == NULL) { |
|
955 post_missing_element_message (playsink, "videoscale"); |
|
956 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
957 (_("Missing element '%s' - check your GStreamer installation."), |
|
958 "videoscale"), ("possibly a liboil version mismatch?")); |
|
959 } else { |
|
960 gst_bin_add (bin, chain->scale); |
|
961 if (prev) { |
|
962 if (!gst_element_link_pads (prev, "src", chain->scale, "sink")) |
|
963 goto link_failed; |
|
964 } else { |
|
965 head = chain->scale; |
|
966 } |
|
967 prev = chain->scale; |
|
968 } |
|
969 } |
|
970 |
|
971 if (prev) { |
|
972 GST_DEBUG_OBJECT (playsink, "linking to sink"); |
|
973 if (!gst_element_link_pads (prev, "src", chain->sink, NULL)) |
|
974 goto link_failed; |
775 goto link_failed; |
975 } |
776 pad = gst_element_get_pad (chain->queue, "sink"); |
976 |
777 } |
977 pad = gst_element_get_static_pad (head, "sink"); |
778 |
978 chain->sinkpad = gst_ghost_pad_new ("sink", pad); |
779 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad); |
979 gst_object_unref (pad); |
780 gst_object_unref (pad); |
980 |
781 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad); |
981 gst_element_add_pad (chain->chain.bin, chain->sinkpad); |
782 |
982 |
783 return (GstPlayChain *) chain; |
983 return chain; |
|
984 |
784 |
985 /* ERRORS */ |
785 /* ERRORS */ |
986 no_sinks: |
786 no_sinks: |
987 { |
787 { |
988 if (!elem) { |
788 post_missing_element_message (playsink, "autovideosink"); |
989 post_missing_element_message (playsink, "autovideosink"); |
789 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
990 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
790 (_("Both autovideosink and xvimagesink elements are missing.")), |
991 (_("Both autovideosink and xvimagesink elements are missing.")), |
791 (NULL)); |
992 (NULL)); |
792 free_chain ((GstPlayChain *) chain); |
993 } else { |
793 return NULL; |
994 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE, |
794 } |
995 (_("Both autovideosink and xvimagesink elements are not working.")), |
795 no_colorspace: |
996 (NULL)); |
796 { |
997 } |
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?")); |
998 free_chain ((GstPlayChain *) chain); |
811 free_chain ((GstPlayChain *) chain); |
999 return NULL; |
812 return NULL; |
1000 } |
813 } |
1001 link_failed: |
814 link_failed: |
1002 { |
815 { |
1005 free_chain ((GstPlayChain *) chain); |
818 free_chain ((GstPlayChain *) chain); |
1006 return NULL; |
819 return NULL; |
1007 } |
820 } |
1008 } |
821 } |
1009 |
822 |
1010 static gboolean |
823 #if 0 |
1011 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, |
|
1012 gboolean queue) |
|
1013 { |
|
1014 GstElement *elem; |
|
1015 GstPlayVideoChain *chain; |
|
1016 GstStateChangeReturn ret; |
|
1017 |
|
1018 chain = playsink->videochain; |
|
1019 |
|
1020 /* if the chain was active we don't do anything */ |
|
1021 if (GST_PLAY_CHAIN (chain)->activated == TRUE) |
|
1022 return TRUE; |
|
1023 |
|
1024 if (chain->chain.raw != raw) |
|
1025 return FALSE; |
|
1026 |
|
1027 /* try to set the sink element to READY again */ |
|
1028 ret = gst_element_set_state (chain->sink, GST_STATE_READY); |
|
1029 if (ret == GST_STATE_CHANGE_FAILURE) |
|
1030 return FALSE; |
|
1031 |
|
1032 /* if we can disable async behaviour of the sink, we can avoid adding a |
|
1033 * queue for the audio chain. */ |
|
1034 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async"); |
|
1035 if (elem) { |
|
1036 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s", |
|
1037 async, GST_ELEMENT_NAME (elem)); |
|
1038 g_object_set (elem, "async", async, NULL); |
|
1039 chain->async = async; |
|
1040 } else { |
|
1041 GST_DEBUG_OBJECT (playsink, "no async property on the sink"); |
|
1042 chain->async = TRUE; |
|
1043 } |
|
1044 return TRUE; |
|
1045 } |
|
1046 |
|
1047 /* make an element for playback of video with subtitles embedded. |
824 /* make an element for playback of video with subtitles embedded. |
1048 * |
825 * |
1049 * +----------------------------------------------+ |
826 * +--------------------------------------------------+ |
1050 * | tbin +-------------+ | |
827 * | tbin +-------------+ | |
1051 * | +-----+ | textoverlay | | |
828 * | +-----+ | textoverlay | +------+ | |
1052 * | | csp | +--video_sink | | |
829 * | | csp | +--video_sink | | vbin | | |
1053 * sink-------sink src+ +-text_sink src--+ | |
830 * video_sink-sink src+ +-text_sink src-sink | | |
1054 * | +-----+ | +-------------+ +-- src |
831 * | +-----+ | +-------------+ +------+ | |
1055 * text_sink-------------+ | |
832 * text_sink-------------+ | |
1056 * +----------------------------------------------+ |
833 * +--------------------------------------------------+ |
|
834 * |
|
835 * If there is no subtitle renderer this function will simply return the |
|
836 * videosink without the text_sink pad. |
1057 */ |
837 */ |
1058 static GstPlayTextChain * |
838 static GstElement * |
1059 gen_text_chain (GstPlaySink * playsink) |
839 gen_text_element (GstPlaySink * playsink) |
1060 { |
840 { |
1061 GstPlayTextChain *chain; |
841 GstElement *element, *csp, *overlay, *vbin; |
1062 GstBin *bin; |
842 GstPad *pad; |
1063 GstElement *elem; |
843 |
1064 GstPad *videosinkpad, *textsinkpad, *srcpad; |
844 /* Create the video rendering bin, error is posted when this fails. */ |
1065 |
845 vbin = gen_video_element (playsink); |
1066 chain = g_new0 (GstPlayTextChain, 1); |
846 if (!vbin) |
1067 chain->chain.playsink = playsink; |
847 return NULL; |
1068 |
848 |
1069 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain); |
849 /* Text overlay */ |
1070 |
850 overlay = gst_element_factory_make ("textoverlay", "overlay"); |
1071 chain->chain.bin = gst_bin_new ("tbin"); |
851 |
1072 bin = GST_BIN_CAST (chain->chain.bin); |
852 /* If no overlay return the video bin without subtitle support. */ |
1073 gst_object_ref (bin); |
853 if (!overlay) |
1074 gst_object_sink (bin); |
854 goto no_overlay; |
1075 |
855 |
1076 videosinkpad = textsinkpad = srcpad = NULL; |
856 /* Create our bin */ |
1077 |
857 element = gst_bin_new ("textbin"); |
1078 /* first try to hook the text pad to the custom sink */ |
858 |
1079 if (playsink->text_sink) { |
859 /* Set some parameters */ |
1080 GST_DEBUG_OBJECT (playsink, "trying configured textsink"); |
860 g_object_set (G_OBJECT (overlay), |
1081 chain->sink = try_element (playsink, playsink->text_sink, FALSE); |
861 "halign", "center", "valign", "bottom", NULL); |
1082 if (chain->sink) { |
862 if (playsink->font_desc) { |
1083 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async"); |
863 g_object_set (G_OBJECT (overlay), "font-desc", playsink->font_desc, NULL); |
1084 if (elem) { |
864 } |
1085 /* make sure the sparse subtitles don't participate in the preroll */ |
865 |
1086 g_object_set (elem, "async", FALSE, NULL); |
866 /* Take a ref */ |
1087 /* we have a custom sink, this will be our textsinkpad */ |
867 playsink->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay)); |
1088 textsinkpad = gst_element_get_static_pad (chain->sink, "sink"); |
868 |
1089 if (textsinkpad) { |
869 /* we know this will succeed, as the video bin already created one before */ |
1090 /* we're all fine now and we can add the sink to the chain */ |
870 csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp"); |
1091 GST_DEBUG_OBJECT (playsink, "adding custom text sink"); |
871 |
1092 gst_bin_add (bin, chain->sink); |
872 /* Add our elements */ |
1093 } else { |
873 gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL); |
1094 GST_WARNING_OBJECT (playsink, |
874 |
1095 "can't find a sink pad on custom text sink"); |
875 /* Link */ |
1096 gst_object_unref (chain->sink); |
876 gst_element_link_pads (csp, "src", overlay, "video_sink"); |
1097 chain->sink = NULL; |
877 gst_element_link_pads (overlay, "src", vbin, "sink"); |
1098 } |
878 |
1099 /* try to set sync to true but it's no biggie when we can't */ |
879 /* Add ghost pads on the subtitle bin */ |
1100 if ((elem = |
880 pad = gst_element_get_pad (overlay, "text_sink"); |
1101 gst_play_sink_find_property_sinks (playsink, chain->sink, |
881 gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad)); |
1102 "sync"))) |
882 gst_object_unref (pad); |
1103 g_object_set (elem, "sync", TRUE, NULL); |
883 |
1104 } else { |
884 pad = gst_element_get_pad (csp, "sink"); |
1105 GST_WARNING_OBJECT (playsink, |
885 gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); |
1106 "can't find async property in custom text sink"); |
886 gst_object_unref (pad); |
1107 } |
887 |
1108 } |
888 /* Set state to READY */ |
1109 if (textsinkpad == NULL) { |
889 gst_element_set_state (element, GST_STATE_READY); |
1110 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
890 |
1111 (_("Custom text sink element is not usable.")), |
891 return element; |
1112 ("fallback to default textoverlay")); |
892 |
1113 } |
893 /* ERRORS */ |
1114 } |
894 no_overlay: |
1115 |
895 { |
1116 if (textsinkpad == NULL) { |
896 post_missing_element_message (playsink, "textoverlay"); |
1117 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { |
897 GST_WARNING_OBJECT (playsink, |
1118 /* no custom sink, try to setup the colorspace and textoverlay elements */ |
898 "No overlay (pango) element, subtitles disabled"); |
1119 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv"); |
899 return vbin; |
1120 if (chain->conv == NULL) { |
900 } |
1121 /* not really needed, it might work without colorspace */ |
901 } |
1122 post_missing_element_message (playsink, "ffmpegcolorspace"); |
902 #endif |
1123 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
903 |
1124 (_("Missing element '%s' - check your GStreamer installation."), |
|
1125 "ffmpegcolorspace"), ("subtitle rendering might fail")); |
|
1126 } else { |
|
1127 gst_bin_add (bin, chain->conv); |
|
1128 videosinkpad = gst_element_get_static_pad (chain->conv, "sink"); |
|
1129 } |
|
1130 } |
|
1131 |
|
1132 chain->overlay = gst_element_factory_make ("textoverlay", "overlay"); |
|
1133 if (chain->overlay == NULL) { |
|
1134 post_missing_element_message (playsink, "textoverlay"); |
|
1135 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1136 (_("Missing element '%s' - check your GStreamer installation."), |
|
1137 "textoverlay"), ("subtitle rendering disabled")); |
|
1138 } else { |
|
1139 gst_bin_add (bin, chain->overlay); |
|
1140 |
|
1141 /* Set some parameters */ |
|
1142 g_object_set (G_OBJECT (chain->overlay), |
|
1143 "halign", "center", "valign", "bottom", NULL); |
|
1144 if (playsink->font_desc) { |
|
1145 g_object_set (G_OBJECT (chain->overlay), "font-desc", |
|
1146 playsink->font_desc, NULL); |
|
1147 } |
|
1148 g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL); |
|
1149 |
|
1150 textsinkpad = gst_element_get_static_pad (chain->overlay, "text_sink"); |
|
1151 |
|
1152 srcpad = gst_element_get_static_pad (chain->overlay, "src"); |
|
1153 |
|
1154 if (videosinkpad) { |
|
1155 /* if we had a videosinkpad, we had a converter and we can link it, we |
|
1156 * know that this will work */ |
|
1157 gst_element_link_pads (chain->conv, "src", chain->overlay, |
|
1158 "video_sink"); |
|
1159 } else { |
|
1160 /* no videopad, expose our own video pad then */ |
|
1161 videosinkpad = |
|
1162 gst_element_get_static_pad (chain->overlay, "video_sink"); |
|
1163 } |
|
1164 } |
|
1165 } |
|
1166 |
|
1167 if (videosinkpad == NULL) { |
|
1168 /* if we still don't have a videosink, we don't have a converter nor an |
|
1169 * overlay. the only thing we can do is insert an identity and ghost the src |
|
1170 * and sink pads. */ |
|
1171 chain->conv = gst_element_factory_make ("identity", "tidentity"); |
|
1172 g_object_set (chain->conv, "signal-handoffs", FALSE, NULL); |
|
1173 g_object_set (chain->conv, "silent", TRUE, NULL); |
|
1174 gst_bin_add (bin, chain->conv); |
|
1175 srcpad = gst_element_get_static_pad (chain->conv, "src"); |
|
1176 videosinkpad = gst_element_get_static_pad (chain->conv, "sink"); |
|
1177 } else { |
|
1178 /* we have a videosink but maybe not a srcpad because there was no |
|
1179 * overlay */ |
|
1180 if (srcpad == NULL) { |
|
1181 /* ghost the source pad of the converter then */ |
|
1182 srcpad = gst_element_get_static_pad (chain->conv, "src"); |
|
1183 } |
|
1184 } |
|
1185 |
|
1186 /* expose the ghostpads */ |
|
1187 if (videosinkpad) { |
|
1188 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad); |
|
1189 gst_object_unref (videosinkpad); |
|
1190 gst_element_add_pad (chain->chain.bin, chain->videosinkpad); |
|
1191 } |
|
1192 if (textsinkpad) { |
|
1193 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad); |
|
1194 gst_object_unref (textsinkpad); |
|
1195 gst_element_add_pad (chain->chain.bin, chain->textsinkpad); |
|
1196 } |
|
1197 if (srcpad) { |
|
1198 chain->srcpad = gst_ghost_pad_new ("src", srcpad); |
|
1199 gst_object_unref (srcpad); |
|
1200 gst_element_add_pad (chain->chain.bin, chain->srcpad); |
|
1201 } |
|
1202 |
|
1203 return chain; |
|
1204 } |
|
1205 |
|
1206 /* make an element for playback of video with subpictures embedded. |
|
1207 * |
|
1208 * +--------------------------------------------------------+ |
|
1209 * | pbin +-------------+ | |
|
1210 * | +-------+ +-----+ | dvdspu | | |
|
1211 * | | queue | | csp | +---video | | |
|
1212 * sink----sink src--sink src+ +-subpicture src--+ | |
|
1213 * | +-------+ +-----+ | +-------------+ +-- src |
|
1214 * subpicture----------------------+ | |
|
1215 * +--------------------------------------------------------+ |
|
1216 */ |
|
1217 static GstPlaySubpChain * |
|
1218 gen_subp_chain (GstPlaySink * playsink) |
|
1219 { |
|
1220 GstPlaySubpChain *chain; |
|
1221 GstBin *bin; |
|
1222 GstElement *elem, *head; |
|
1223 GstPad *videosinkpad, *subpsinkpad, *srcpad; |
|
1224 |
|
1225 chain = g_new0 (GstPlaySubpChain, 1); |
|
1226 chain->chain.playsink = playsink; |
|
1227 |
|
1228 GST_DEBUG_OBJECT (playsink, "making subpicture chain %p", chain); |
|
1229 |
|
1230 chain->chain.bin = gst_bin_new ("pbin"); |
|
1231 bin = GST_BIN_CAST (chain->chain.bin); |
|
1232 gst_object_ref (bin); |
|
1233 gst_object_sink (bin); |
|
1234 |
|
1235 videosinkpad = subpsinkpad = srcpad = NULL; |
|
1236 |
|
1237 /* first try to hook the text pad to the custom sink */ |
|
1238 if (playsink->subp_sink) { |
|
1239 GST_DEBUG_OBJECT (playsink, "trying configured subpsink"); |
|
1240 chain->sink = try_element (playsink, playsink->text_sink, FALSE); |
|
1241 if (chain->sink) { |
|
1242 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async"); |
|
1243 if (elem) { |
|
1244 /* make sure the sparse subtitles don't participate in the preroll */ |
|
1245 g_object_set (elem, "async", FALSE, NULL); |
|
1246 /* we have a custom sink, this will be our subpsinkpad */ |
|
1247 subpsinkpad = gst_element_get_static_pad (chain->sink, "sink"); |
|
1248 if (subpsinkpad) { |
|
1249 /* we're all fine now and we can add the sink to the chain */ |
|
1250 GST_DEBUG_OBJECT (playsink, "adding custom text sink"); |
|
1251 gst_bin_add (bin, chain->sink); |
|
1252 } else { |
|
1253 GST_WARNING_OBJECT (playsink, |
|
1254 "can't find a sink pad on custom text sink"); |
|
1255 gst_object_unref (chain->sink); |
|
1256 chain->sink = NULL; |
|
1257 } |
|
1258 /* try to set sync to true but it's no biggie when we can't */ |
|
1259 if ((elem = |
|
1260 gst_play_sink_find_property_sinks (playsink, chain->sink, |
|
1261 "sync"))) |
|
1262 g_object_set (elem, "sync", TRUE, NULL); |
|
1263 } else { |
|
1264 GST_WARNING_OBJECT (playsink, |
|
1265 "can't find async property in custom text sink"); |
|
1266 } |
|
1267 } |
|
1268 if (subpsinkpad == NULL) { |
|
1269 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1270 (_("Custom text sink element is not usable.")), |
|
1271 ("fallback to default dvdspu overlay")); |
|
1272 } |
|
1273 } |
|
1274 |
|
1275 /* make a little queue */ |
|
1276 chain->queue = gst_element_factory_make ("queue", "vqueue"); |
|
1277 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3, |
|
1278 "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL); |
|
1279 gst_bin_add (bin, chain->queue); |
|
1280 head = chain->queue; |
|
1281 |
|
1282 /* video goes into the queue */ |
|
1283 videosinkpad = gst_element_get_static_pad (chain->queue, "sink"); |
|
1284 |
|
1285 if (subpsinkpad == NULL) { |
|
1286 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { |
|
1287 /* no custom sink, try to setup the colorspace and textoverlay elements */ |
|
1288 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv"); |
|
1289 if (chain->conv == NULL) { |
|
1290 /* not really needed, it might work without colorspace */ |
|
1291 post_missing_element_message (playsink, "ffmpegcolorspace"); |
|
1292 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1293 (_("Missing element '%s' - check your GStreamer installation."), |
|
1294 "ffmpegcolorspace"), ("subpicture rendering might fail")); |
|
1295 } else { |
|
1296 gst_bin_add (bin, chain->conv); |
|
1297 gst_element_link_pads (head, "src", chain->conv, "sink"); |
|
1298 head = chain->conv; |
|
1299 } |
|
1300 } |
|
1301 |
|
1302 chain->overlay = gst_element_factory_make ("dvdspu", "spuoverlay"); |
|
1303 if (chain->overlay == NULL) { |
|
1304 post_missing_element_message (playsink, "dvdspu"); |
|
1305 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1306 (_("Missing element '%s' - check your GStreamer installation."), |
|
1307 "dvdspu"), ("subpicture rendering disabled")); |
|
1308 } else { |
|
1309 gst_bin_add (bin, chain->overlay); |
|
1310 /* Set some parameters */ |
|
1311 subpsinkpad = gst_element_get_static_pad (chain->overlay, "subpicture"); |
|
1312 /* link to the next element */ |
|
1313 gst_element_link_pads (head, "src", chain->overlay, "video"); |
|
1314 head = chain->overlay; |
|
1315 } |
|
1316 } |
|
1317 srcpad = gst_element_get_static_pad (head, "src"); |
|
1318 chain->srcpad = gst_ghost_pad_new ("src", srcpad); |
|
1319 gst_object_unref (srcpad); |
|
1320 gst_element_add_pad (chain->chain.bin, chain->srcpad); |
|
1321 |
|
1322 /* expose the ghostpads */ |
|
1323 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad); |
|
1324 gst_object_unref (videosinkpad); |
|
1325 gst_element_add_pad (chain->chain.bin, chain->videosinkpad); |
|
1326 |
|
1327 if (subpsinkpad) { |
|
1328 chain->subpsinkpad = gst_ghost_pad_new ("subpicture", subpsinkpad); |
|
1329 gst_object_unref (subpsinkpad); |
|
1330 gst_element_add_pad (chain->chain.bin, chain->subpsinkpad); |
|
1331 } |
|
1332 return chain; |
|
1333 } |
|
1334 |
904 |
1335 /* make the chain that contains the elements needed to perform |
905 /* make the chain that contains the elements needed to perform |
1336 * audio playback. |
906 * audio playback. |
1337 * |
907 * |
1338 * We add a tee as the first element so that we can link the visualisation chain |
908 * We add a tee as the first element so that we can link the visualisation chain |
1391 /* we have to add a queue when we need to decouple for the video sink in |
948 /* we have to add a queue when we need to decouple for the video sink in |
1392 * visualisations */ |
949 * visualisations */ |
1393 GST_DEBUG_OBJECT (playsink, "adding audio queue"); |
950 GST_DEBUG_OBJECT (playsink, "adding audio queue"); |
1394 chain->queue = gst_element_factory_make ("queue", "aqueue"); |
951 chain->queue = gst_element_factory_make ("queue", "aqueue"); |
1395 gst_bin_add (bin, chain->queue); |
952 gst_bin_add (bin, chain->queue); |
1396 prev = head = 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 } |
1397 } else { |
997 } else { |
1398 head = chain->sink; |
998 if (queue) { |
1399 prev = NULL; |
999 res = gst_element_link_pads (chain->queue, "src", chain->sink, "sink"); |
1400 } |
1000 pad = gst_element_get_pad (chain->queue, "sink"); |
1401 |
|
1402 /* check if the sink, or something within the sink, has the volume property. |
|
1403 * If it does we don't need to add a volume element. */ |
|
1404 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume"); |
|
1405 if (elem) { |
|
1406 chain->volume = elem; |
|
1407 |
|
1408 GST_DEBUG_OBJECT (playsink, "the sink has a volume property"); |
|
1409 have_volume = TRUE; |
|
1410 chain->sink_volume = TRUE; |
|
1411 /* if the sink also has a mute property we can use this as well. We'll only |
|
1412 * use the mute property if there is a volume property. We can simulate the |
|
1413 * mute with the volume otherwise. */ |
|
1414 chain->mute = |
|
1415 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute"); |
|
1416 if (chain->mute) { |
|
1417 GST_DEBUG_OBJECT (playsink, "the sink has a mute property"); |
|
1418 } |
|
1419 /* use the sink to control the volume and mute */ |
|
1420 if (playsink->volume_changed) { |
|
1421 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL); |
|
1422 } |
|
1423 if (playsink->mute_changed) { |
|
1424 if (chain->mute) { |
|
1425 g_object_set (chain->mute, "mute", playsink->mute, NULL); |
|
1426 } else { |
|
1427 if (playsink->mute) |
|
1428 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL); |
|
1429 } |
|
1430 } |
|
1431 } else { |
|
1432 /* no volume, we need to add a volume element when we can */ |
|
1433 GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); |
|
1434 have_volume = FALSE; |
|
1435 chain->sink_volume = FALSE; |
|
1436 } |
|
1437 |
|
1438 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) { |
|
1439 GST_DEBUG_OBJECT (playsink, "creating audioconvert"); |
|
1440 chain->conv = gst_element_factory_make ("audioconvert", "aconv"); |
|
1441 if (chain->conv == NULL) { |
|
1442 post_missing_element_message (playsink, "audioconvert"); |
|
1443 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1444 (_("Missing element '%s' - check your GStreamer installation."), |
|
1445 "audioconvert"), ("possibly a liboil version mismatch?")); |
|
1446 } else { |
1001 } else { |
1447 gst_bin_add (bin, chain->conv); |
1002 pad = gst_element_get_pad (chain->sink, "sink"); |
1448 if (prev) { |
1003 } |
1449 if (!gst_element_link_pads (prev, "src", chain->conv, "sink")) |
1004 } |
1450 goto link_failed; |
1005 chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad); |
1451 } else { |
|
1452 head = chain->conv; |
|
1453 } |
|
1454 prev = chain->conv; |
|
1455 } |
|
1456 |
|
1457 GST_DEBUG_OBJECT (playsink, "creating audioresample"); |
|
1458 chain->resample = gst_element_factory_make ("audioresample", "aresample"); |
|
1459 if (chain->resample == NULL) { |
|
1460 post_missing_element_message (playsink, "audioresample"); |
|
1461 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1462 (_("Missing element '%s' - check your GStreamer installation."), |
|
1463 "audioresample"), ("possibly a liboil version mismatch?")); |
|
1464 } else { |
|
1465 gst_bin_add (bin, chain->resample); |
|
1466 if (prev) { |
|
1467 if (!gst_element_link_pads (prev, "src", chain->resample, "sink")) |
|
1468 goto link_failed; |
|
1469 } else { |
|
1470 head = chain->resample; |
|
1471 } |
|
1472 prev = chain->resample; |
|
1473 } |
|
1474 |
|
1475 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) { |
|
1476 GST_DEBUG_OBJECT (playsink, "creating volume"); |
|
1477 chain->volume = gst_element_factory_make ("volume", "volume"); |
|
1478 if (chain->volume == NULL) { |
|
1479 post_missing_element_message (playsink, "volume"); |
|
1480 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, |
|
1481 (_("Missing element '%s' - check your GStreamer installation."), |
|
1482 "volume"), ("possibly a liboil version mismatch?")); |
|
1483 } else { |
|
1484 have_volume = TRUE; |
|
1485 |
|
1486 /* volume also has the mute property */ |
|
1487 chain->mute = chain->volume; |
|
1488 |
|
1489 /* configure with the latest volume and mute */ |
|
1490 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, |
|
1491 NULL); |
|
1492 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL); |
|
1493 gst_bin_add (bin, chain->volume); |
|
1494 |
|
1495 if (prev) { |
|
1496 if (!gst_element_link_pads (prev, "src", chain->volume, "sink")) |
|
1497 goto link_failed; |
|
1498 } else { |
|
1499 head = chain->volume; |
|
1500 } |
|
1501 prev = chain->volume; |
|
1502 } |
|
1503 } |
|
1504 } |
|
1505 |
|
1506 if (prev) { |
|
1507 /* we only have to link to the previous element if we have something in |
|
1508 * front of the sink */ |
|
1509 GST_DEBUG_OBJECT (playsink, "linking to sink"); |
|
1510 if (!gst_element_link_pads (prev, "src", chain->sink, NULL)) |
|
1511 goto link_failed; |
|
1512 } |
|
1513 |
|
1514 /* post a warning if we have no way to configure the volume */ |
|
1515 if (!have_volume) { |
|
1516 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED, |
|
1517 (_("No volume control found")), ("Volume/mute is not available")); |
|
1518 } |
|
1519 |
|
1520 /* and ghost the sinkpad of the headmost element */ |
|
1521 GST_DEBUG_OBJECT (playsink, "ghosting sink pad"); |
|
1522 pad = gst_element_get_static_pad (head, "sink"); |
|
1523 chain->sinkpad = gst_ghost_pad_new ("sink", pad); |
|
1524 gst_object_unref (pad); |
1006 gst_object_unref (pad); |
1525 gst_element_add_pad (chain->chain.bin, chain->sinkpad); |
1007 gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad); |
1526 |
1008 |
1527 return chain; |
1009 return (GstPlayChain *) chain; |
1528 |
1010 |
1529 /* ERRORS */ |
1011 /* ERRORS */ |
1530 no_sinks: |
1012 no_sinks: |
1531 { |
1013 { |
1532 if (!elem) { |
1014 post_missing_element_message (playsink, "autoaudiosink"); |
1533 post_missing_element_message (playsink, "autoaudiosink"); |
1015 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
1534 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN, |
1016 (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); |
1535 (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); |
1017 free_chain ((GstPlayChain *) chain); |
1536 } else { |
1018 return NULL; |
1537 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE, |
1019 } |
1538 (_("Both autoaudiosink and alsasink elements are not working.")), |
1020 no_audioconvert: |
1539 (NULL)); |
1021 { |
1540 } |
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?")); |
1541 free_chain ((GstPlayChain *) chain); |
1044 free_chain ((GstPlayChain *) chain); |
1542 return NULL; |
1045 return NULL; |
1543 } |
1046 } |
1544 link_failed: |
1047 link_failed: |
1545 { |
1048 { |
1546 GST_ELEMENT_ERROR (playsink, CORE, PAD, |
1049 GST_ELEMENT_ERROR (playsink, CORE, PAD, |
1547 (NULL), ("Failed to configure the audio sink.")); |
1050 (NULL), ("Failed to configure the audio sink.")); |
1548 free_chain ((GstPlayChain *) chain); |
1051 free_chain ((GstPlayChain *) chain); |
1549 return NULL; |
1052 return NULL; |
1550 } |
1053 } |
1551 } |
|
1552 |
|
1553 static gboolean |
|
1554 setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) |
|
1555 { |
|
1556 GstElement *elem; |
|
1557 GstPlayAudioChain *chain; |
|
1558 GstStateChangeReturn ret; |
|
1559 |
|
1560 chain = playsink->audiochain; |
|
1561 |
|
1562 /* if the chain was active we don't do anything */ |
|
1563 if (GST_PLAY_CHAIN (chain)->activated == TRUE) |
|
1564 return TRUE; |
|
1565 |
|
1566 if (chain->chain.raw != raw) |
|
1567 return FALSE; |
|
1568 |
|
1569 /* try to set the sink element to READY again */ |
|
1570 ret = gst_element_set_state (chain->sink, GST_STATE_READY); |
|
1571 if (ret == GST_STATE_CHANGE_FAILURE) |
|
1572 return FALSE; |
|
1573 |
|
1574 /* check if the sink, or something within the sink, has the volume property. |
|
1575 * If it does we don't need to add a volume element. */ |
|
1576 elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume"); |
|
1577 if (elem) { |
|
1578 chain->volume = elem; |
|
1579 |
|
1580 if (playsink->volume_changed) { |
|
1581 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f", |
|
1582 playsink->volume); |
|
1583 /* use the sink to control the volume */ |
|
1584 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL); |
|
1585 } |
|
1586 /* if the sink also has a mute property we can use this as well. We'll only |
|
1587 * use the mute property if there is a volume property. We can simulate the |
|
1588 * mute with the volume otherwise. */ |
|
1589 chain->mute = |
|
1590 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute"); |
|
1591 if (chain->mute) { |
|
1592 GST_DEBUG_OBJECT (playsink, "the sink has a mute property"); |
|
1593 } |
|
1594 } else { |
|
1595 /* no volume, we need to add a volume element when we can */ |
|
1596 GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); |
|
1597 if (!raw) { |
|
1598 GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control"); |
|
1599 chain->volume = NULL; |
|
1600 chain->mute = NULL; |
|
1601 } else { |
|
1602 /* both last and current chain are raw audio, there should be a volume |
|
1603 * element already, unless the sink changed from one with a volume |
|
1604 * property to one that hasn't got a volume property, in which case we |
|
1605 * re-generate the chain */ |
|
1606 if (chain->volume == NULL) { |
|
1607 GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use"); |
|
1608 return FALSE; |
|
1609 } |
|
1610 |
|
1611 GST_DEBUG_OBJECT (playsink, "reusing existing volume element"); |
|
1612 } |
|
1613 } |
|
1614 return TRUE; |
|
1615 } |
1054 } |
1616 |
1055 |
1617 /* |
1056 /* |
1618 * +-------------------------------------------------------------------+ |
1057 * +-------------------------------------------------------------------+ |
1619 * | visbin | |
1058 * | visbin | |
1731 free_chain ((GstPlayChain *) chain); |
1162 free_chain ((GstPlayChain *) chain); |
1732 return NULL; |
1163 return NULL; |
1733 } |
1164 } |
1734 } |
1165 } |
1735 |
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 |
1736 /* this function is called when all the request pads are requested and when we |
1192 /* this function is called when all the request pads are requested and when we |
1737 * have to construct the final pipeline. Based on the flags we construct the |
1193 * have to construct the final pipeline. |
1738 * final output pipelines. |
|
1739 */ |
1194 */ |
1740 #ifdef __SYMBIAN32__ |
1195 #ifdef __SYMBIAN32__ |
1741 EXPORT_C |
1196 EXPORT_C |
1742 #endif |
1197 #endif |
1743 |
1198 |
1744 gboolean |
1199 gboolean |
1745 gst_play_sink_reconfigure (GstPlaySink * playsink) |
1200 gst_play_sink_reconfigure (GstPlaySink * playsink) |
1746 { |
1201 { |
1747 GstPlayFlags flags; |
1202 GstPlayFlags flags; |
1748 gboolean need_audio, need_video, need_vis, need_text, need_subp; |
1203 gboolean need_audio, need_video, need_vis; |
1749 |
1204 |
1750 GST_DEBUG_OBJECT (playsink, "reconfiguring"); |
1205 GST_DEBUG_OBJECT (playsink, "reconfiguring"); |
1751 |
1206 |
1752 /* assume we need nothing */ |
1207 /* assume we need nothing */ |
1753 need_audio = need_video = need_vis = need_text = need_subp = FALSE; |
1208 need_audio = need_video = need_vis = FALSE; |
1754 |
1209 |
1755 GST_PLAY_SINK_LOCK (playsink); |
1210 GST_PLAY_SINK_LOCK (playsink); |
1756 GST_OBJECT_LOCK (playsink); |
1211 GST_OBJECT_LOCK (playsink); |
1757 /* get flags, there are protected with the object lock */ |
1212 /* get flags, there are protected with the object lock */ |
1758 flags = playsink->flags; |
1213 flags = playsink->flags; |
1759 GST_OBJECT_UNLOCK (playsink); |
1214 GST_OBJECT_UNLOCK (playsink); |
1760 |
1215 |
1761 /* figure out which components we need */ |
1216 /* figure out which components we need */ |
1762 if (flags & GST_PLAY_FLAG_TEXT && (playsink->text_pad || playsink->subp_pad)) { |
1217 if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) { |
1763 /* we have a text_pad and we need text rendering, in this case we need a |
|
1764 * video_pad to combine the video with the text */ |
|
1765 if (!playsink->video_pad) |
|
1766 goto subs_but_no_video; |
|
1767 |
|
1768 /* we have subtitles and we are requested to show it, we also need to show |
|
1769 * video in this case. */ |
|
1770 need_video = TRUE; |
|
1771 need_text = (playsink->text_pad != NULL); |
|
1772 need_subp = (playsink->subp_pad != NULL); |
|
1773 |
|
1774 /* we can't handle both of them yet */ |
|
1775 if (need_text && need_subp) |
|
1776 goto subs_and_text; |
|
1777 } else if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) { |
|
1778 /* we have video and we are requested to show it */ |
1218 /* we have video and we are requested to show it */ |
1779 need_video = TRUE; |
1219 need_video = TRUE; |
1780 } |
1220 } |
1781 if (playsink->audio_pad) { |
1221 if (playsink->audio_pad) { |
1782 if (flags & GST_PLAY_FLAG_AUDIO) { |
1222 if (flags & GST_PLAY_FLAG_AUDIO) { |
1783 need_audio = TRUE; |
1223 need_audio = TRUE; |
1784 } |
1224 } |
1785 if (playsink->audio_pad_raw) { |
1225 if (flags & GST_PLAY_FLAG_VIS && !need_video) { |
1786 /* only can do vis with raw uncompressed audio */ |
1226 /* also add video when we add visualisation */ |
1787 if (flags & GST_PLAY_FLAG_VIS && !need_video) { |
1227 need_video = TRUE; |
1788 /* also add video when we add visualisation */ |
1228 need_vis = TRUE; |
1789 need_video = TRUE; |
1229 } |
1790 need_vis = TRUE; |
1230 } |
1791 } |
1231 |
1792 } |
|
1793 } |
|
1794 |
|
1795 /* set up video pipeline */ |
|
1796 if (need_video) { |
1232 if (need_video) { |
1797 gboolean raw, async, queue; |
|
1798 |
|
1799 /* we need a raw sink when we do vis or when we have a raw pad */ |
|
1800 raw = need_vis ? TRUE : playsink->video_pad_raw; |
|
1801 /* we try to set the sink async=FALSE when we need vis, this way we can |
|
1802 * avoid a queue in the audio chain. */ |
|
1803 async = !need_vis; |
|
1804 /* put a little queue in front of the video but only if we are not doing |
|
1805 * subpictures because then we will add the queue in front of the subpicture |
|
1806 * mixer to minimize latency. */ |
|
1807 queue = (need_subp == FALSE); |
|
1808 |
|
1809 GST_DEBUG_OBJECT (playsink, "adding video, raw %d", |
1233 GST_DEBUG_OBJECT (playsink, "adding video, raw %d", |
1810 playsink->video_pad_raw); |
1234 playsink->video_pad_raw); |
1811 |
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 { |
1812 if (playsink->videochain) { |
1252 if (playsink->videochain) { |
1813 /* try to reactivate the chain */ |
1253 add_chain (playsink->videochain, FALSE); |
1814 if (!setup_video_chain (playsink, raw, async, queue)) { |
1254 activate_chain (playsink->videochain, FALSE); |
1815 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
|
1816 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
|
1817 free_chain ((GstPlayChain *) playsink->videochain); |
|
1818 playsink->videochain = NULL; |
|
1819 } |
|
1820 } |
|
1821 |
|
1822 if (!playsink->videochain) { |
|
1823 playsink->videochain = gen_video_chain (playsink, raw, async, queue); |
|
1824 } |
|
1825 if (playsink->videochain) { |
|
1826 GST_DEBUG_OBJECT (playsink, "adding video chain"); |
|
1827 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE); |
|
1828 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE); |
|
1829 /* if we are not part of vis or subtitles, set the ghostpad target */ |
|
1830 if (!need_vis && !need_text && playsink->text_pad == NULL) { |
|
1831 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad"); |
|
1832 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), |
|
1833 playsink->videochain->sinkpad); |
|
1834 } |
|
1835 } |
|
1836 } else { |
|
1837 GST_DEBUG_OBJECT (playsink, "no video needed"); |
|
1838 if (playsink->videochain) { |
|
1839 GST_DEBUG_OBJECT (playsink, "removing video chain"); |
|
1840 if (playsink->vischain) { |
|
1841 GstPad *srcpad; |
|
1842 |
|
1843 GST_DEBUG_OBJECT (playsink, "unlinking vis chain"); |
|
1844 |
|
1845 /* also had visualisation, release the tee srcpad before we then |
|
1846 * unlink the video from it */ |
|
1847 if (playsink->audio_tee_vissrc) { |
|
1848 gst_element_release_request_pad (playsink->audio_tee, |
|
1849 playsink->audio_tee_vissrc); |
|
1850 gst_object_unref (playsink->audio_tee_vissrc); |
|
1851 playsink->audio_tee_vissrc = NULL; |
|
1852 } |
|
1853 srcpad = |
|
1854 gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); |
|
1855 gst_pad_unlink (srcpad, playsink->videochain->sinkpad); |
|
1856 } |
|
1857 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
|
1858 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
|
1859 } |
1255 } |
1860 if (playsink->video_pad) |
1256 if (playsink->video_pad) |
1861 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); |
1257 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); |
1862 } |
1258 } |
1863 |
1259 |
1864 if (need_text) { |
1260 if (need_audio) { |
1865 GST_DEBUG_OBJECT (playsink, "adding text"); |
1261 GST_DEBUG_OBJECT (playsink, "adding audio"); |
1866 if (!playsink->textchain) { |
1262 if (!playsink->audiochain) { |
1867 GST_DEBUG_OBJECT (playsink, "creating text chain"); |
1263 gboolean raw, queue; |
1868 playsink->textchain = gen_text_chain (playsink); |
1264 |
1869 } |
1265 /* get a raw sink if we are asked for a raw pad */ |
1870 if (playsink->textchain) { |
1266 raw = playsink->audio_pad_raw; |
1871 GST_DEBUG_OBJECT (playsink, "adding text chain"); |
1267 if (need_vis) { |
1872 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE); |
1268 /* If we are dealing with visualisations, we need to add a queue to |
1873 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), |
1269 * decouple the audio from the video part. We only have to do this when |
1874 playsink->textchain->textsinkpad); |
1270 * the video part is async=true */ |
1875 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), |
1271 queue = ((GstPlayVideoChain *) playsink->videochain)->async; |
1876 playsink->textchain->videosinkpad); |
1272 GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue); |
1877 gst_pad_link (playsink->textchain->srcpad, playsink->videochain->sinkpad); |
1273 } else { |
1878 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE); |
1274 /* no vis, we can avoid a queue */ |
1879 if (playsink->textchain->overlay) |
1275 GST_DEBUG_OBJECT (playsink, "don't need audio queue"); |
1880 g_object_set (playsink->textchain->overlay, "silent", FALSE, NULL); |
1276 queue = FALSE; |
1881 } |
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); |
1882 } else { |
1284 } else { |
1883 GST_DEBUG_OBJECT (playsink, "no text needed"); |
|
1884 /* we have no subtitles/text or we are requested to not show them */ |
|
1885 if (playsink->textchain) { |
|
1886 if (playsink->text_pad == NULL) { |
|
1887 /* no text pad, remove the chain entirely */ |
|
1888 GST_DEBUG_OBJECT (playsink, "removing text chain"); |
|
1889 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); |
|
1890 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); |
|
1891 } else { |
|
1892 /* we have a chain and a textpad, turn the subtitles off */ |
|
1893 GST_DEBUG_OBJECT (playsink, "turning off the text"); |
|
1894 if (playsink->textchain->overlay) |
|
1895 g_object_set (playsink->textchain->overlay, "silent", TRUE, NULL); |
|
1896 } |
|
1897 } |
|
1898 if (!need_video && playsink->video_pad) |
|
1899 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); |
|
1900 if (playsink->text_pad) |
|
1901 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL); |
|
1902 } |
|
1903 |
|
1904 if (need_subp && playsink->videochain) { |
|
1905 GST_DEBUG_OBJECT (playsink, "adding subpicture"); |
|
1906 if (!playsink->subpchain) { |
|
1907 GST_DEBUG_OBJECT (playsink, "creating subpicture chain"); |
|
1908 playsink->subpchain = gen_subp_chain (playsink); |
|
1909 } |
|
1910 if (playsink->subpchain) { |
|
1911 GST_DEBUG_OBJECT (playsink, "adding subp chain"); |
|
1912 add_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE); |
|
1913 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad), |
|
1914 playsink->subpchain->subpsinkpad); |
|
1915 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), |
|
1916 playsink->subpchain->videosinkpad); |
|
1917 gst_pad_link (playsink->subpchain->srcpad, playsink->videochain->sinkpad); |
|
1918 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE); |
|
1919 } |
|
1920 } else { |
|
1921 GST_DEBUG_OBJECT (playsink, "no subpicture needed"); |
|
1922 /* we have no subpicture or we are requested to not show them */ |
|
1923 if (playsink->subpchain) { |
|
1924 if (playsink->subp_pad == NULL) { |
|
1925 /* no subpicture pad, remove the chain entirely */ |
|
1926 GST_DEBUG_OBJECT (playsink, "removing subp chain"); |
|
1927 add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE); |
|
1928 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE); |
|
1929 } else { |
|
1930 /* we have a chain and a subpicture pad, turn the subtitles off */ |
|
1931 GST_DEBUG_OBJECT (playsink, "turning off the subp"); |
|
1932 } |
|
1933 } |
|
1934 if (!need_video && playsink->video_pad) |
|
1935 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL); |
|
1936 if (playsink->subp_pad) |
|
1937 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad), NULL); |
|
1938 } |
|
1939 |
|
1940 if (need_audio) { |
|
1941 gboolean raw, queue; |
|
1942 |
|
1943 GST_DEBUG_OBJECT (playsink, "adding audio"); |
|
1944 |
|
1945 /* get a raw sink if we are asked for a raw pad */ |
|
1946 raw = playsink->audio_pad_raw; |
|
1947 if (need_vis && playsink->videochain) { |
|
1948 /* If we are dealing with visualisations, we need to add a queue to |
|
1949 * decouple the audio from the video part. We only have to do this when |
|
1950 * the video part is async=true */ |
|
1951 queue = ((GstPlayVideoChain *) playsink->videochain)->async; |
|
1952 GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue); |
|
1953 } else { |
|
1954 /* no vis, we can avoid a queue */ |
|
1955 GST_DEBUG_OBJECT (playsink, "don't need audio queue"); |
|
1956 queue = FALSE; |
|
1957 } |
|
1958 |
|
1959 if (playsink->audiochain) { |
|
1960 /* try to reactivate the chain */ |
|
1961 if (!setup_audio_chain (playsink, raw, queue)) { |
|
1962 GST_DEBUG_OBJECT (playsink, "removing current audio chain"); |
|
1963 if (playsink->audio_tee_asrc) { |
|
1964 gst_element_release_request_pad (playsink->audio_tee, |
|
1965 playsink->audio_tee_asrc); |
|
1966 gst_object_unref (playsink->audio_tee_asrc); |
|
1967 playsink->audio_tee_asrc = NULL; |
|
1968 } |
|
1969 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
|
1970 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
|
1971 playsink->audiochain->volume = NULL; |
|
1972 playsink->audiochain->mute = NULL; |
|
1973 free_chain ((GstPlayChain *) playsink->audiochain); |
|
1974 playsink->audiochain = NULL; |
|
1975 playsink->volume_changed = playsink->mute_changed = FALSE; |
|
1976 } |
|
1977 } |
|
1978 |
|
1979 if (!playsink->audiochain) { |
|
1980 GST_DEBUG_OBJECT (playsink, "creating new audio chain"); |
|
1981 playsink->audiochain = gen_audio_chain (playsink, raw, queue); |
|
1982 } |
|
1983 |
|
1984 if (playsink->audiochain) { |
|
1985 GST_DEBUG_OBJECT (playsink, "adding audio chain"); |
|
1986 if (playsink->audio_tee_asrc == NULL) { |
|
1987 playsink->audio_tee_asrc = |
|
1988 gst_element_get_request_pad (playsink->audio_tee, "src%d"); |
|
1989 } |
|
1990 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE); |
|
1991 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE); |
|
1992 gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad); |
|
1993 } |
|
1994 } else { |
|
1995 GST_DEBUG_OBJECT (playsink, "no audio needed"); |
|
1996 /* we have no audio or we are requested to not play audio */ |
1285 /* we have no audio or we are requested to not play audio */ |
1997 if (playsink->audiochain) { |
1286 if (playsink->audiochain) { |
1998 GST_DEBUG_OBJECT (playsink, "removing audio chain"); |
1287 gst_pad_unlink (playsink->audio_tee_asrc, playsink->audiochain->sinkpad); |
1999 /* release the audio pad */ |
1288 add_chain (playsink->audiochain, FALSE); |
2000 if (playsink->audio_tee_asrc) { |
1289 activate_chain (playsink->audiochain, FALSE); |
2001 gst_element_release_request_pad (playsink->audio_tee, |
|
2002 playsink->audio_tee_asrc); |
|
2003 gst_object_unref (playsink->audio_tee_asrc); |
|
2004 playsink->audio_tee_asrc = NULL; |
|
2005 } |
|
2006 if (playsink->audiochain->sink_volume) { |
|
2007 playsink->audiochain->volume = NULL; |
|
2008 playsink->audiochain->mute = NULL; |
|
2009 } |
|
2010 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
|
2011 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
|
2012 } |
1290 } |
2013 } |
1291 } |
2014 |
1292 |
2015 if (need_vis) { |
1293 if (need_vis) { |
2016 GstPad *srcpad; |
1294 GstPad *srcpad; |
2018 if (!playsink->vischain) |
1296 if (!playsink->vischain) |
2019 playsink->vischain = gen_vis_chain (playsink); |
1297 playsink->vischain = gen_vis_chain (playsink); |
2020 |
1298 |
2021 GST_DEBUG_OBJECT (playsink, "adding visualisation"); |
1299 GST_DEBUG_OBJECT (playsink, "adding visualisation"); |
2022 |
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 { |
2023 if (playsink->vischain) { |
1309 if (playsink->vischain) { |
2024 GST_DEBUG_OBJECT (playsink, "setting up vis chain"); |
1310 add_chain (playsink->vischain, FALSE); |
2025 srcpad = |
1311 activate_chain (playsink->vischain, FALSE); |
2026 gst_element_get_static_pad (playsink->vischain->chain.bin, "src"); |
1312 } |
2027 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE); |
1313 } |
2028 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE); |
|
2029 if (playsink->audio_tee_vissrc == NULL) { |
|
2030 playsink->audio_tee_vissrc = |
|
2031 gst_element_get_request_pad (playsink->audio_tee, "src%d"); |
|
2032 } |
|
2033 gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad); |
|
2034 gst_pad_link (srcpad, playsink->videochain->sinkpad); |
|
2035 gst_object_unref (srcpad); |
|
2036 } |
|
2037 } else { |
|
2038 GST_DEBUG_OBJECT (playsink, "no vis needed"); |
|
2039 if (playsink->vischain) { |
|
2040 if (playsink->audio_tee_vissrc) { |
|
2041 gst_element_release_request_pad (playsink->audio_tee, |
|
2042 playsink->audio_tee_vissrc); |
|
2043 gst_object_unref (playsink->audio_tee_vissrc); |
|
2044 playsink->audio_tee_vissrc = NULL; |
|
2045 } |
|
2046 GST_DEBUG_OBJECT (playsink, "removing vis chain"); |
|
2047 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); |
|
2048 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); |
|
2049 } |
|
2050 } |
|
2051 do_async_done (playsink); |
|
2052 GST_PLAY_SINK_UNLOCK (playsink); |
1314 GST_PLAY_SINK_UNLOCK (playsink); |
2053 |
1315 |
2054 return TRUE; |
1316 return TRUE; |
2055 |
1317 } |
2056 /* ERRORS */ |
|
2057 subs_but_no_video: |
|
2058 { |
|
2059 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT, |
|
2060 (_("Can't play a text file without video.")), |
|
2061 ("Have text pad but no video pad")); |
|
2062 GST_PLAY_SINK_UNLOCK (playsink); |
|
2063 return FALSE; |
|
2064 } |
|
2065 subs_and_text: |
|
2066 { |
|
2067 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT, |
|
2068 (_("Can't play a text subtitles and subpictures.")), |
|
2069 ("Have text pad and subpicture pad")); |
|
2070 GST_PLAY_SINK_UNLOCK (playsink); |
|
2071 return FALSE; |
|
2072 } |
|
2073 } |
|
2074 |
|
2075 /** |
|
2076 * gst_play_sink_set_flags: |
|
2077 * @playsink: a #GstPlaySink |
|
2078 * @flags: #GstPlayFlags |
|
2079 * |
|
2080 * Configure @flags on @playsink. The flags control the behaviour of @playsink |
|
2081 * when constructing the sink pipelins. |
|
2082 * |
|
2083 * Returns: TRUE if the flags could be configured. |
|
2084 */ |
|
2085 #ifdef __SYMBIAN32__ |
1318 #ifdef __SYMBIAN32__ |
2086 EXPORT_C |
1319 EXPORT_C |
2087 #endif |
1320 #endif |
|
1321 |
2088 |
1322 |
2089 gboolean |
1323 gboolean |
2090 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags) |
1324 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags) |
2091 { |
1325 { |
2092 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE); |
1326 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE); |
2445 static gboolean |
1518 static gboolean |
2446 gst_play_sink_send_event (GstElement * element, GstEvent * event) |
1519 gst_play_sink_send_event (GstElement * element, GstEvent * event) |
2447 { |
1520 { |
2448 gboolean res = FALSE; |
1521 gboolean res = FALSE; |
2449 GstEventType event_type = GST_EVENT_TYPE (event); |
1522 GstEventType event_type = GST_EVENT_TYPE (event); |
2450 GstPlaySink *playsink; |
|
2451 |
|
2452 playsink = GST_PLAY_SINK_CAST (element); |
|
2453 |
1523 |
2454 switch (event_type) { |
1524 switch (event_type) { |
2455 case GST_EVENT_SEEK: |
1525 case GST_EVENT_SEEK: |
2456 GST_DEBUG_OBJECT (element, "Sending event to a sink"); |
1526 GST_DEBUG_OBJECT (element, "Sending seek event to a sink"); |
2457 res = gst_play_sink_send_event_to_sink (playsink, event); |
1527 res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event); |
2458 break; |
1528 break; |
2459 case GST_EVENT_STEP: |
|
2460 { |
|
2461 GstFormat format; |
|
2462 guint64 amount; |
|
2463 gdouble rate; |
|
2464 gboolean flush, intermediate; |
|
2465 |
|
2466 gst_event_parse_step (event, &format, &amount, &rate, &flush, |
|
2467 &intermediate); |
|
2468 |
|
2469 if (format == GST_FORMAT_BUFFERS) { |
|
2470 /* for buffers, we will try to step video frames, for other formats we |
|
2471 * send the step to all sinks */ |
|
2472 res = gst_play_sink_send_event_to_sink (playsink, event); |
|
2473 } else { |
|
2474 res = |
|
2475 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element, |
|
2476 event); |
|
2477 } |
|
2478 break; |
|
2479 } |
|
2480 default: |
1529 default: |
2481 res = |
1530 res = parent_class->send_event (element, event); |
2482 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element, |
|
2483 event); |
|
2484 break; |
1531 break; |
2485 } |
1532 } |
2486 return res; |
1533 return res; |
2487 } |
1534 } |
2488 |
1535 |
2489 static GstStateChangeReturn |
1536 static GstStateChangeReturn |
2490 gst_play_sink_change_state (GstElement * element, GstStateChange transition) |
1537 gst_play_sink_change_state (GstElement * element, GstStateChange transition) |
2491 { |
1538 { |
2492 GstStateChangeReturn ret; |
1539 GstStateChangeReturn ret; |
2493 GstStateChangeReturn bret; |
|
2494 |
|
2495 GstPlaySink *playsink; |
1540 GstPlaySink *playsink; |
2496 |
1541 |
2497 playsink = GST_PLAY_SINK (element); |
1542 playsink = GST_PLAY_SINK (element); |
2498 |
1543 |
2499 switch (transition) { |
1544 switch (transition) { |
2500 case GST_STATE_CHANGE_READY_TO_PAUSED: |
1545 case GST_STATE_CHANGE_READY_TO_PAUSED: |
2501 /* we want to go async to PAUSED until we managed to configure and add the |
|
2502 * sinks */ |
|
2503 do_async_start (playsink); |
|
2504 ret = GST_STATE_CHANGE_ASYNC; |
|
2505 break; |
|
2506 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
2507 case GST_STATE_CHANGE_READY_TO_NULL: |
|
2508 if (playsink->audiochain && playsink->audiochain->sink_volume) { |
|
2509 /* remove our links to the mute and volume elements when they were |
|
2510 * provided by a sink */ |
|
2511 playsink->audiochain->volume = NULL; |
|
2512 playsink->audiochain->mute = NULL; |
|
2513 } |
|
2514 ret = GST_STATE_CHANGE_SUCCESS; |
|
2515 break; |
1546 break; |
2516 default: |
1547 default: |
2517 /* all other state changes return SUCCESS by default, this value can be |
1548 break; |
2518 * overridden by the result of the children */ |
1549 } |
2519 ret = GST_STATE_CHANGE_SUCCESS; |
1550 |
2520 break; |
1551 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
2521 } |
1552 if (ret == GST_STATE_CHANGE_FAILURE) |
2522 |
1553 return ret; |
2523 /* do the state change of the children */ |
|
2524 bret = |
|
2525 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element, |
|
2526 transition); |
|
2527 /* now look at the result of our children and adjust the return value */ |
|
2528 switch (bret) { |
|
2529 case GST_STATE_CHANGE_FAILURE: |
|
2530 /* failure, we stop */ |
|
2531 goto activate_failed; |
|
2532 case GST_STATE_CHANGE_NO_PREROLL: |
|
2533 /* some child returned NO_PREROLL. This is strange but we never know. We |
|
2534 * commit our async state change (if any) and return the NO_PREROLL */ |
|
2535 do_async_done (playsink); |
|
2536 ret = bret; |
|
2537 break; |
|
2538 case GST_STATE_CHANGE_ASYNC: |
|
2539 /* some child was async, return this */ |
|
2540 ret = bret; |
|
2541 break; |
|
2542 default: |
|
2543 /* return our previously configured return value */ |
|
2544 break; |
|
2545 } |
|
2546 |
1554 |
2547 switch (transition) { |
1555 switch (transition) { |
2548 case GST_STATE_CHANGE_READY_TO_PAUSED: |
1556 case GST_STATE_CHANGE_READY_TO_PAUSED: |
2549 break; |
1557 break; |
2550 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
1558 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
2551 /* FIXME Release audio device when we implement that */ |
1559 /* FIXME Release audio device when we implement that */ |
2552 playsink->need_async_start = TRUE; |
|
2553 break; |
1560 break; |
2554 case GST_STATE_CHANGE_PAUSED_TO_READY: |
1561 case GST_STATE_CHANGE_PAUSED_TO_READY: |
2555 case GST_STATE_CHANGE_READY_TO_NULL: |
|
2556 /* remove sinks we added */ |
1562 /* remove sinks we added */ |
2557 if (playsink->videochain) { |
1563 if (playsink->videochain) { |
2558 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
1564 activate_chain (playsink->videochain, FALSE); |
2559 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE); |
1565 add_chain (playsink->videochain, FALSE); |
2560 } |
1566 } |
2561 if (playsink->audiochain) { |
1567 if (playsink->audiochain) { |
2562 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
1568 activate_chain (playsink->audiochain, FALSE); |
2563 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE); |
1569 add_chain (playsink->audiochain, FALSE); |
2564 } |
1570 } |
2565 if (playsink->vischain) { |
|
2566 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); |
|
2567 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE); |
|
2568 } |
|
2569 if (playsink->textchain) { |
|
2570 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); |
|
2571 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); |
|
2572 } |
|
2573 if (playsink->subpchain) { |
|
2574 activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE); |
|
2575 add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE); |
|
2576 } |
|
2577 do_async_done (playsink); |
|
2578 break; |
1571 break; |
2579 default: |
1572 default: |
2580 break; |
1573 break; |
2581 } |
1574 } |
|
1575 |
2582 return ret; |
1576 return ret; |
2583 |
1577 } |
2584 /* ERRORS */ |
|
2585 activate_failed: |
|
2586 { |
|
2587 GST_DEBUG_OBJECT (element, |
|
2588 "element failed to change states -- activation problem?"); |
|
2589 return GST_STATE_CHANGE_FAILURE; |
|
2590 } |
|
2591 } |
|