playbin2: Send flush events when changing subtitle tracks and use new input-selector...
[platform/upstream/gstreamer.git] / gst / playback / gstsubtitleoverlay.c
1 /*
2  * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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 /**
21  * SECTION:element-subtitleoverlay
22  *
23  * #GstBin that auto-magically overlays a video stream with subtitles by
24  * autoplugging the required elements.
25  *
26  * It supports raw, timestamped text, different textual subtitle formats and
27  * DVD subpicture subtitles.
28  *
29  * <refsect2>
30  * <title>Examples</title>
31  * |[
32  * gst-launch -v filesrc location=test.mkv ! matroskademux name=demux ! "video/x-h264" ! queue2 ! decodebin ! subtitleoverlay name=overlay ! videoconvert ! autovideosink  demux. ! "video/x-dvd-subpicture" ! queue2 ! overlay.
33  * ]| This will play back the given Matroska file with h264 video and subpicture subtitles.
34  * </refsect2>
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include "gstsubtitleoverlay.h"
42
43 #include <gst/pbutils/missing-plugins.h>
44 #include <gst/video/video.h>
45 #include <string.h>
46
47 #include "gst/glib-compat-private.h"
48
49 GST_DEBUG_CATEGORY_STATIC (subtitle_overlay_debug);
50 #define GST_CAT_DEFAULT subtitle_overlay_debug
51
52 #define IS_SUBTITLE_CHAIN_IGNORE_ERROR(flow) \
53   G_UNLIKELY (flow == GST_FLOW_ERROR || flow == GST_FLOW_NOT_NEGOTIATED)
54
55 #define IS_VIDEO_CHAIN_IGNORE_ERROR(flow) \
56   G_UNLIKELY (flow == GST_FLOW_ERROR)
57
58 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS_ANY);
62
63 static GstStaticPadTemplate video_sinktemplate =
64 GST_STATIC_PAD_TEMPLATE ("video_sink",
65     GST_PAD_SINK,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS_ANY);
68
69 static GstStaticPadTemplate subtitle_sinktemplate =
70 GST_STATIC_PAD_TEMPLATE ("subtitle_sink",
71     GST_PAD_SINK,
72     GST_PAD_ALWAYS,
73     GST_STATIC_CAPS_ANY);
74
75 enum
76 {
77   PROP_0,
78   PROP_SILENT,
79   PROP_FONT_DESC,
80   PROP_SUBTITLE_ENCODING
81 };
82
83 #define gst_subtitle_overlay_parent_class parent_class
84 G_DEFINE_TYPE (GstSubtitleOverlay, gst_subtitle_overlay, GST_TYPE_BIN);
85
86 static GQuark _subtitle_overlay_event_marker_id = 0;
87
88 static void
89 do_async_start (GstSubtitleOverlay * self)
90 {
91   if (!self->do_async) {
92     GstMessage *msg = gst_message_new_async_start (GST_OBJECT_CAST (self));
93
94     GST_DEBUG_OBJECT (self, "Posting async-start");
95     GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (self), msg);
96     self->do_async = TRUE;
97   }
98 }
99
100 static void
101 do_async_done (GstSubtitleOverlay * self)
102 {
103   if (self->do_async) {
104     GstMessage *msg =
105         gst_message_new_async_done (GST_OBJECT_CAST (self), FALSE);
106
107     GST_DEBUG_OBJECT (self, "Posting async-done");
108     GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (self), msg);
109     self->do_async = FALSE;
110   }
111 }
112
113 static GstPadProbeReturn
114 _pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data);
115
116 static void
117 block_video (GstSubtitleOverlay * self)
118 {
119   if (self->video_block_id != 0)
120     return;
121
122   if (self->video_block_pad) {
123     self->video_block_id =
124         gst_pad_add_probe (self->video_block_pad,
125         GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, _pad_blocked_cb, self, NULL);
126   }
127 }
128
129 static void
130 unblock_video (GstSubtitleOverlay * self)
131 {
132   if (self->video_block_id) {
133     gst_pad_remove_probe (self->video_block_pad, self->video_block_id);
134     self->video_sink_blocked = FALSE;
135     self->video_block_id = 0;
136   }
137 }
138
139 static void
140 block_subtitle (GstSubtitleOverlay * self)
141 {
142   if (self->subtitle_block_id != 0)
143     return;
144
145   if (self->subtitle_block_pad) {
146     self->subtitle_block_id =
147         gst_pad_add_probe (self->subtitle_block_pad,
148         GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, _pad_blocked_cb, self, NULL);
149   }
150 }
151
152 static void
153 unblock_subtitle (GstSubtitleOverlay * self)
154 {
155   if (self->subtitle_block_id) {
156     gst_pad_remove_probe (self->subtitle_block_pad, self->subtitle_block_id);
157     self->subtitle_sink_blocked = FALSE;
158     self->subtitle_block_id = 0;
159   }
160 }
161
162 static void
163 gst_subtitle_overlay_finalize (GObject * object)
164 {
165   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (object);
166
167   if (self->lock) {
168     g_mutex_free (self->lock);
169     self->lock = NULL;
170   }
171
172   if (self->factories_lock) {
173     g_mutex_free (self->factories_lock);
174     self->factories_lock = NULL;
175   }
176
177   if (self->factories)
178     gst_plugin_feature_list_free (self->factories);
179   self->factories = NULL;
180   gst_caps_replace (&self->factory_caps, NULL);
181
182   if (self->font_desc) {
183     g_free (self->font_desc);
184     self->font_desc = NULL;
185   }
186
187   if (self->encoding) {
188     g_free (self->encoding);
189     self->encoding = NULL;
190   }
191
192   G_OBJECT_CLASS (parent_class)->finalize (object);
193 }
194
195 static gboolean
196 _is_renderer (GstElementFactory * factory)
197 {
198   const gchar *klass, *name;
199
200   klass = gst_element_factory_get_klass (factory);
201   name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST (factory));
202
203   if (strstr (klass, "Overlay/Subtitle") != NULL ||
204       strstr (klass, "Overlay/SubPicture") != NULL)
205     return TRUE;
206   if (strcmp (name, "textoverlay") == 0)
207     return TRUE;
208   return FALSE;
209 }
210
211 static gboolean
212 _is_parser (GstElementFactory * factory)
213 {
214   const gchar *klass;
215
216   klass = gst_element_factory_get_klass (factory);
217
218   if (strstr (klass, "Parser/Subtitle") != NULL)
219     return TRUE;
220   return FALSE;
221 }
222
223 static const gchar *const _sub_pad_names[] = { "subpicture", "subpicture_sink",
224   "text", "text_sink",
225   "subtitle_sink", "subtitle"
226 };
227
228 static inline gboolean
229 _is_raw_video (GstStructure * s)
230 {
231   const gchar *name;
232
233   name = gst_structure_get_name (s);
234
235   if (g_str_equal (name, "video/x-raw"))
236     return TRUE;
237   return FALSE;
238 }
239
240 static gboolean
241 _is_video_pad (GstPad * pad, gboolean * hw_accelerated)
242 {
243   GstPad *peer = gst_pad_get_peer (pad);
244   GstCaps *caps;
245   gboolean ret;
246   const gchar *name;
247
248   if (peer) {
249     caps = gst_pad_get_current_caps (peer);
250     if (!caps) {
251       caps = gst_pad_query_caps (peer, NULL);
252     }
253     gst_object_unref (peer);
254   } else {
255     caps = gst_pad_query_caps (pad, NULL);
256   }
257
258   name = gst_structure_get_name (gst_caps_get_structure (caps, 0));
259   if (g_str_equal (name, "video/x-raw")) {
260     ret = TRUE;
261     if (hw_accelerated)
262       *hw_accelerated = FALSE;
263
264   } else if (g_str_has_prefix (name, "video/x-surface")) {
265     ret = TRUE;
266     if (hw_accelerated)
267       *hw_accelerated = TRUE;
268   } else {
269
270     ret = FALSE;
271     if (hw_accelerated)
272       *hw_accelerated = FALSE;
273   }
274
275   gst_caps_unref (caps);
276
277   return ret;
278 }
279
280 static GstCaps *
281 _get_sub_caps (GstElementFactory * factory)
282 {
283   const GList *templates;
284   GList *walk;
285   gboolean is_parser = _is_parser (factory);
286
287   templates = gst_element_factory_get_static_pad_templates (factory);
288   for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
289     GstStaticPadTemplate *templ = walk->data;
290
291     if (templ->direction == GST_PAD_SINK && templ->presence == GST_PAD_ALWAYS) {
292       gboolean found = FALSE;
293
294       if (is_parser) {
295         found = TRUE;
296       } else {
297         guint i;
298
299         for (i = 0; i < G_N_ELEMENTS (_sub_pad_names); i++) {
300           if (strcmp (templ->name_template, _sub_pad_names[i]) == 0) {
301             found = TRUE;
302             break;
303           }
304         }
305       }
306       if (found)
307         return gst_static_caps_get (&templ->static_caps);
308     }
309   }
310   return NULL;
311 }
312
313 static gboolean
314 _factory_filter (GstPluginFeature * feature, GstCaps ** subcaps)
315 {
316   GstElementFactory *factory;
317   guint rank;
318   const gchar *name;
319   const GList *templates;
320   GList *walk;
321   gboolean is_renderer;
322   GstCaps *templ_caps = NULL;
323   gboolean have_video_sink = FALSE;
324
325   /* we only care about element factories */
326   if (!GST_IS_ELEMENT_FACTORY (feature))
327     return FALSE;
328
329   factory = GST_ELEMENT_FACTORY_CAST (feature);
330
331   /* only select elements with autoplugging rank or textoverlay */
332   name = gst_plugin_feature_get_name (feature);
333   rank = gst_plugin_feature_get_rank (feature);
334   if (strcmp ("textoverlay", name) != 0 && rank < GST_RANK_MARGINAL)
335     return FALSE;
336
337   /* Check if it's a renderer or a parser */
338   if (_is_renderer (factory)) {
339     is_renderer = TRUE;
340   } else if (_is_parser (factory)) {
341     is_renderer = FALSE;
342   } else {
343     return FALSE;
344   }
345
346   /* Check if there's a video sink in case of a renderer */
347   if (is_renderer) {
348     templates = gst_element_factory_get_static_pad_templates (factory);
349     for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
350       GstStaticPadTemplate *templ = walk->data;
351
352       /* we only care about the always-sink templates */
353       if (templ->direction == GST_PAD_SINK && templ->presence == GST_PAD_ALWAYS) {
354         if (strcmp (templ->name_template, "video") == 0 ||
355             strcmp (templ->name_template, "video_sink") == 0) {
356           have_video_sink = TRUE;
357         }
358       }
359     }
360   }
361   templ_caps = _get_sub_caps (factory);
362
363   if (is_renderer && have_video_sink && templ_caps) {
364     GST_DEBUG ("Found renderer element %s (%s) with caps %" GST_PTR_FORMAT,
365         gst_element_factory_get_longname (factory),
366         gst_plugin_feature_get_name (feature), templ_caps);
367     *subcaps = gst_caps_merge (*subcaps, templ_caps);
368     return TRUE;
369   } else if (!is_renderer && !have_video_sink && templ_caps) {
370     GST_DEBUG ("Found parser element %s (%s) with caps %" GST_PTR_FORMAT,
371         gst_element_factory_get_longname (factory),
372         gst_plugin_feature_get_name (feature), templ_caps);
373     *subcaps = gst_caps_merge (*subcaps, templ_caps);
374     return TRUE;
375   } else {
376     if (templ_caps)
377       gst_caps_unref (templ_caps);
378     return FALSE;
379   }
380 }
381
382 /* Call with factories_lock! */
383 static gboolean
384 gst_subtitle_overlay_update_factory_list (GstSubtitleOverlay * self)
385 {
386   GstRegistry *registry;
387   guint cookie;
388
389   registry = gst_registry_get ();
390   cookie = gst_registry_get_feature_list_cookie (registry);
391   if (!self->factories || self->factories_cookie != cookie) {
392     GstCaps *subcaps;
393     GList *factories;
394
395     subcaps = gst_caps_new_empty ();
396
397     factories = gst_registry_feature_filter (registry,
398         (GstPluginFeatureFilter) _factory_filter, FALSE, &subcaps);
399     GST_DEBUG_OBJECT (self, "Created factory caps: %" GST_PTR_FORMAT, subcaps);
400     gst_caps_replace (&self->factory_caps, subcaps);
401     gst_caps_unref (subcaps);
402     if (self->factories)
403       gst_plugin_feature_list_free (self->factories);
404     self->factories = factories;
405     self->factories_cookie = cookie;
406   }
407
408   return (self->factories != NULL);
409 }
410
411 G_LOCK_DEFINE_STATIC (_factory_caps);
412 static GstCaps *_factory_caps = NULL;
413 static guint32 _factory_caps_cookie = 0;
414
415 GstCaps *
416 gst_subtitle_overlay_create_factory_caps (void)
417 {
418   GstRegistry *registry;
419   GList *factories;
420   GstCaps *subcaps = NULL;
421   guint cookie;
422
423   registry = gst_registry_get ();
424   cookie = gst_registry_get_feature_list_cookie (registry);
425   G_LOCK (_factory_caps);
426   if (!_factory_caps || _factory_caps_cookie != cookie) {
427     if (_factory_caps)
428       gst_caps_unref (_factory_caps);
429     _factory_caps = gst_caps_new_empty ();
430
431     factories = gst_registry_feature_filter (registry,
432         (GstPluginFeatureFilter) _factory_filter, FALSE, &_factory_caps);
433     GST_DEBUG ("Created factory caps: %" GST_PTR_FORMAT, _factory_caps);
434     gst_plugin_feature_list_free (factories);
435     _factory_caps_cookie = cookie;
436   }
437   subcaps = gst_caps_ref (_factory_caps);
438   G_UNLOCK (_factory_caps);
439
440   return subcaps;
441 }
442
443 static gboolean
444 check_factory_for_caps (GstElementFactory * factory, const GstCaps * caps)
445 {
446   GstCaps *fcaps = _get_sub_caps (factory);
447   gboolean ret = (fcaps) ? gst_caps_can_intersect (fcaps, caps) : FALSE;
448
449   if (fcaps)
450     gst_caps_unref (fcaps);
451
452   if (ret)
453     gst_object_ref (factory);
454   return ret;
455 }
456
457 static GList *
458 gst_subtitle_overlay_get_factories_for_caps (const GList * list,
459     const GstCaps * caps)
460 {
461   const GList *walk = list;
462   GList *result = NULL;
463
464   while (walk) {
465     GstElementFactory *factory = walk->data;
466
467     walk = g_list_next (walk);
468
469     if (check_factory_for_caps (factory, caps)) {
470       result = g_list_prepend (result, factory);
471     }
472   }
473
474   return result;
475 }
476
477 static gint
478 _sort_by_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
479 {
480   gint diff;
481   const gchar *rname1, *rname2;
482
483   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
484   if (diff != 0)
485     return diff;
486
487   /* If the ranks are the same sort by name to get deterministic results */
488   rname1 = gst_plugin_feature_get_name (f1);
489   rname2 = gst_plugin_feature_get_name (f2);
490
491   diff = strcmp (rname1, rname2);
492
493   return diff;
494 }
495
496 static GstPad *
497 _get_sub_pad (GstElement * element)
498 {
499   GstPad *pad;
500   guint i;
501
502   for (i = 0; i < G_N_ELEMENTS (_sub_pad_names); i++) {
503     pad = gst_element_get_static_pad (element, _sub_pad_names[i]);
504     if (pad)
505       return pad;
506   }
507   return NULL;
508 }
509
510 static GstPad *
511 _get_video_pad (GstElement * element)
512 {
513   static const gchar *const pad_names[] = { "video", "video_sink" };
514   GstPad *pad;
515   guint i;
516
517   for (i = 0; i < G_N_ELEMENTS (pad_names); i++) {
518     pad = gst_element_get_static_pad (element, pad_names[i]);
519     if (pad)
520       return pad;
521   }
522   return NULL;
523 }
524
525 static gboolean
526 _create_element (GstSubtitleOverlay * self, GstElement ** element,
527     const gchar * factory_name, GstElementFactory * factory,
528     const gchar * element_name, gboolean mandatory)
529 {
530   GstElement *elt;
531
532   g_assert (!factory || !factory_name);
533
534   if (factory_name) {
535     elt = gst_element_factory_make (factory_name, element_name);
536   } else {
537     factory_name =
538         gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST (factory));
539     elt = gst_element_factory_create (factory, element_name);
540   }
541
542   if (G_UNLIKELY (!elt)) {
543     if (!factory) {
544       GstMessage *msg;
545
546       msg =
547           gst_missing_element_message_new (GST_ELEMENT_CAST (self),
548           factory_name);
549       gst_element_post_message (GST_ELEMENT_CAST (self), msg);
550
551       if (mandatory)
552         GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
553             ("no '%s' plugin found", factory_name));
554       else
555         GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, (NULL),
556             ("no '%s' plugin found", factory_name));
557     } else {
558       if (mandatory) {
559         GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL),
560             ("can't instantiate '%s'", factory_name));
561       } else {
562         GST_ELEMENT_WARNING (self, CORE, FAILED, (NULL),
563             ("can't instantiate '%s'", factory_name));
564       }
565     }
566
567     return FALSE;
568   }
569
570   if (G_UNLIKELY (gst_element_set_state (elt,
571               GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS)) {
572     gst_object_unref (elt);
573     if (mandatory) {
574       GST_ELEMENT_ERROR (self, CORE, STATE_CHANGE, (NULL),
575           ("failed to set '%s' to READY", factory_name));
576     } else {
577       GST_WARNING_OBJECT (self, "Failed to set '%s' to READY", factory_name);
578     }
579     return FALSE;
580   }
581
582   if (G_UNLIKELY (!gst_bin_add (GST_BIN_CAST (self), gst_object_ref (elt)))) {
583     gst_element_set_state (elt, GST_STATE_NULL);
584     gst_object_unref (elt);
585     if (mandatory) {
586       GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL),
587           ("failed to add '%s' to subtitleoverlay", factory_name));
588     } else {
589       GST_WARNING_OBJECT (self, "Failed to add '%s' to subtitleoverlay",
590           factory_name);
591     }
592     return FALSE;
593   }
594
595   gst_element_sync_state_with_parent (elt);
596   *element = elt;
597   return TRUE;
598 }
599
600 static void
601 _remove_element (GstSubtitleOverlay * self, GstElement ** element)
602 {
603   if (*element) {
604     gst_bin_remove (GST_BIN_CAST (self), *element);
605     gst_element_set_state (*element, GST_STATE_NULL);
606     gst_object_unref (*element);
607     *element = NULL;
608   }
609 }
610
611 static gboolean
612 _pad_query_convert_to_time (GstPad * pad, GstFormat src_format, gint64 src_val,
613     gint64 * dest_val)
614 {
615   GstFormat dest_format;
616
617   if (src_val == GST_CLOCK_TIME_NONE) {
618     *dest_val = GST_CLOCK_TIME_NONE;
619     return TRUE;
620   }
621
622   dest_format = GST_FORMAT_TIME;
623   return gst_pad_query_convert (pad, src_format, src_val, dest_format,
624       dest_val);
625 }
626
627 static void
628 _generate_update_segment_event (GstPad * pad, GstSegment * segment,
629     GstEvent ** event1)
630 {
631   GstEvent *event;
632   GstStructure *structure;
633   GstSegment newsegment;
634   gboolean use_newseg = FALSE;
635   gint64 start, stop, base, time, position, duration;
636
637   /* always push newsegment with format TIME */
638   if (segment->format != GST_FORMAT_TIME) {
639     gboolean res;
640     GstPad *peer;
641
642     peer = gst_pad_get_peer (pad);
643     if (peer) {
644       res = _pad_query_convert_to_time (peer, segment->format,
645           segment->start, &start);
646       res = res && _pad_query_convert_to_time (peer, segment->format,
647           segment->stop, &stop);
648       res = res && _pad_query_convert_to_time (peer, segment->format,
649           segment->time, &time);
650       res = res && _pad_query_convert_to_time (peer, segment->format,
651           segment->base, &base);
652       res = res && _pad_query_convert_to_time (peer, segment->format,
653           segment->position, &position);
654       res = res && _pad_query_convert_to_time (peer, segment->format,
655           segment->duration, &duration);
656       if (!res) {
657         start = 0;
658         stop = GST_CLOCK_TIME_NONE;
659         time = 0;
660         position = 0;
661         duration = GST_CLOCK_TIME_NONE;
662         base = 0;
663       } else {
664         use_newseg = TRUE;
665         gst_segment_init (&newsegment, GST_FORMAT_TIME);
666         newsegment.rate = segment->rate;
667         newsegment.applied_rate = segment->applied_rate;
668         newsegment.start = start;
669         newsegment.stop = stop;
670         newsegment.time = time;
671         newsegment.base = base;
672         newsegment.position = position;
673         newsegment.duration = duration;
674       }
675
676       gst_object_unref (peer);
677     }
678   }
679
680   if (use_newseg) {
681     event = gst_event_new_segment (&newsegment);
682   } else {
683     event = gst_event_new_segment (segment);
684   }
685   structure = gst_event_writable_structure (event);
686   gst_structure_id_set (structure, _subtitle_overlay_event_marker_id,
687       G_TYPE_BOOLEAN, TRUE, NULL);
688   *event1 = event;
689 }
690
691 static gboolean
692 _setup_passthrough (GstSubtitleOverlay * self)
693 {
694   GstPad *src, *sink;
695   GstElement *identity;
696
697   GST_DEBUG_OBJECT (self, "Doing video passthrough");
698
699   if (self->passthrough_identity) {
700     GST_DEBUG_OBJECT (self, "Already in passthrough mode");
701     goto out;
702   }
703
704   /* Unlink & destroy everything */
705   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
706   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad), NULL);
707   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad), NULL);
708   self->silent_property = NULL;
709   _remove_element (self, &self->post_colorspace);
710   _remove_element (self, &self->overlay);
711   _remove_element (self, &self->parser);
712   _remove_element (self, &self->renderer);
713   _remove_element (self, &self->pre_colorspace);
714   _remove_element (self, &self->passthrough_identity);
715
716   if (G_UNLIKELY (!_create_element (self, &self->passthrough_identity,
717               "identity", NULL, "passthrough-identity", TRUE))) {
718     return FALSE;
719   }
720
721   identity = self->passthrough_identity;
722   g_object_set (G_OBJECT (identity), "silent", TRUE, "signal-handoffs", FALSE,
723       NULL);
724
725   /* Set src ghostpad target */
726   src = gst_element_get_static_pad (self->passthrough_identity, "src");
727   if (G_UNLIKELY (!src)) {
728     GST_ELEMENT_ERROR (self, CORE, PAD, (NULL),
729         ("Failed to get srcpad from identity"));
730     return FALSE;
731   }
732
733   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
734               src))) {
735     GST_ELEMENT_ERROR (self, CORE, PAD, (NULL),
736         ("Failed to set srcpad target"));
737     gst_object_unref (src);
738     return FALSE;
739   }
740   gst_object_unref (src);
741
742   sink = gst_element_get_static_pad (self->passthrough_identity, "sink");
743   if (G_UNLIKELY (!sink)) {
744     GST_ELEMENT_ERROR (self, CORE, PAD, (NULL),
745         ("Failed to get sinkpad from identity"));
746     return FALSE;
747   }
748
749   /* Send segment to the identity. This is dropped because identity
750    * is not linked downstream yet */
751   if (self->video_segment.format != GST_FORMAT_UNDEFINED) {
752     GstEvent *event1;
753
754     _generate_update_segment_event (sink, &self->video_segment, &event1);
755     GST_DEBUG_OBJECT (self,
756         "Pushing video segment event: %" GST_PTR_FORMAT, event1);
757     gst_pad_send_event (sink, event1);
758   }
759
760   /* Link sink ghostpads to identity */
761   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
762               (self->video_sinkpad), sink))) {
763     GST_ELEMENT_ERROR (self, CORE, PAD, (NULL),
764         ("Failed to set video sinkpad target"));
765     gst_object_unref (sink);
766     return FALSE;
767   }
768   gst_object_unref (sink);
769
770   GST_DEBUG_OBJECT (self, "Video passthrough setup successfully");
771
772 out:
773   /* Unblock pads */
774   unblock_video (self);
775   unblock_subtitle (self);
776
777   return TRUE;
778 }
779
780 /* Must be called with subtitleoverlay lock! */
781 static gboolean
782 _has_property_with_type (GObject * object, const gchar * property, GType type)
783 {
784   GObjectClass *gobject_class;
785   GParamSpec *pspec;
786
787   gobject_class = G_OBJECT_GET_CLASS (object);
788   pspec = g_object_class_find_property (gobject_class, property);
789   return (pspec && pspec->value_type == type);
790 }
791
792 static void
793 gst_subtitle_overlay_set_fps (GstSubtitleOverlay * self)
794 {
795   if (!self->parser || self->fps_d == 0)
796     return;
797
798   if (!_has_property_with_type (G_OBJECT (self->parser), "video-fps",
799           GST_TYPE_FRACTION))
800     return;
801
802   GST_DEBUG_OBJECT (self, "Updating video-fps property in parser");
803   g_object_set (self->parser, "video-fps", self->fps_n, self->fps_d, NULL);
804 }
805
806 static const gchar *
807 _get_silent_property (GstElement * element, gboolean * invert)
808 {
809   static const struct
810   {
811     const gchar *name;
812     gboolean invert;
813   } properties[] = { {
814   "silent", FALSE}, {
815   "enable", TRUE}};
816   guint i;
817
818   for (i = 0; i < G_N_ELEMENTS (properties); i++) {
819     if (_has_property_with_type (G_OBJECT (element), properties[i].name,
820             G_TYPE_BOOLEAN)) {
821       *invert = properties[i].invert;
822       return properties[i].name;
823     }
824   }
825   return NULL;
826 }
827
828 static gboolean
829 _setup_parser (GstSubtitleOverlay * self)
830 {
831   GstPad *video_peer;
832
833   /* Try to get the latest video framerate */
834   video_peer = gst_pad_get_peer (self->video_sinkpad);
835   if (video_peer) {
836     GstCaps *video_caps;
837     gint fps_n, fps_d;
838
839     video_caps = gst_pad_get_current_caps (video_peer);
840     if (!video_caps) {
841       video_caps = gst_pad_query_caps (video_peer, NULL);
842       if (!gst_caps_is_fixed (video_caps)) {
843         gst_caps_unref (video_caps);
844         video_caps = NULL;
845       }
846     }
847
848     if (video_caps) {
849       GstStructure *st = gst_caps_get_structure (video_caps, 0);
850       if (gst_structure_get_fraction (st, "framerate", &fps_n, &fps_d)) {
851         GST_DEBUG_OBJECT (self, "New video fps: %d/%d", fps_n, fps_d);
852         self->fps_n = fps_n;
853         self->fps_d = fps_d;
854       }
855     }
856
857     if (video_caps)
858       gst_caps_unref (video_caps);
859     gst_object_unref (video_peer);
860   }
861
862   if (_has_property_with_type (G_OBJECT (self->parser), "subtitle-encoding",
863           G_TYPE_STRING))
864     g_object_set (self->parser, "subtitle-encoding", self->encoding, NULL);
865
866   /* Try to set video fps on the parser */
867   gst_subtitle_overlay_set_fps (self);
868
869
870   return TRUE;
871 }
872
873 static gboolean
874 _setup_renderer (GstSubtitleOverlay * self, GstElement * renderer)
875 {
876   GstElementFactory *factory = gst_element_get_factory (renderer);
877   const gchar *name =
878       gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST (factory));
879
880   if (strcmp (name, "textoverlay") == 0) {
881     /* Set some textoverlay specific properties */
882     g_object_set (G_OBJECT (renderer),
883         "halign", "center", "valign", "bottom", "wait-text", FALSE, NULL);
884     if (self->font_desc)
885       g_object_set (G_OBJECT (renderer), "font-desc", self->font_desc, NULL);
886     self->silent_property = "silent";
887     self->silent_property_invert = FALSE;
888   } else {
889     self->silent_property =
890         _get_silent_property (renderer, &self->silent_property_invert);
891     if (_has_property_with_type (G_OBJECT (renderer), "subtitle-encoding",
892             G_TYPE_STRING))
893       g_object_set (renderer, "subtitle-encoding", self->encoding, NULL);
894     if (_has_property_with_type (G_OBJECT (renderer), "font-desc",
895             G_TYPE_STRING))
896       g_object_set (renderer, "font-desc", self->font_desc, NULL);
897   }
898
899   return TRUE;
900 }
901
902 /* subtitle_src==NULL means: use subtitle_sink ghostpad */
903 static gboolean
904 _link_renderer (GstSubtitleOverlay * self, GstElement * renderer,
905     GstPad * subtitle_src)
906 {
907   GstPad *sink, *src;
908   gboolean is_video, is_hw;
909
910   is_video = _is_video_pad (self->video_sinkpad, &is_hw);
911
912   if (is_video) {
913     gboolean render_is_hw;
914
915     /* First check that renderer also supports the video format */
916     sink = _get_video_pad (renderer);
917     if (G_UNLIKELY (!sink)) {
918       GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
919       return FALSE;
920     }
921
922     if (is_video != _is_video_pad (sink, &render_is_hw) ||
923         is_hw != render_is_hw) {
924       GST_DEBUG_OBJECT (self, "Renderer doesn't support %s video",
925           is_hw ? "surface" : "raw");
926       gst_object_unref (sink);
927       return FALSE;
928     }
929     gst_object_unref (sink);
930
931     if (!is_hw) {
932       /* First link everything internally */
933       if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
934                   COLORSPACE, NULL, "post-colorspace", FALSE))) {
935         return FALSE;
936       }
937       src = gst_element_get_static_pad (renderer, "src");
938       if (G_UNLIKELY (!src)) {
939         GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
940         return FALSE;
941       }
942
943       sink = gst_element_get_static_pad (self->post_colorspace, "sink");
944       if (G_UNLIKELY (!sink)) {
945         GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
946         gst_object_unref (src);
947         return FALSE;
948       }
949
950       if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
951         GST_WARNING_OBJECT (self, "Can't link renderer with " COLORSPACE);
952         gst_object_unref (src);
953         gst_object_unref (sink);
954         return FALSE;
955       }
956       gst_object_unref (src);
957       gst_object_unref (sink);
958
959       if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
960                   COLORSPACE, NULL, "pre-colorspace", FALSE))) {
961         return FALSE;
962       }
963
964       sink = _get_video_pad (renderer);
965       if (G_UNLIKELY (!sink)) {
966         GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
967         return FALSE;
968       }
969
970       src = gst_element_get_static_pad (self->pre_colorspace, "src");
971       if (G_UNLIKELY (!src)) {
972         GST_WARNING_OBJECT (self, "Can't get srcpad from " COLORSPACE);
973         gst_object_unref (sink);
974         return FALSE;
975       }
976
977       if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
978         GST_WARNING_OBJECT (self, "Can't link " COLORSPACE " to renderer");
979         gst_object_unref (src);
980         gst_object_unref (sink);
981         return FALSE;
982       }
983       gst_object_unref (src);
984       gst_object_unref (sink);
985
986       /* Set src ghostpad target */
987       src = gst_element_get_static_pad (self->post_colorspace, "src");
988       if (G_UNLIKELY (!src)) {
989         GST_WARNING_OBJECT (self, "Can't get src pad from " COLORSPACE);
990         return FALSE;
991       }
992     } else {
993       /* Set src ghostpad target in the harware accelerated case */
994
995       src = gst_element_get_static_pad (renderer, "src");
996       if (G_UNLIKELY (!src)) {
997         GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
998         return FALSE;
999       }
1000     }
1001   } else {                      /* No video pad */
1002     GstCaps *allowed_caps, *video_caps = NULL;
1003     GstPad *video_peer;
1004     gboolean can_intersect = FALSE;
1005
1006     video_peer = gst_pad_get_peer (self->video_sinkpad);
1007     if (video_peer) {
1008       video_caps = gst_pad_get_current_caps (video_peer);
1009       if (!video_caps) {
1010         video_caps = gst_pad_query_caps (video_peer, NULL);
1011       }
1012       gst_object_unref (video_peer);
1013     }
1014
1015     sink = _get_video_pad (renderer);
1016     if (G_UNLIKELY (!sink)) {
1017       GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
1018       return FALSE;
1019     }
1020     allowed_caps = gst_pad_query_caps (sink, NULL);
1021     gst_object_unref (sink);
1022
1023     if (allowed_caps && video_caps)
1024       can_intersect = gst_caps_can_intersect (allowed_caps, video_caps);
1025
1026     if (allowed_caps)
1027       gst_caps_unref (allowed_caps);
1028
1029     if (video_caps)
1030       gst_caps_unref (video_caps);
1031
1032     if (G_UNLIKELY (!can_intersect)) {
1033       GST_WARNING_OBJECT (self, "Renderer with custom caps is not "
1034           "compatible with video stream");
1035       return FALSE;
1036     }
1037
1038     src = gst_element_get_static_pad (renderer, "src");
1039     if (G_UNLIKELY (!src)) {
1040       GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
1041       return FALSE;
1042     }
1043   }
1044
1045   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1046               (self->srcpad), src))) {
1047     GST_WARNING_OBJECT (self, "Can't set srcpad target");
1048     gst_object_unref (src);
1049     return FALSE;
1050   }
1051   gst_object_unref (src);
1052
1053   /* Set the sink ghostpad targets */
1054   if (self->pre_colorspace) {
1055     sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
1056     if (G_UNLIKELY (!sink)) {
1057       GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
1058       return FALSE;
1059     }
1060   } else {
1061     sink = _get_video_pad (renderer);
1062     if (G_UNLIKELY (!sink)) {
1063       GST_WARNING_OBJECT (self, "Can't get sink pad from %" GST_PTR_FORMAT,
1064           renderer);
1065       return FALSE;
1066     }
1067   }
1068
1069   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1070               (self->video_sinkpad), sink))) {
1071     GST_WARNING_OBJECT (self, "Can't set video sinkpad target");
1072     gst_object_unref (sink);
1073     return FALSE;
1074   }
1075   gst_object_unref (sink);
1076
1077   sink = _get_sub_pad (renderer);
1078   if (G_UNLIKELY (!sink)) {
1079     GST_WARNING_OBJECT (self, "Failed to get subpad");
1080     return FALSE;
1081   }
1082
1083   if (subtitle_src) {
1084     if (G_UNLIKELY (gst_pad_link (subtitle_src, sink) != GST_PAD_LINK_OK)) {
1085       GST_WARNING_OBJECT (self, "Failed to link subtitle srcpad with renderer");
1086       gst_object_unref (sink);
1087       return FALSE;
1088     }
1089   } else {
1090     if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1091                 (self->subtitle_sinkpad), sink))) {
1092       GST_WARNING_OBJECT (self, "Failed to set subtitle sink target");
1093       gst_object_unref (sink);
1094       return FALSE;
1095     }
1096   }
1097   gst_object_unref (sink);
1098
1099   return TRUE;
1100 }
1101
1102 static GstPadProbeReturn
1103 _pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1104 {
1105   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (user_data);
1106   GstCaps *subcaps;
1107   GList *l, *factories = NULL;
1108
1109   GST_DEBUG_OBJECT (pad, "Pad blocked");
1110
1111   GST_SUBTITLE_OVERLAY_LOCK (self);
1112   if (pad == self->video_block_pad)
1113     self->video_sink_blocked = TRUE;
1114   else if (pad == self->subtitle_block_pad)
1115     self->subtitle_sink_blocked = TRUE;
1116
1117   /* Now either both or the video sink are blocked */
1118
1119   /* Get current subtitle caps */
1120   subcaps = self->subcaps;
1121   if (!subcaps) {
1122     GstPad *peer;
1123
1124     peer = gst_pad_get_peer (self->subtitle_sinkpad);
1125     if (peer) {
1126       subcaps = gst_pad_get_current_caps (peer);
1127       if (!subcaps) {
1128         subcaps = gst_pad_query_caps (peer, NULL);
1129         if (!gst_caps_is_fixed (subcaps)) {
1130           gst_caps_unref (subcaps);
1131           subcaps = NULL;
1132         }
1133       }
1134       gst_object_unref (peer);
1135     }
1136     gst_caps_replace (&self->subcaps, subcaps);
1137     if (subcaps)
1138       gst_caps_unref (subcaps);
1139   }
1140   GST_DEBUG_OBJECT (self, "Current subtitle caps: %" GST_PTR_FORMAT, subcaps);
1141
1142   /* If there are no subcaps but the subtitle sink is blocked upstream
1143    * must behave wrong as there are no fixed caps set for the first
1144    * buffer or in-order event */
1145   if (G_UNLIKELY (!subcaps && self->subtitle_sink_blocked)) {
1146     GST_ELEMENT_WARNING (self, CORE, NEGOTIATION, (NULL),
1147         ("Subtitle sink is blocked but we have no subtitle caps"));
1148     subcaps = NULL;
1149   }
1150
1151   if (self->subtitle_error || (self->silent && !self->silent_property)) {
1152     _setup_passthrough (self);
1153     do_async_done (self);
1154     goto out;
1155   }
1156
1157   /* Now do something with the caps */
1158   if (subcaps && !self->subtitle_flush) {
1159     GstPad *target =
1160         gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad));
1161
1162     if (target && gst_pad_query_accept_caps (target, subcaps)) {
1163       GST_DEBUG_OBJECT (pad, "Target accepts caps");
1164
1165       gst_object_unref (target);
1166
1167       /* Unblock pads */
1168       unblock_video (self);
1169       unblock_subtitle (self);
1170       goto out;
1171     } else if (target) {
1172       gst_object_unref (target);
1173     }
1174   }
1175
1176   if (self->subtitle_sink_blocked && !self->video_sink_blocked) {
1177     GST_DEBUG_OBJECT (self, "Subtitle sink blocked but video not blocked");
1178     block_video (self);
1179     goto out;
1180   }
1181
1182   self->subtitle_flush = FALSE;
1183
1184   /* Find our factories */
1185   g_mutex_lock (self->factories_lock);
1186   gst_subtitle_overlay_update_factory_list (self);
1187   if (subcaps) {
1188     factories =
1189         gst_subtitle_overlay_get_factories_for_caps (self->factories, subcaps);
1190     if (!factories) {
1191       GstMessage *msg;
1192
1193       msg = gst_missing_decoder_message_new (GST_ELEMENT_CAST (self), subcaps);
1194       gst_element_post_message (GST_ELEMENT_CAST (self), msg);
1195       GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, (NULL),
1196           ("no suitable subtitle plugin found"));
1197       subcaps = NULL;
1198       self->subtitle_error = TRUE;
1199     }
1200   }
1201   g_mutex_unlock (self->factories_lock);
1202
1203   if (!subcaps) {
1204     _setup_passthrough (self);
1205     do_async_done (self);
1206     goto out;
1207   }
1208
1209   /* Now the interesting parts are done: subtitle overlaying! */
1210
1211   /* Sort the factories by rank */
1212   factories = g_list_sort (factories, (GCompareFunc) _sort_by_ranks);
1213
1214   for (l = factories; l; l = l->next) {
1215     GstElementFactory *factory = l->data;
1216     gboolean is_renderer = _is_renderer (factory);
1217     GstPad *sink, *src;
1218
1219     /* Unlink & destroy everything */
1220
1221     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1222     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad), NULL);
1223     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1224         NULL);
1225     self->silent_property = NULL;
1226     _remove_element (self, &self->post_colorspace);
1227     _remove_element (self, &self->overlay);
1228     _remove_element (self, &self->parser);
1229     _remove_element (self, &self->renderer);
1230     _remove_element (self, &self->pre_colorspace);
1231     _remove_element (self, &self->passthrough_identity);
1232
1233     GST_DEBUG_OBJECT (self, "Trying factory '%s'",
1234         GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1235                 (factory))));
1236
1237     if (G_UNLIKELY ((is_renderer
1238                 && !_create_element (self, &self->renderer, NULL, factory,
1239                     "renderer", FALSE)) || (!is_renderer
1240                 && !_create_element (self, &self->parser, NULL, factory,
1241                     "parser", FALSE))))
1242       continue;
1243
1244     if (!is_renderer) {
1245       GstCaps *parser_caps;
1246       GList *overlay_factories, *k;
1247
1248       if (!_setup_parser (self))
1249         continue;
1250
1251       /* Find our factories */
1252       src = gst_element_get_static_pad (self->parser, "src");
1253       parser_caps = gst_pad_query_caps (src, NULL);
1254       gst_object_unref (src);
1255
1256       g_assert (parser_caps != NULL);
1257
1258       g_mutex_lock (self->factories_lock);
1259       gst_subtitle_overlay_update_factory_list (self);
1260       GST_DEBUG_OBJECT (self,
1261           "Searching overlay factories for caps %" GST_PTR_FORMAT, parser_caps);
1262       overlay_factories =
1263           gst_subtitle_overlay_get_factories_for_caps (self->factories,
1264           parser_caps);
1265       g_mutex_unlock (self->factories_lock);
1266
1267       if (!overlay_factories) {
1268         GST_WARNING_OBJECT (self,
1269             "Found no suitable overlay factories for caps %" GST_PTR_FORMAT,
1270             parser_caps);
1271         gst_caps_unref (parser_caps);
1272         continue;
1273       }
1274       gst_caps_unref (parser_caps);
1275
1276       /* Sort the factories by rank */
1277       overlay_factories =
1278           g_list_sort (overlay_factories, (GCompareFunc) _sort_by_ranks);
1279
1280       for (k = overlay_factories; k; k = k->next) {
1281         GstElementFactory *overlay_factory = k->data;
1282
1283         GST_DEBUG_OBJECT (self, "Trying overlay factory '%s'",
1284             GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1285                     (overlay_factory))));
1286
1287         /* Try this factory and link it, otherwise unlink everything
1288          * again and remove the overlay. Up to this point only the
1289          * parser was instantiated and setup, nothing was linked
1290          */
1291
1292         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1293         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad),
1294             NULL);
1295         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1296             NULL);
1297         self->silent_property = NULL;
1298         _remove_element (self, &self->post_colorspace);
1299         _remove_element (self, &self->overlay);
1300         _remove_element (self, &self->pre_colorspace);
1301
1302         if (!_create_element (self, &self->overlay, NULL, overlay_factory,
1303                 "overlay", FALSE)) {
1304           GST_DEBUG_OBJECT (self, "Could not create element");
1305           continue;
1306         }
1307
1308         if (!_setup_renderer (self, self->overlay)) {
1309           GST_DEBUG_OBJECT (self, "Could not setup element");
1310           continue;
1311         }
1312
1313         src = gst_element_get_static_pad (self->parser, "src");
1314         if (!_link_renderer (self, self->overlay, src)) {
1315           GST_DEBUG_OBJECT (self, "Could not link element");
1316           gst_object_unref (src);
1317           continue;
1318         }
1319         gst_object_unref (src);
1320
1321         /* Everything done here, go out of loop */
1322         GST_DEBUG_OBJECT (self, "%s is a suitable element",
1323             GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1324                     (overlay_factory))));
1325         break;
1326       }
1327
1328       if (overlay_factories)
1329         gst_plugin_feature_list_free (overlay_factories);
1330
1331       if (G_UNLIKELY (k == NULL)) {
1332         GST_WARNING_OBJECT (self, "Failed to find usable overlay factory");
1333         continue;
1334       }
1335
1336       /* Now link subtitle sinkpad of the bin and the parser */
1337       sink = gst_element_get_static_pad (self->parser, "sink");
1338       if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1339               (self->subtitle_sinkpad), sink)) {
1340         gst_object_unref (sink);
1341         continue;
1342       }
1343       gst_object_unref (sink);
1344
1345       /* Everything done here, go out of loop */
1346       break;
1347     } else {
1348       /* Is renderer factory */
1349
1350       if (!_setup_renderer (self, self->renderer))
1351         continue;
1352
1353       /* subtitle_src==NULL means: use subtitle_sink ghostpad */
1354       if (!_link_renderer (self, self->renderer, NULL))
1355         continue;
1356
1357       /* Everything done here, go out of loop */
1358       break;
1359     }
1360   }
1361
1362   if (G_UNLIKELY (l == NULL)) {
1363     GST_ELEMENT_WARNING (self, CORE, FAILED, (NULL),
1364         ("Failed to find any usable factories"));
1365     self->subtitle_error = TRUE;
1366     _setup_passthrough (self);
1367     do_async_done (self);
1368     goto out;
1369   }
1370
1371   /* Send segments to the renderer if necessary. These are not sent
1372    * outside this element because of the proxy pad event handler */
1373   if (self->video_segment.format != GST_FORMAT_UNDEFINED) {
1374     GstEvent *event1;
1375     GstPad *sink;
1376
1377     if (self->pre_colorspace) {
1378       sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
1379     } else {
1380       sink = _get_video_pad ((self->renderer) ? self->renderer : self->overlay);
1381     }
1382
1383     _generate_update_segment_event (sink, &self->video_segment, &event1);
1384     GST_DEBUG_OBJECT (self,
1385         "Pushing video update segment event: %" GST_PTR_FORMAT,
1386         gst_event_get_structure (event1));
1387     gst_pad_send_event (sink, event1);
1388     gst_object_unref (sink);
1389   }
1390
1391   if (self->subtitle_segment.format != GST_FORMAT_UNDEFINED) {
1392     GstEvent *event1;
1393     GstPad *sink;
1394
1395     if (self->renderer)
1396       sink = _get_sub_pad (self->renderer);
1397     else
1398       sink = gst_element_get_static_pad (self->parser, "sink");
1399
1400     _generate_update_segment_event (sink, &self->subtitle_segment, &event1);
1401     GST_DEBUG_OBJECT (self,
1402         "Pushing subtitle update segment event: %" GST_PTR_FORMAT,
1403         gst_event_get_structure (event1));
1404     gst_pad_send_event (sink, event1);
1405     gst_object_unref (sink);
1406   }
1407
1408   GST_DEBUG_OBJECT (self, "Everything worked, unblocking pads");
1409   unblock_video (self);
1410   unblock_subtitle (self);
1411   do_async_done (self);
1412
1413 out:
1414   if (factories)
1415     gst_plugin_feature_list_free (factories);
1416   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1417
1418   return GST_PAD_PROBE_OK;
1419 }
1420
1421 static GstStateChangeReturn
1422 gst_subtitle_overlay_change_state (GstElement * element,
1423     GstStateChange transition)
1424 {
1425   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (element);
1426   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1427
1428   switch (transition) {
1429     case GST_STATE_CHANGE_NULL_TO_READY:
1430       GST_DEBUG_OBJECT (self, "State change NULL->READY");
1431       g_mutex_lock (self->factories_lock);
1432       if (G_UNLIKELY (!gst_subtitle_overlay_update_factory_list (self))) {
1433         g_mutex_unlock (self->factories_lock);
1434         return GST_STATE_CHANGE_FAILURE;
1435       }
1436       g_mutex_unlock (self->factories_lock);
1437
1438       GST_SUBTITLE_OVERLAY_LOCK (self);
1439       /* Set the internal pads to blocking */
1440       block_video (self);
1441       block_subtitle (self);
1442       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1443       break;
1444     case GST_STATE_CHANGE_READY_TO_PAUSED:
1445       GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
1446       gst_segment_init (&self->video_segment, GST_FORMAT_UNDEFINED);
1447       gst_segment_init (&self->subtitle_segment, GST_FORMAT_UNDEFINED);
1448
1449       self->fps_n = self->fps_d = 0;
1450
1451       self->subtitle_flush = FALSE;
1452       self->subtitle_error = FALSE;
1453
1454       self->downstream_chain_error = FALSE;
1455
1456       do_async_start (self);
1457       ret = GST_STATE_CHANGE_ASYNC;
1458
1459       break;
1460     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1461       GST_DEBUG_OBJECT (self, "State change PAUSED->PLAYING");
1462     default:
1463       break;
1464   }
1465
1466   {
1467     GstStateChangeReturn bret;
1468
1469     bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1470     GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", bret);
1471     if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
1472       return ret;
1473     else if (bret == GST_STATE_CHANGE_ASYNC)
1474       ret = bret;
1475     else if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
1476       do_async_done (self);
1477       ret = bret;
1478     }
1479   }
1480
1481   switch (transition) {
1482     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1483       GST_DEBUG_OBJECT (self, "State change PLAYING->PAUSED");
1484       break;
1485     case GST_STATE_CHANGE_PAUSED_TO_READY:
1486       GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
1487
1488       /* Set the pads back to blocking state */
1489       GST_SUBTITLE_OVERLAY_LOCK (self);
1490       block_video (self);
1491       block_subtitle (self);
1492       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1493
1494       do_async_done (self);
1495
1496       break;
1497     case GST_STATE_CHANGE_READY_TO_NULL:
1498       GST_DEBUG_OBJECT (self, "State change READY->NULL");
1499
1500       GST_SUBTITLE_OVERLAY_LOCK (self);
1501       gst_caps_replace (&self->subcaps, NULL);
1502
1503       /* Unlink ghost pads */
1504       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1505       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad), NULL);
1506       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1507           NULL);
1508
1509       /* Unblock pads */
1510       unblock_video (self);
1511       unblock_subtitle (self);
1512
1513       /* Remove elements */
1514       self->silent_property = NULL;
1515       _remove_element (self, &self->post_colorspace);
1516       _remove_element (self, &self->overlay);
1517       _remove_element (self, &self->parser);
1518       _remove_element (self, &self->renderer);
1519       _remove_element (self, &self->pre_colorspace);
1520       _remove_element (self, &self->passthrough_identity);
1521       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1522
1523       break;
1524     default:
1525       break;
1526   }
1527
1528   return ret;
1529 }
1530
1531 static void
1532 gst_subtitle_overlay_handle_message (GstBin * bin, GstMessage * message)
1533 {
1534   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (bin);
1535
1536   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
1537     GstObject *src = GST_MESSAGE_SRC (message);
1538
1539     /* Convert error messages from the subtitle pipeline to
1540      * warnings and switch to passthrough mode */
1541     if (src && (
1542             (self->overlay
1543                 && gst_object_has_ancestor (src,
1544                     GST_OBJECT_CAST (self->overlay))) || (self->parser
1545                 && gst_object_has_ancestor (src,
1546                     GST_OBJECT_CAST (self->parser))) || (self->renderer
1547                 && gst_object_has_ancestor (src,
1548                     GST_OBJECT_CAST (self->renderer))))) {
1549       GError *err = NULL;
1550       gchar *debug = NULL;
1551       GstMessage *wmsg;
1552
1553       gst_message_parse_error (message, &err, &debug);
1554       GST_DEBUG_OBJECT (self,
1555           "Got error message from subtitle element %s: %s (%s)",
1556           GST_MESSAGE_SRC_NAME (message), GST_STR_NULL (err->message),
1557           GST_STR_NULL (debug));
1558
1559       wmsg = gst_message_new_warning (src, err, debug);
1560       gst_message_unref (message);
1561       g_error_free (err);
1562       g_free (debug);
1563       message = wmsg;
1564
1565       GST_SUBTITLE_OVERLAY_LOCK (self);
1566       self->subtitle_error = TRUE;
1567
1568       block_subtitle (self);
1569       block_video (self);
1570       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1571     }
1572   }
1573
1574   GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1575 }
1576
1577 static void
1578 gst_subtitle_overlay_get_property (GObject * object, guint prop_id,
1579     GValue * value, GParamSpec * pspec)
1580 {
1581   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (object);
1582
1583   switch (prop_id) {
1584     case PROP_SILENT:
1585       g_value_set_boolean (value, self->silent);
1586       break;
1587     case PROP_FONT_DESC:
1588       GST_SUBTITLE_OVERLAY_LOCK (self);
1589       g_value_set_string (value, self->font_desc);
1590       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1591       break;
1592     case PROP_SUBTITLE_ENCODING:
1593       GST_SUBTITLE_OVERLAY_LOCK (self);
1594       g_value_set_string (value, self->encoding);
1595       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1596       break;
1597     default:
1598       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1599       break;
1600   }
1601 }
1602
1603 static void
1604 gst_subtitle_overlay_set_property (GObject * object, guint prop_id,
1605     const GValue * value, GParamSpec * pspec)
1606 {
1607   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (object);
1608
1609   switch (prop_id) {
1610     case PROP_SILENT:
1611       GST_SUBTITLE_OVERLAY_LOCK (self);
1612       self->silent = g_value_get_boolean (value);
1613       if (self->silent_property) {
1614         gboolean silent = self->silent;
1615
1616         if (self->silent_property_invert)
1617           silent = !silent;
1618
1619         if (self->overlay)
1620           g_object_set (self->overlay, self->silent_property, silent, NULL);
1621         else if (self->renderer)
1622           g_object_set (self->renderer, self->silent_property, silent, NULL);
1623       } else {
1624         block_subtitle (self);
1625         block_video (self);
1626       }
1627       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1628       break;
1629     case PROP_FONT_DESC:
1630       GST_SUBTITLE_OVERLAY_LOCK (self);
1631       g_free (self->font_desc);
1632       self->font_desc = g_value_dup_string (value);
1633       if (self->overlay
1634           && _has_property_with_type (G_OBJECT (self->overlay), "font-desc",
1635               G_TYPE_STRING))
1636         g_object_set (self->overlay, "font-desc", self->font_desc, NULL);
1637       else if (self->renderer
1638           && _has_property_with_type (G_OBJECT (self->renderer), "font-desc",
1639               G_TYPE_STRING))
1640         g_object_set (self->renderer, "font-desc", self->font_desc, NULL);
1641       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1642       break;
1643     case PROP_SUBTITLE_ENCODING:
1644       GST_SUBTITLE_OVERLAY_LOCK (self);
1645       g_free (self->encoding);
1646       self->encoding = g_value_dup_string (value);
1647       if (self->renderer
1648           && _has_property_with_type (G_OBJECT (self->renderer),
1649               "subtitle-encoding", G_TYPE_STRING))
1650         g_object_set (self->renderer, "subtitle-encoding", self->encoding,
1651             NULL);
1652       if (self->parser
1653           && _has_property_with_type (G_OBJECT (self->parser),
1654               "subtitle-encoding", G_TYPE_STRING))
1655         g_object_set (self->parser, "subtitle-encoding", self->encoding, NULL);
1656       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1657       break;
1658     default:
1659       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1660       break;
1661   }
1662 }
1663
1664 static void
1665 gst_subtitle_overlay_class_init (GstSubtitleOverlayClass * klass)
1666 {
1667   GObjectClass *gobject_class = (GObjectClass *) klass;
1668   GstElementClass *element_class = (GstElementClass *) klass;
1669   GstBinClass *bin_class = (GstBinClass *) klass;
1670
1671   gobject_class->finalize = gst_subtitle_overlay_finalize;
1672   gobject_class->set_property = gst_subtitle_overlay_set_property;
1673   gobject_class->get_property = gst_subtitle_overlay_get_property;
1674
1675   g_object_class_install_property (gobject_class, PROP_SILENT,
1676       g_param_spec_boolean ("silent",
1677           "Silent",
1678           "Whether to show subtitles", FALSE,
1679           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1680
1681   g_object_class_install_property (gobject_class, PROP_FONT_DESC,
1682       g_param_spec_string ("font-desc",
1683           "Subtitle font description",
1684           "Pango font description of font "
1685           "to be used for subtitle rendering", NULL,
1686           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1687
1688   g_object_class_install_property (gobject_class, PROP_SUBTITLE_ENCODING,
1689       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
1690           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
1691           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
1692           "be checked for an encoding to use. If that is not set either, "
1693           "ISO-8859-15 will be assumed.", NULL,
1694           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1695
1696   gst_element_class_add_pad_template (element_class,
1697       gst_static_pad_template_get (&srctemplate));
1698
1699   gst_element_class_add_pad_template (element_class,
1700       gst_static_pad_template_get (&video_sinktemplate));
1701   gst_element_class_add_pad_template (element_class,
1702       gst_static_pad_template_get (&subtitle_sinktemplate));
1703
1704   gst_element_class_set_static_metadata (element_class, "Subtitle Overlay",
1705       "Video/Overlay/Subtitle",
1706       "Overlays a video stream with subtitles",
1707       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1708
1709   element_class->change_state =
1710       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_change_state);
1711
1712   bin_class->handle_message =
1713       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_handle_message);
1714 }
1715
1716 static GstFlowReturn
1717 gst_subtitle_overlay_src_proxy_chain (GstPad * proxypad, GstObject * parent,
1718     GstBuffer * buffer)
1719 {
1720   GstPad *ghostpad;
1721   GstSubtitleOverlay *self;
1722   GstFlowReturn ret;
1723
1724   ghostpad = GST_PAD_CAST (parent);
1725   if (G_UNLIKELY (!ghostpad)) {
1726     gst_buffer_unref (buffer);
1727     return GST_FLOW_ERROR;
1728   }
1729   self = GST_SUBTITLE_OVERLAY_CAST (gst_pad_get_parent (ghostpad));
1730   if (G_UNLIKELY (!self || self->srcpad != ghostpad)) {
1731     gst_buffer_unref (buffer);
1732     gst_object_unref (ghostpad);
1733     return GST_FLOW_ERROR;
1734   }
1735
1736   ret = gst_proxy_pad_chain_default (proxypad, parent, buffer);
1737
1738   if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
1739     GST_ERROR_OBJECT (self, "Downstream chain error: %s",
1740         gst_flow_get_name (ret));
1741     self->downstream_chain_error = TRUE;
1742   }
1743
1744   gst_object_unref (self);
1745
1746   return ret;
1747 }
1748
1749 static gboolean
1750 gst_subtitle_overlay_src_proxy_event (GstPad * proxypad, GstObject * parent,
1751     GstEvent * event)
1752 {
1753   GstPad *ghostpad = NULL;
1754   GstSubtitleOverlay *self = NULL;
1755   gboolean ret = FALSE;
1756   const GstStructure *s;
1757
1758   ghostpad = GST_PAD_CAST (parent);
1759   if (G_UNLIKELY (!ghostpad))
1760     goto out;
1761   self = GST_SUBTITLE_OVERLAY_CAST (gst_pad_get_parent (ghostpad));
1762   if (G_UNLIKELY (!self || self->srcpad != ghostpad))
1763     goto out;
1764
1765   s = gst_event_get_structure (event);
1766   if (s && gst_structure_id_has_field (s, _subtitle_overlay_event_marker_id)) {
1767     GST_DEBUG_OBJECT (ghostpad,
1768         "Dropping event with marker: %" GST_PTR_FORMAT,
1769         gst_event_get_structure (event));
1770     gst_event_unref (event);
1771     event = NULL;
1772     ret = TRUE;
1773   } else {
1774     ret = gst_proxy_pad_event_default (proxypad, parent, event);
1775     event = NULL;
1776   }
1777
1778 out:
1779   if (event)
1780     gst_event_unref (event);
1781   if (self)
1782     gst_object_unref (self);
1783
1784   return ret;
1785 }
1786
1787 static gboolean
1788 gst_subtitle_overlay_video_sink_setcaps (GstSubtitleOverlay * self,
1789     GstCaps * caps)
1790 {
1791   GstPad *target;
1792   gboolean ret = TRUE;
1793   GstVideoInfo info;
1794
1795   GST_DEBUG_OBJECT (self, "Setting caps: %" GST_PTR_FORMAT, caps);
1796
1797   if (!gst_video_info_from_caps (&info, caps)) {
1798     GST_ERROR_OBJECT (self, "Failed to parse caps");
1799     ret = FALSE;
1800     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1801     goto out;
1802   }
1803
1804   target = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->video_sinkpad));
1805
1806   GST_SUBTITLE_OVERLAY_LOCK (self);
1807
1808   if (!target || !gst_pad_query_accept_caps (target, caps)) {
1809     GST_DEBUG_OBJECT (target, "Target did not accept caps -- reconfiguring");
1810
1811     block_subtitle (self);
1812     block_video (self);
1813   }
1814
1815   if (self->fps_n != info.fps_n || self->fps_d != info.fps_d) {
1816     GST_DEBUG_OBJECT (self, "New video fps: %d/%d", info.fps_n, info.fps_d);
1817     self->fps_n = info.fps_n;
1818     self->fps_d = info.fps_d;
1819     gst_subtitle_overlay_set_fps (self);
1820   }
1821   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1822
1823   if (target)
1824     gst_object_unref (target);
1825
1826 out:
1827
1828   return ret;
1829 }
1830
1831 static gboolean
1832 gst_subtitle_overlay_video_sink_event (GstPad * pad, GstObject * parent,
1833     GstEvent * event)
1834 {
1835   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1836   gboolean ret;
1837
1838   switch (GST_EVENT_TYPE (event)) {
1839     case GST_EVENT_FLUSH_STOP:
1840     {
1841       GST_DEBUG_OBJECT (pad,
1842           "Resetting video segment because of flush-stop event");
1843       gst_segment_init (&self->video_segment, GST_FORMAT_UNDEFINED);
1844       self->fps_n = self->fps_d = 0;
1845       break;
1846     }
1847     case GST_EVENT_CAPS:
1848     {
1849       GstCaps *caps;
1850
1851       gst_event_parse_caps (event, &caps);
1852       ret = gst_subtitle_overlay_video_sink_setcaps (self, caps);
1853       if (!ret)
1854         goto done;
1855       break;
1856     }
1857     default:
1858       break;
1859   }
1860
1861   ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1862
1863   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1864     GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
1865     gst_event_copy_segment (event, &self->video_segment);
1866
1867     if (self->video_segment.format != GST_FORMAT_TIME)
1868       goto invalid_format;
1869   }
1870
1871 done:
1872   gst_event_unref (event);
1873
1874   return ret;
1875
1876   /* ERRORS */
1877 invalid_format:
1878   {
1879     GST_ERROR_OBJECT (pad, "Segment event in non-time format: %s",
1880         gst_format_get_name (self->video_segment.format));
1881     ret = FALSE;
1882     goto done;
1883   }
1884 }
1885
1886 static GstFlowReturn
1887 gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstObject * parent,
1888     GstBuffer * buffer)
1889 {
1890   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1891   GstFlowReturn ret = gst_proxy_pad_chain_default (pad, parent, buffer);
1892
1893   if (G_UNLIKELY (self->downstream_chain_error) || self->passthrough_identity) {
1894     return ret;
1895   } else if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
1896     GST_DEBUG_OBJECT (self, "Subtitle renderer produced chain error: %s",
1897         gst_flow_get_name (ret));
1898     GST_SUBTITLE_OVERLAY_LOCK (self);
1899     self->subtitle_error = TRUE;
1900     block_subtitle (self);
1901     block_video (self);
1902     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1903
1904     return GST_FLOW_OK;
1905   }
1906
1907   return ret;
1908 }
1909
1910 static GstFlowReturn
1911 gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstObject * parent,
1912     GstBuffer * buffer)
1913 {
1914   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1915
1916   if (self->subtitle_error) {
1917     gst_buffer_unref (buffer);
1918     return GST_FLOW_OK;
1919   } else {
1920     GstFlowReturn ret = gst_proxy_pad_chain_default (pad, parent, buffer);
1921
1922     if (IS_SUBTITLE_CHAIN_IGNORE_ERROR (ret)) {
1923       GST_DEBUG_OBJECT (self, "Subtitle chain error: %s",
1924           gst_flow_get_name (ret));
1925       GST_SUBTITLE_OVERLAY_LOCK (self);
1926       self->subtitle_error = TRUE;
1927       block_subtitle (self);
1928       block_video (self);
1929       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1930
1931       return GST_FLOW_OK;
1932     }
1933
1934     return ret;
1935   }
1936 }
1937
1938 static GstCaps *
1939 gst_subtitle_overlay_subtitle_sink_getcaps (GstPad * pad, GstCaps * filter)
1940 {
1941   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (gst_pad_get_parent (pad));
1942   GstCaps *ret;
1943
1944   g_mutex_lock (self->factories_lock);
1945   if (G_UNLIKELY (!gst_subtitle_overlay_update_factory_list (self)))
1946     ret = gst_caps_new_empty ();
1947   else if (filter)
1948     ret =
1949         gst_caps_intersect_full (filter, self->factory_caps,
1950         GST_CAPS_INTERSECT_FIRST);
1951   else
1952     ret = gst_caps_ref (self->factory_caps);
1953   g_mutex_unlock (self->factories_lock);
1954
1955   GST_DEBUG_OBJECT (pad, "Returning subtitle caps %" GST_PTR_FORMAT, ret);
1956
1957   gst_object_unref (self);
1958
1959   return ret;
1960 }
1961
1962 static gboolean
1963 gst_subtitle_overlay_subtitle_sink_setcaps (GstSubtitleOverlay * self,
1964     GstCaps * caps)
1965 {
1966   gboolean ret = TRUE;
1967   GstPad *target = NULL;;
1968
1969   GST_DEBUG_OBJECT (self, "Setting caps: %" GST_PTR_FORMAT, caps);
1970
1971   target =
1972       gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad));
1973
1974   GST_SUBTITLE_OVERLAY_LOCK (self);
1975   gst_caps_replace (&self->subcaps, caps);
1976
1977   if (target && gst_pad_query_accept_caps (target, caps)) {
1978     GST_DEBUG_OBJECT (self, "Target accepts caps");
1979     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1980     goto out;
1981   }
1982
1983   GST_DEBUG_OBJECT (self, "Target did not accept caps");
1984
1985   self->subtitle_error = FALSE;
1986   block_subtitle (self);
1987   block_video (self);
1988   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1989
1990 out:
1991   if (target)
1992     gst_object_unref (target);
1993
1994   return ret;
1995 }
1996
1997 static GstPadLinkReturn
1998 gst_subtitle_overlay_subtitle_sink_link (GstPad * pad, GstPad * peer)
1999 {
2000   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (gst_pad_get_parent (pad));
2001   GstPadLinkReturn ret;
2002   GstCaps *caps;
2003
2004   GST_DEBUG_OBJECT (pad, "Linking pad to peer %" GST_PTR_FORMAT, peer);
2005
2006   caps = gst_pad_get_current_caps (peer);
2007   if (!caps) {
2008     caps = gst_pad_query_caps (peer, NULL);
2009     if (!gst_caps_is_fixed (caps)) {
2010       gst_caps_unref (caps);
2011       caps = NULL;
2012     }
2013   }
2014
2015   if (caps) {
2016     GST_SUBTITLE_OVERLAY_LOCK (self);
2017     GST_DEBUG_OBJECT (pad, "Have fixed peer caps: %" GST_PTR_FORMAT, caps);
2018     gst_caps_replace (&self->subcaps, caps);
2019
2020     self->subtitle_error = FALSE;
2021
2022     block_subtitle (self);
2023     block_video (self);
2024     GST_SUBTITLE_OVERLAY_UNLOCK (self);
2025     gst_caps_unref (caps);
2026   }
2027
2028   ret = gst_ghost_pad_link_default (pad, peer);
2029
2030   gst_object_unref (self);
2031   return ret;
2032 }
2033
2034 static void
2035 gst_subtitle_overlay_subtitle_sink_unlink (GstPad * pad)
2036 {
2037   GstSubtitleOverlay *self =
2038       GST_SUBTITLE_OVERLAY (gst_object_ref (GST_PAD_PARENT (pad)));
2039
2040   /* FIXME: Can't use gst_pad_get_parent() here because this is called with
2041    * the object lock from state changes
2042    */
2043
2044   GST_DEBUG_OBJECT (pad, "Pad unlinking");
2045   gst_caps_replace (&self->subcaps, NULL);
2046
2047   gst_ghost_pad_unlink_default (pad);
2048
2049   GST_SUBTITLE_OVERLAY_LOCK (self);
2050   self->subtitle_error = FALSE;
2051
2052   block_subtitle (self);
2053   block_video (self);
2054   GST_SUBTITLE_OVERLAY_UNLOCK (self);
2055
2056   gst_object_unref (self);
2057 }
2058
2059 static gboolean
2060 gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstObject * parent,
2061     GstEvent * event)
2062 {
2063   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
2064   gboolean ret;
2065
2066   GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2067
2068   if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB &&
2069       gst_event_has_name (event, "playsink-custom-subtitle-flush")) {
2070     GST_DEBUG_OBJECT (pad, "Custom subtitle flush event");
2071     GST_SUBTITLE_OVERLAY_LOCK (self);
2072     self->subtitle_flush = TRUE;
2073     self->subtitle_error = FALSE;
2074     block_subtitle (self);
2075     block_video (self);
2076     GST_SUBTITLE_OVERLAY_UNLOCK (self);
2077
2078     gst_event_unref (event);
2079     event = NULL;
2080     ret = TRUE;
2081     goto out;
2082   }
2083
2084   switch (GST_EVENT_TYPE (event)) {
2085     case GST_EVENT_CAPS:
2086     {
2087       GstCaps *caps;
2088
2089       gst_event_parse_caps (event, &caps);
2090       ret = gst_subtitle_overlay_subtitle_sink_setcaps (self, caps);
2091       if (!ret)
2092         goto out;
2093       break;
2094     }
2095     case GST_EVENT_FLUSH_STOP:
2096       GST_DEBUG_OBJECT (pad,
2097           "Resetting subtitle segment because of flush-stop");
2098       gst_segment_init (&self->subtitle_segment, GST_FORMAT_UNDEFINED);
2099       /* fall through */
2100     case GST_EVENT_FLUSH_START:
2101     case GST_EVENT_SEGMENT:
2102     case GST_EVENT_EOS:
2103     {
2104       GstStructure *structure;
2105
2106       /* Add our event marker to make sure no events from here go ever outside
2107        * the element, they're only interesting for our internal elements */
2108       event = GST_EVENT_CAST (gst_event_make_writable (event));
2109       structure = gst_event_writable_structure (event);
2110
2111       gst_structure_id_set (structure, _subtitle_overlay_event_marker_id,
2112           G_TYPE_BOOLEAN, TRUE, NULL);
2113       break;
2114     }
2115     default:
2116       break;
2117   }
2118
2119   ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2120
2121   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
2122     const GstSegment *eventsegment;
2123     GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
2124
2125     gst_event_parse_segment (event, &eventsegment);
2126
2127     if (eventsegment->format == GST_FORMAT_TIME) {
2128       gst_event_copy_segment (event, &self->subtitle_segment);
2129     } else {
2130       GstPad *peer;
2131       gboolean res = FALSE;
2132       gint64 tstart = 0;
2133       gint64 tstop = GST_CLOCK_TIME_NONE;
2134       gint64 tposition = 0;
2135       gint64 tduration = 0;
2136       gint64 ttime = 0;
2137       gint64 tbase = 0;
2138       GstFormat format = eventsegment->format;
2139
2140       GST_DEBUG_OBJECT (pad, "Subtitle newsegment event (%s) not in TIME "
2141           "format, converting", gst_format_get_name (format));
2142       peer = gst_pad_get_peer (pad);
2143
2144       if (peer) {
2145         res =
2146             _pad_query_convert_to_time (peer, format, eventsegment->start,
2147             &tstart);
2148         res = res
2149             && _pad_query_convert_to_time (peer, format, eventsegment->stop,
2150             &tstop);
2151         res = res
2152             && _pad_query_convert_to_time (peer, format, eventsegment->position,
2153             &tposition);
2154         res = res
2155             && _pad_query_convert_to_time (peer, format, eventsegment->base,
2156             &tbase);
2157         res = res
2158             && _pad_query_convert_to_time (peer, format, eventsegment->time,
2159             &ttime);
2160         res = res
2161             && _pad_query_convert_to_time (peer, format, eventsegment->duration,
2162             &tduration);
2163
2164         gst_object_unref (peer);
2165       }
2166
2167       if (!res) {
2168         tstart = 0;
2169         tstop = GST_CLOCK_TIME_NONE;
2170         tposition = 0;
2171         tduration = GST_CLOCK_TIME_NONE;
2172         ttime = 0;
2173         tbase = 0;
2174       }
2175
2176       gst_segment_init (&self->subtitle_segment, GST_FORMAT_TIME);
2177       self->subtitle_segment.start = tstart;
2178       self->subtitle_segment.stop = tstop;
2179       self->subtitle_segment.base = tbase;
2180       self->subtitle_segment.time = ttime;
2181       self->subtitle_segment.position = tposition;
2182       self->subtitle_segment.duration = tduration;
2183     }
2184     GST_DEBUG_OBJECT (pad, "New subtitle segment: %" GST_SEGMENT_FORMAT,
2185         &self->subtitle_segment);
2186   }
2187   gst_event_unref (event);
2188
2189 out:
2190   return ret;
2191 }
2192
2193 static gboolean
2194 gst_subtitle_overlay_subtitle_sink_query (GstPad * pad, GstObject * parent,
2195     GstQuery * query)
2196 {
2197   gboolean ret;
2198
2199   switch (GST_QUERY_TYPE (query)) {
2200     case GST_QUERY_ACCEPT_CAPS:
2201     {
2202       GstCaps *caps, *othercaps;
2203
2204       gst_query_parse_accept_caps (query, &caps);
2205       othercaps = gst_subtitle_overlay_subtitle_sink_getcaps (pad, NULL);
2206       ret = gst_caps_is_subset (caps, othercaps);
2207       gst_caps_unref (othercaps);
2208       gst_query_set_accept_caps_result (query, ret);
2209       ret = TRUE;
2210       break;
2211     }
2212     case GST_QUERY_CAPS:
2213     {
2214       GstCaps *filter, *caps;
2215
2216       gst_query_parse_caps (query, &filter);
2217       caps = gst_subtitle_overlay_subtitle_sink_getcaps (pad, filter);
2218       gst_query_set_caps_result (query, caps);
2219       gst_caps_unref (caps);
2220       ret = TRUE;
2221       break;
2222     }
2223     default:
2224       ret = gst_pad_query_default (pad, parent, query);
2225       break;
2226   }
2227
2228   return ret;
2229 }
2230
2231 static void
2232 gst_subtitle_overlay_init (GstSubtitleOverlay * self)
2233 {
2234   GstPadTemplate *templ;
2235   GstPad *proxypad = NULL;
2236
2237   self->lock = g_mutex_new ();
2238   self->factories_lock = g_mutex_new ();
2239
2240   templ = gst_static_pad_template_get (&srctemplate);
2241   self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
2242   gst_object_unref (templ);
2243
2244   proxypad =
2245       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->srcpad)));
2246   gst_pad_set_event_function (proxypad,
2247       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
2248   gst_pad_set_chain_function (proxypad,
2249       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
2250   gst_object_unref (proxypad);
2251
2252   gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
2253
2254   templ = gst_static_pad_template_get (&video_sinktemplate);
2255   self->video_sinkpad =
2256       gst_ghost_pad_new_no_target_from_template ("video_sink", templ);
2257   gst_object_unref (templ);
2258   gst_pad_set_event_function (self->video_sinkpad,
2259       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
2260   gst_pad_set_chain_function (self->video_sinkpad,
2261       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
2262
2263   proxypad =
2264       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2265           (self->video_sinkpad)));
2266   self->video_block_pad = proxypad;
2267   gst_object_unref (proxypad);
2268   gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad);
2269
2270   templ = gst_static_pad_template_get (&subtitle_sinktemplate);
2271   self->subtitle_sinkpad =
2272       gst_ghost_pad_new_no_target_from_template ("subtitle_sink", templ);
2273   gst_object_unref (templ);
2274   gst_pad_set_link_function (self->subtitle_sinkpad,
2275       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_link));
2276   gst_pad_set_unlink_function (self->subtitle_sinkpad,
2277       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_unlink));
2278   gst_pad_set_event_function (self->subtitle_sinkpad,
2279       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_event));
2280   gst_pad_set_query_function (self->subtitle_sinkpad,
2281       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_query));
2282   gst_pad_set_chain_function (self->subtitle_sinkpad,
2283       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain));
2284
2285   proxypad =
2286       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2287           (self->subtitle_sinkpad)));
2288   self->subtitle_block_pad = proxypad;
2289   gst_object_unref (proxypad);
2290
2291   gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad);
2292
2293   self->fps_n = 0;
2294   self->fps_d = 0;
2295 }
2296
2297 gboolean
2298 gst_subtitle_overlay_plugin_init (GstPlugin * plugin)
2299 {
2300   GST_DEBUG_CATEGORY_INIT (subtitle_overlay_debug, "subtitleoverlay", 0,
2301       "Subtitle Overlay");
2302
2303   _subtitle_overlay_event_marker_id =
2304       g_quark_from_static_string ("gst-subtitle-overlay-event-marker");
2305
2306   return gst_element_register (plugin, "subtitleoverlay", GST_RANK_NONE,
2307       GST_TYPE_SUBTITLE_OVERLAY);
2308 }