streamsynchronizer: do not leak EOS events
[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. ! "subpicture/x-dvd" ! 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 = gst_message_new_async_done (GST_OBJECT_CAST (self),
105         GST_CLOCK_TIME_NONE);
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     gst_util_set_object_arg (G_OBJECT (renderer), "halignment", "center");
883     gst_util_set_object_arg (G_OBJECT (renderer), "valignment", "bottom");
884     g_object_set (G_OBJECT (renderer), "wait-text", FALSE, NULL);
885     if (self->font_desc)
886       g_object_set (G_OBJECT (renderer), "font-desc", self->font_desc, NULL);
887     self->silent_property = "silent";
888     self->silent_property_invert = FALSE;
889   } else {
890     self->silent_property =
891         _get_silent_property (renderer, &self->silent_property_invert);
892     if (_has_property_with_type (G_OBJECT (renderer), "subtitle-encoding",
893             G_TYPE_STRING))
894       g_object_set (renderer, "subtitle-encoding", self->encoding, NULL);
895     if (_has_property_with_type (G_OBJECT (renderer), "font-desc",
896             G_TYPE_STRING))
897       g_object_set (renderer, "font-desc", self->font_desc, NULL);
898   }
899
900   return TRUE;
901 }
902
903 /* subtitle_src==NULL means: use subtitle_sink ghostpad */
904 static gboolean
905 _link_renderer (GstSubtitleOverlay * self, GstElement * renderer,
906     GstPad * subtitle_src)
907 {
908   GstPad *sink, *src;
909   gboolean is_video, is_hw;
910
911   is_video = _is_video_pad (self->video_sinkpad, &is_hw);
912
913   if (is_video) {
914     gboolean render_is_hw;
915
916     /* First check that renderer also supports the video format */
917     sink = _get_video_pad (renderer);
918     if (G_UNLIKELY (!sink)) {
919       GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
920       return FALSE;
921     }
922
923     if (is_video != _is_video_pad (sink, &render_is_hw) ||
924         is_hw != render_is_hw) {
925       GST_DEBUG_OBJECT (self, "Renderer doesn't support %s video",
926           is_hw ? "surface" : "raw");
927       gst_object_unref (sink);
928       return FALSE;
929     }
930     gst_object_unref (sink);
931
932     if (!is_hw) {
933       /* First link everything internally */
934       if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
935                   COLORSPACE, NULL, "post-colorspace", FALSE))) {
936         return FALSE;
937       }
938       src = gst_element_get_static_pad (renderer, "src");
939       if (G_UNLIKELY (!src)) {
940         GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
941         return FALSE;
942       }
943
944       sink = gst_element_get_static_pad (self->post_colorspace, "sink");
945       if (G_UNLIKELY (!sink)) {
946         GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
947         gst_object_unref (src);
948         return FALSE;
949       }
950
951       if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
952         GST_WARNING_OBJECT (self, "Can't link renderer with " COLORSPACE);
953         gst_object_unref (src);
954         gst_object_unref (sink);
955         return FALSE;
956       }
957       gst_object_unref (src);
958       gst_object_unref (sink);
959
960       if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
961                   COLORSPACE, NULL, "pre-colorspace", FALSE))) {
962         return FALSE;
963       }
964
965       sink = _get_video_pad (renderer);
966       if (G_UNLIKELY (!sink)) {
967         GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
968         return FALSE;
969       }
970
971       src = gst_element_get_static_pad (self->pre_colorspace, "src");
972       if (G_UNLIKELY (!src)) {
973         GST_WARNING_OBJECT (self, "Can't get srcpad from " COLORSPACE);
974         gst_object_unref (sink);
975         return FALSE;
976       }
977
978       if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
979         GST_WARNING_OBJECT (self, "Can't link " COLORSPACE " to renderer");
980         gst_object_unref (src);
981         gst_object_unref (sink);
982         return FALSE;
983       }
984       gst_object_unref (src);
985       gst_object_unref (sink);
986
987       /* Set src ghostpad target */
988       src = gst_element_get_static_pad (self->post_colorspace, "src");
989       if (G_UNLIKELY (!src)) {
990         GST_WARNING_OBJECT (self, "Can't get src pad from " COLORSPACE);
991         return FALSE;
992       }
993     } else {
994       /* Set src ghostpad target in the harware accelerated case */
995
996       src = gst_element_get_static_pad (renderer, "src");
997       if (G_UNLIKELY (!src)) {
998         GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
999         return FALSE;
1000       }
1001     }
1002   } else {                      /* No video pad */
1003     GstCaps *allowed_caps, *video_caps = NULL;
1004     GstPad *video_peer;
1005     gboolean can_intersect = FALSE;
1006
1007     video_peer = gst_pad_get_peer (self->video_sinkpad);
1008     if (video_peer) {
1009       video_caps = gst_pad_get_current_caps (video_peer);
1010       if (!video_caps) {
1011         video_caps = gst_pad_query_caps (video_peer, NULL);
1012       }
1013       gst_object_unref (video_peer);
1014     }
1015
1016     sink = _get_video_pad (renderer);
1017     if (G_UNLIKELY (!sink)) {
1018       GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
1019       return FALSE;
1020     }
1021     allowed_caps = gst_pad_query_caps (sink, NULL);
1022     gst_object_unref (sink);
1023
1024     if (allowed_caps && video_caps)
1025       can_intersect = gst_caps_can_intersect (allowed_caps, video_caps);
1026
1027     if (allowed_caps)
1028       gst_caps_unref (allowed_caps);
1029
1030     if (video_caps)
1031       gst_caps_unref (video_caps);
1032
1033     if (G_UNLIKELY (!can_intersect)) {
1034       GST_WARNING_OBJECT (self, "Renderer with custom caps is not "
1035           "compatible with video stream");
1036       return FALSE;
1037     }
1038
1039     src = gst_element_get_static_pad (renderer, "src");
1040     if (G_UNLIKELY (!src)) {
1041       GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
1042       return FALSE;
1043     }
1044   }
1045
1046   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1047               (self->srcpad), src))) {
1048     GST_WARNING_OBJECT (self, "Can't set srcpad target");
1049     gst_object_unref (src);
1050     return FALSE;
1051   }
1052   gst_object_unref (src);
1053
1054   /* Set the sink ghostpad targets */
1055   if (self->pre_colorspace) {
1056     sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
1057     if (G_UNLIKELY (!sink)) {
1058       GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
1059       return FALSE;
1060     }
1061   } else {
1062     sink = _get_video_pad (renderer);
1063     if (G_UNLIKELY (!sink)) {
1064       GST_WARNING_OBJECT (self, "Can't get sink pad from %" GST_PTR_FORMAT,
1065           renderer);
1066       return FALSE;
1067     }
1068   }
1069
1070   if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1071               (self->video_sinkpad), sink))) {
1072     GST_WARNING_OBJECT (self, "Can't set video sinkpad target");
1073     gst_object_unref (sink);
1074     return FALSE;
1075   }
1076   gst_object_unref (sink);
1077
1078   sink = _get_sub_pad (renderer);
1079   if (G_UNLIKELY (!sink)) {
1080     GST_WARNING_OBJECT (self, "Failed to get subpad");
1081     return FALSE;
1082   }
1083
1084   if (subtitle_src) {
1085     if (G_UNLIKELY (gst_pad_link (subtitle_src, sink) != GST_PAD_LINK_OK)) {
1086       GST_WARNING_OBJECT (self, "Failed to link subtitle srcpad with renderer");
1087       gst_object_unref (sink);
1088       return FALSE;
1089     }
1090   } else {
1091     if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1092                 (self->subtitle_sinkpad), sink))) {
1093       GST_WARNING_OBJECT (self, "Failed to set subtitle sink target");
1094       gst_object_unref (sink);
1095       return FALSE;
1096     }
1097   }
1098   gst_object_unref (sink);
1099
1100   return TRUE;
1101 }
1102
1103 static GstPadProbeReturn
1104 _pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1105 {
1106   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (user_data);
1107   GstCaps *subcaps;
1108   GList *l, *factories = NULL;
1109
1110   GST_DEBUG_OBJECT (pad, "Pad blocked");
1111
1112   GST_SUBTITLE_OVERLAY_LOCK (self);
1113   if (pad == self->video_block_pad)
1114     self->video_sink_blocked = TRUE;
1115   else if (pad == self->subtitle_block_pad)
1116     self->subtitle_sink_blocked = TRUE;
1117
1118   /* Now either both or the video sink are blocked */
1119
1120   /* Get current subtitle caps */
1121   subcaps = self->subcaps;
1122   if (!subcaps) {
1123     GstPad *peer;
1124
1125     peer = gst_pad_get_peer (self->subtitle_sinkpad);
1126     if (peer) {
1127       subcaps = gst_pad_get_current_caps (peer);
1128       if (!subcaps) {
1129         subcaps = gst_pad_query_caps (peer, NULL);
1130         if (!gst_caps_is_fixed (subcaps)) {
1131           gst_caps_unref (subcaps);
1132           subcaps = NULL;
1133         }
1134       }
1135       gst_object_unref (peer);
1136     }
1137     gst_caps_replace (&self->subcaps, subcaps);
1138     if (subcaps)
1139       gst_caps_unref (subcaps);
1140   }
1141   GST_DEBUG_OBJECT (self, "Current subtitle caps: %" GST_PTR_FORMAT, subcaps);
1142
1143   /* If there are no subcaps but the subtitle sink is blocked upstream
1144    * must behave wrong as there are no fixed caps set for the first
1145    * buffer or in-order event */
1146   if (G_UNLIKELY (!subcaps && self->subtitle_sink_blocked)) {
1147     GST_ELEMENT_WARNING (self, CORE, NEGOTIATION, (NULL),
1148         ("Subtitle sink is blocked but we have no subtitle caps"));
1149     subcaps = NULL;
1150   }
1151
1152   if (self->subtitle_error || (self->silent && !self->silent_property)) {
1153     _setup_passthrough (self);
1154     do_async_done (self);
1155     goto out;
1156   }
1157
1158   /* Now do something with the caps */
1159   if (subcaps && !self->subtitle_flush) {
1160     GstPad *target =
1161         gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad));
1162
1163     if (target && gst_pad_query_accept_caps (target, subcaps)) {
1164       GST_DEBUG_OBJECT (pad, "Target accepts caps");
1165
1166       gst_object_unref (target);
1167
1168       /* Unblock pads */
1169       unblock_video (self);
1170       unblock_subtitle (self);
1171       goto out;
1172     } else if (target) {
1173       gst_object_unref (target);
1174     }
1175   }
1176
1177   if (self->subtitle_sink_blocked && !self->video_sink_blocked) {
1178     GST_DEBUG_OBJECT (self, "Subtitle sink blocked but video not blocked");
1179     block_video (self);
1180     goto out;
1181   }
1182
1183   self->subtitle_flush = FALSE;
1184
1185   /* Find our factories */
1186   g_mutex_lock (self->factories_lock);
1187   gst_subtitle_overlay_update_factory_list (self);
1188   if (subcaps) {
1189     factories =
1190         gst_subtitle_overlay_get_factories_for_caps (self->factories, subcaps);
1191     if (!factories) {
1192       GstMessage *msg;
1193
1194       msg = gst_missing_decoder_message_new (GST_ELEMENT_CAST (self), subcaps);
1195       gst_element_post_message (GST_ELEMENT_CAST (self), msg);
1196       GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, (NULL),
1197           ("no suitable subtitle plugin found"));
1198       subcaps = NULL;
1199       self->subtitle_error = TRUE;
1200     }
1201   }
1202   g_mutex_unlock (self->factories_lock);
1203
1204   if (!subcaps) {
1205     _setup_passthrough (self);
1206     do_async_done (self);
1207     goto out;
1208   }
1209
1210   /* Now the interesting parts are done: subtitle overlaying! */
1211
1212   /* Sort the factories by rank */
1213   factories = g_list_sort (factories, (GCompareFunc) _sort_by_ranks);
1214
1215   for (l = factories; l; l = l->next) {
1216     GstElementFactory *factory = l->data;
1217     gboolean is_renderer = _is_renderer (factory);
1218     GstPad *sink, *src;
1219
1220     /* Unlink & destroy everything */
1221
1222     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1223     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad), NULL);
1224     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1225         NULL);
1226     self->silent_property = NULL;
1227     _remove_element (self, &self->post_colorspace);
1228     _remove_element (self, &self->overlay);
1229     _remove_element (self, &self->parser);
1230     _remove_element (self, &self->renderer);
1231     _remove_element (self, &self->pre_colorspace);
1232     _remove_element (self, &self->passthrough_identity);
1233
1234     GST_DEBUG_OBJECT (self, "Trying factory '%s'",
1235         GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1236                 (factory))));
1237
1238     if (G_UNLIKELY ((is_renderer
1239                 && !_create_element (self, &self->renderer, NULL, factory,
1240                     "renderer", FALSE)) || (!is_renderer
1241                 && !_create_element (self, &self->parser, NULL, factory,
1242                     "parser", FALSE))))
1243       continue;
1244
1245     if (!is_renderer) {
1246       GstCaps *parser_caps;
1247       GList *overlay_factories, *k;
1248
1249       if (!_setup_parser (self))
1250         continue;
1251
1252       /* Find our factories */
1253       src = gst_element_get_static_pad (self->parser, "src");
1254       parser_caps = gst_pad_query_caps (src, NULL);
1255       gst_object_unref (src);
1256
1257       g_assert (parser_caps != NULL);
1258
1259       g_mutex_lock (self->factories_lock);
1260       gst_subtitle_overlay_update_factory_list (self);
1261       GST_DEBUG_OBJECT (self,
1262           "Searching overlay factories for caps %" GST_PTR_FORMAT, parser_caps);
1263       overlay_factories =
1264           gst_subtitle_overlay_get_factories_for_caps (self->factories,
1265           parser_caps);
1266       g_mutex_unlock (self->factories_lock);
1267
1268       if (!overlay_factories) {
1269         GST_WARNING_OBJECT (self,
1270             "Found no suitable overlay factories for caps %" GST_PTR_FORMAT,
1271             parser_caps);
1272         gst_caps_unref (parser_caps);
1273         continue;
1274       }
1275       gst_caps_unref (parser_caps);
1276
1277       /* Sort the factories by rank */
1278       overlay_factories =
1279           g_list_sort (overlay_factories, (GCompareFunc) _sort_by_ranks);
1280
1281       for (k = overlay_factories; k; k = k->next) {
1282         GstElementFactory *overlay_factory = k->data;
1283
1284         GST_DEBUG_OBJECT (self, "Trying overlay factory '%s'",
1285             GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1286                     (overlay_factory))));
1287
1288         /* Try this factory and link it, otherwise unlink everything
1289          * again and remove the overlay. Up to this point only the
1290          * parser was instantiated and setup, nothing was linked
1291          */
1292
1293         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1294         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad),
1295             NULL);
1296         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1297             NULL);
1298         self->silent_property = NULL;
1299         _remove_element (self, &self->post_colorspace);
1300         _remove_element (self, &self->overlay);
1301         _remove_element (self, &self->pre_colorspace);
1302
1303         if (!_create_element (self, &self->overlay, NULL, overlay_factory,
1304                 "overlay", FALSE)) {
1305           GST_DEBUG_OBJECT (self, "Could not create element");
1306           continue;
1307         }
1308
1309         if (!_setup_renderer (self, self->overlay)) {
1310           GST_DEBUG_OBJECT (self, "Could not setup element");
1311           continue;
1312         }
1313
1314         src = gst_element_get_static_pad (self->parser, "src");
1315         if (!_link_renderer (self, self->overlay, src)) {
1316           GST_DEBUG_OBJECT (self, "Could not link element");
1317           gst_object_unref (src);
1318           continue;
1319         }
1320         gst_object_unref (src);
1321
1322         /* Everything done here, go out of loop */
1323         GST_DEBUG_OBJECT (self, "%s is a suitable element",
1324             GST_STR_NULL (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST
1325                     (overlay_factory))));
1326         break;
1327       }
1328
1329       if (overlay_factories)
1330         gst_plugin_feature_list_free (overlay_factories);
1331
1332       if (G_UNLIKELY (k == NULL)) {
1333         GST_WARNING_OBJECT (self, "Failed to find usable overlay factory");
1334         continue;
1335       }
1336
1337       /* Now link subtitle sinkpad of the bin and the parser */
1338       sink = gst_element_get_static_pad (self->parser, "sink");
1339       if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
1340               (self->subtitle_sinkpad), sink)) {
1341         gst_object_unref (sink);
1342         continue;
1343       }
1344       gst_object_unref (sink);
1345
1346       /* Everything done here, go out of loop */
1347       break;
1348     } else {
1349       /* Is renderer factory */
1350
1351       if (!_setup_renderer (self, self->renderer))
1352         continue;
1353
1354       /* subtitle_src==NULL means: use subtitle_sink ghostpad */
1355       if (!_link_renderer (self, self->renderer, NULL))
1356         continue;
1357
1358       /* Everything done here, go out of loop */
1359       break;
1360     }
1361   }
1362
1363   if (G_UNLIKELY (l == NULL)) {
1364     GST_ELEMENT_WARNING (self, CORE, FAILED, (NULL),
1365         ("Failed to find any usable factories"));
1366     self->subtitle_error = TRUE;
1367     _setup_passthrough (self);
1368     do_async_done (self);
1369     goto out;
1370   }
1371
1372   /* Send segments to the renderer if necessary. These are not sent
1373    * outside this element because of the proxy pad event handler */
1374   if (self->video_segment.format != GST_FORMAT_UNDEFINED) {
1375     GstEvent *event1;
1376     GstPad *sink;
1377
1378     if (self->pre_colorspace) {
1379       sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
1380     } else {
1381       sink = _get_video_pad ((self->renderer) ? self->renderer : self->overlay);
1382     }
1383
1384     _generate_update_segment_event (sink, &self->video_segment, &event1);
1385     GST_DEBUG_OBJECT (self,
1386         "Pushing video update segment event: %" GST_PTR_FORMAT,
1387         gst_event_get_structure (event1));
1388     gst_pad_send_event (sink, event1);
1389     gst_object_unref (sink);
1390   }
1391
1392   if (self->subtitle_segment.format != GST_FORMAT_UNDEFINED) {
1393     GstEvent *event1;
1394     GstPad *sink;
1395
1396     if (self->renderer)
1397       sink = _get_sub_pad (self->renderer);
1398     else
1399       sink = gst_element_get_static_pad (self->parser, "sink");
1400
1401     _generate_update_segment_event (sink, &self->subtitle_segment, &event1);
1402     GST_DEBUG_OBJECT (self,
1403         "Pushing subtitle update segment event: %" GST_PTR_FORMAT,
1404         gst_event_get_structure (event1));
1405     gst_pad_send_event (sink, event1);
1406     gst_object_unref (sink);
1407   }
1408
1409   GST_DEBUG_OBJECT (self, "Everything worked, unblocking pads");
1410   unblock_video (self);
1411   unblock_subtitle (self);
1412   do_async_done (self);
1413
1414 out:
1415   if (factories)
1416     gst_plugin_feature_list_free (factories);
1417   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1418
1419   return GST_PAD_PROBE_OK;
1420 }
1421
1422 static GstStateChangeReturn
1423 gst_subtitle_overlay_change_state (GstElement * element,
1424     GstStateChange transition)
1425 {
1426   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (element);
1427   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1428
1429   switch (transition) {
1430     case GST_STATE_CHANGE_NULL_TO_READY:
1431       GST_DEBUG_OBJECT (self, "State change NULL->READY");
1432       g_mutex_lock (self->factories_lock);
1433       if (G_UNLIKELY (!gst_subtitle_overlay_update_factory_list (self))) {
1434         g_mutex_unlock (self->factories_lock);
1435         return GST_STATE_CHANGE_FAILURE;
1436       }
1437       g_mutex_unlock (self->factories_lock);
1438
1439       GST_SUBTITLE_OVERLAY_LOCK (self);
1440       /* Set the internal pads to blocking */
1441       block_video (self);
1442       block_subtitle (self);
1443       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1444       break;
1445     case GST_STATE_CHANGE_READY_TO_PAUSED:
1446       GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
1447       gst_segment_init (&self->video_segment, GST_FORMAT_UNDEFINED);
1448       gst_segment_init (&self->subtitle_segment, GST_FORMAT_UNDEFINED);
1449
1450       self->fps_n = self->fps_d = 0;
1451
1452       self->subtitle_flush = FALSE;
1453       self->subtitle_error = FALSE;
1454
1455       self->downstream_chain_error = FALSE;
1456
1457       do_async_start (self);
1458       ret = GST_STATE_CHANGE_ASYNC;
1459
1460       break;
1461     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1462       GST_DEBUG_OBJECT (self, "State change PAUSED->PLAYING");
1463     default:
1464       break;
1465   }
1466
1467   {
1468     GstStateChangeReturn bret;
1469
1470     bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1471     GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", bret);
1472     if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
1473       return ret;
1474     else if (bret == GST_STATE_CHANGE_ASYNC)
1475       ret = bret;
1476     else if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
1477       do_async_done (self);
1478       ret = bret;
1479     }
1480   }
1481
1482   switch (transition) {
1483     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1484       GST_DEBUG_OBJECT (self, "State change PLAYING->PAUSED");
1485       break;
1486     case GST_STATE_CHANGE_PAUSED_TO_READY:
1487       GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
1488
1489       /* Set the pads back to blocking state */
1490       GST_SUBTITLE_OVERLAY_LOCK (self);
1491       block_video (self);
1492       block_subtitle (self);
1493       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1494
1495       do_async_done (self);
1496
1497       break;
1498     case GST_STATE_CHANGE_READY_TO_NULL:
1499       GST_DEBUG_OBJECT (self, "State change READY->NULL");
1500
1501       GST_SUBTITLE_OVERLAY_LOCK (self);
1502       gst_caps_replace (&self->subcaps, NULL);
1503
1504       /* Unlink ghost pads */
1505       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1506       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->video_sinkpad), NULL);
1507       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad),
1508           NULL);
1509
1510       /* Unblock pads */
1511       unblock_video (self);
1512       unblock_subtitle (self);
1513
1514       /* Remove elements */
1515       self->silent_property = NULL;
1516       _remove_element (self, &self->post_colorspace);
1517       _remove_element (self, &self->overlay);
1518       _remove_element (self, &self->parser);
1519       _remove_element (self, &self->renderer);
1520       _remove_element (self, &self->pre_colorspace);
1521       _remove_element (self, &self->passthrough_identity);
1522       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1523
1524       break;
1525     default:
1526       break;
1527   }
1528
1529   return ret;
1530 }
1531
1532 static void
1533 gst_subtitle_overlay_handle_message (GstBin * bin, GstMessage * message)
1534 {
1535   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (bin);
1536
1537   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
1538     GstObject *src = GST_MESSAGE_SRC (message);
1539
1540     /* Convert error messages from the subtitle pipeline to
1541      * warnings and switch to passthrough mode */
1542     if (src && (
1543             (self->overlay
1544                 && gst_object_has_ancestor (src,
1545                     GST_OBJECT_CAST (self->overlay))) || (self->parser
1546                 && gst_object_has_ancestor (src,
1547                     GST_OBJECT_CAST (self->parser))) || (self->renderer
1548                 && gst_object_has_ancestor (src,
1549                     GST_OBJECT_CAST (self->renderer))))) {
1550       GError *err = NULL;
1551       gchar *debug = NULL;
1552       GstMessage *wmsg;
1553
1554       gst_message_parse_error (message, &err, &debug);
1555       GST_DEBUG_OBJECT (self,
1556           "Got error message from subtitle element %s: %s (%s)",
1557           GST_MESSAGE_SRC_NAME (message), GST_STR_NULL (err->message),
1558           GST_STR_NULL (debug));
1559
1560       wmsg = gst_message_new_warning (src, err, debug);
1561       gst_message_unref (message);
1562       g_error_free (err);
1563       g_free (debug);
1564       message = wmsg;
1565
1566       GST_SUBTITLE_OVERLAY_LOCK (self);
1567       self->subtitle_error = TRUE;
1568
1569       block_subtitle (self);
1570       block_video (self);
1571       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1572     }
1573   }
1574
1575   GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1576 }
1577
1578 static void
1579 gst_subtitle_overlay_get_property (GObject * object, guint prop_id,
1580     GValue * value, GParamSpec * pspec)
1581 {
1582   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (object);
1583
1584   switch (prop_id) {
1585     case PROP_SILENT:
1586       g_value_set_boolean (value, self->silent);
1587       break;
1588     case PROP_FONT_DESC:
1589       GST_SUBTITLE_OVERLAY_LOCK (self);
1590       g_value_set_string (value, self->font_desc);
1591       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1592       break;
1593     case PROP_SUBTITLE_ENCODING:
1594       GST_SUBTITLE_OVERLAY_LOCK (self);
1595       g_value_set_string (value, self->encoding);
1596       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1597       break;
1598     default:
1599       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1600       break;
1601   }
1602 }
1603
1604 static void
1605 gst_subtitle_overlay_set_property (GObject * object, guint prop_id,
1606     const GValue * value, GParamSpec * pspec)
1607 {
1608   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY_CAST (object);
1609
1610   switch (prop_id) {
1611     case PROP_SILENT:
1612       GST_SUBTITLE_OVERLAY_LOCK (self);
1613       self->silent = g_value_get_boolean (value);
1614       if (self->silent_property) {
1615         gboolean silent = self->silent;
1616
1617         if (self->silent_property_invert)
1618           silent = !silent;
1619
1620         if (self->overlay)
1621           g_object_set (self->overlay, self->silent_property, silent, NULL);
1622         else if (self->renderer)
1623           g_object_set (self->renderer, self->silent_property, silent, NULL);
1624       } else {
1625         block_subtitle (self);
1626         block_video (self);
1627       }
1628       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1629       break;
1630     case PROP_FONT_DESC:
1631       GST_SUBTITLE_OVERLAY_LOCK (self);
1632       g_free (self->font_desc);
1633       self->font_desc = g_value_dup_string (value);
1634       if (self->overlay
1635           && _has_property_with_type (G_OBJECT (self->overlay), "font-desc",
1636               G_TYPE_STRING))
1637         g_object_set (self->overlay, "font-desc", self->font_desc, NULL);
1638       else if (self->renderer
1639           && _has_property_with_type (G_OBJECT (self->renderer), "font-desc",
1640               G_TYPE_STRING))
1641         g_object_set (self->renderer, "font-desc", self->font_desc, NULL);
1642       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1643       break;
1644     case PROP_SUBTITLE_ENCODING:
1645       GST_SUBTITLE_OVERLAY_LOCK (self);
1646       g_free (self->encoding);
1647       self->encoding = g_value_dup_string (value);
1648       if (self->renderer
1649           && _has_property_with_type (G_OBJECT (self->renderer),
1650               "subtitle-encoding", G_TYPE_STRING))
1651         g_object_set (self->renderer, "subtitle-encoding", self->encoding,
1652             NULL);
1653       if (self->parser
1654           && _has_property_with_type (G_OBJECT (self->parser),
1655               "subtitle-encoding", G_TYPE_STRING))
1656         g_object_set (self->parser, "subtitle-encoding", self->encoding, NULL);
1657       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1658       break;
1659     default:
1660       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1661       break;
1662   }
1663 }
1664
1665 static void
1666 gst_subtitle_overlay_class_init (GstSubtitleOverlayClass * klass)
1667 {
1668   GObjectClass *gobject_class = (GObjectClass *) klass;
1669   GstElementClass *element_class = (GstElementClass *) klass;
1670   GstBinClass *bin_class = (GstBinClass *) klass;
1671
1672   gobject_class->finalize = gst_subtitle_overlay_finalize;
1673   gobject_class->set_property = gst_subtitle_overlay_set_property;
1674   gobject_class->get_property = gst_subtitle_overlay_get_property;
1675
1676   g_object_class_install_property (gobject_class, PROP_SILENT,
1677       g_param_spec_boolean ("silent",
1678           "Silent",
1679           "Whether to show subtitles", FALSE,
1680           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1681
1682   g_object_class_install_property (gobject_class, PROP_FONT_DESC,
1683       g_param_spec_string ("font-desc",
1684           "Subtitle font description",
1685           "Pango font description of font "
1686           "to be used for subtitle rendering", NULL,
1687           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1688
1689   g_object_class_install_property (gobject_class, PROP_SUBTITLE_ENCODING,
1690       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
1691           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
1692           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
1693           "be checked for an encoding to use. If that is not set either, "
1694           "ISO-8859-15 will be assumed.", NULL,
1695           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1696
1697   gst_element_class_add_pad_template (element_class,
1698       gst_static_pad_template_get (&srctemplate));
1699
1700   gst_element_class_add_pad_template (element_class,
1701       gst_static_pad_template_get (&video_sinktemplate));
1702   gst_element_class_add_pad_template (element_class,
1703       gst_static_pad_template_get (&subtitle_sinktemplate));
1704
1705   gst_element_class_set_static_metadata (element_class, "Subtitle Overlay",
1706       "Video/Overlay/Subtitle",
1707       "Overlays a video stream with subtitles",
1708       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1709
1710   element_class->change_state =
1711       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_change_state);
1712
1713   bin_class->handle_message =
1714       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_handle_message);
1715 }
1716
1717 static GstFlowReturn
1718 gst_subtitle_overlay_src_proxy_chain (GstPad * proxypad, GstObject * parent,
1719     GstBuffer * buffer)
1720 {
1721   GstPad *ghostpad;
1722   GstSubtitleOverlay *self;
1723   GstFlowReturn ret;
1724
1725   ghostpad = GST_PAD_CAST (parent);
1726   if (G_UNLIKELY (!ghostpad)) {
1727     gst_buffer_unref (buffer);
1728     return GST_FLOW_ERROR;
1729   }
1730   self = GST_SUBTITLE_OVERLAY_CAST (gst_pad_get_parent (ghostpad));
1731   if (G_UNLIKELY (!self || self->srcpad != ghostpad)) {
1732     gst_buffer_unref (buffer);
1733     gst_object_unref (ghostpad);
1734     return GST_FLOW_ERROR;
1735   }
1736
1737   ret = gst_proxy_pad_chain_default (proxypad, parent, buffer);
1738
1739   if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
1740     GST_ERROR_OBJECT (self, "Downstream chain error: %s",
1741         gst_flow_get_name (ret));
1742     self->downstream_chain_error = TRUE;
1743   }
1744
1745   gst_object_unref (self);
1746
1747   return ret;
1748 }
1749
1750 static gboolean
1751 gst_subtitle_overlay_src_proxy_event (GstPad * proxypad, GstObject * parent,
1752     GstEvent * event)
1753 {
1754   GstPad *ghostpad = NULL;
1755   GstSubtitleOverlay *self = NULL;
1756   gboolean ret = FALSE;
1757   const GstStructure *s;
1758
1759   ghostpad = GST_PAD_CAST (parent);
1760   if (G_UNLIKELY (!ghostpad))
1761     goto out;
1762   self = GST_SUBTITLE_OVERLAY_CAST (gst_pad_get_parent (ghostpad));
1763   if (G_UNLIKELY (!self || self->srcpad != ghostpad))
1764     goto out;
1765
1766   s = gst_event_get_structure (event);
1767   if (s && gst_structure_id_has_field (s, _subtitle_overlay_event_marker_id)) {
1768     GST_DEBUG_OBJECT (ghostpad,
1769         "Dropping event with marker: %" GST_PTR_FORMAT,
1770         gst_event_get_structure (event));
1771     gst_event_unref (event);
1772     event = NULL;
1773     ret = TRUE;
1774   } else {
1775     ret = gst_pad_event_default (proxypad, parent, event);
1776     event = NULL;
1777   }
1778
1779 out:
1780   if (event)
1781     gst_event_unref (event);
1782   if (self)
1783     gst_object_unref (self);
1784
1785   return ret;
1786 }
1787
1788 static gboolean
1789 gst_subtitle_overlay_video_sink_setcaps (GstSubtitleOverlay * self,
1790     GstCaps * caps)
1791 {
1792   GstPad *target;
1793   gboolean ret = TRUE;
1794   GstVideoInfo info;
1795
1796   GST_DEBUG_OBJECT (self, "Setting caps: %" GST_PTR_FORMAT, caps);
1797
1798   if (!gst_video_info_from_caps (&info, caps)) {
1799     GST_ERROR_OBJECT (self, "Failed to parse caps");
1800     ret = FALSE;
1801     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1802     goto out;
1803   }
1804
1805   target = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->video_sinkpad));
1806
1807   GST_SUBTITLE_OVERLAY_LOCK (self);
1808
1809   if (!target || !gst_pad_query_accept_caps (target, caps)) {
1810     GST_DEBUG_OBJECT (target, "Target did not accept caps -- reconfiguring");
1811
1812     block_subtitle (self);
1813     block_video (self);
1814   }
1815
1816   if (self->fps_n != info.fps_n || self->fps_d != info.fps_d) {
1817     GST_DEBUG_OBJECT (self, "New video fps: %d/%d", info.fps_n, info.fps_d);
1818     self->fps_n = info.fps_n;
1819     self->fps_d = info.fps_d;
1820     gst_subtitle_overlay_set_fps (self);
1821   }
1822   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1823
1824   if (target)
1825     gst_object_unref (target);
1826
1827 out:
1828
1829   return ret;
1830 }
1831
1832 static gboolean
1833 gst_subtitle_overlay_video_sink_event (GstPad * pad, GstObject * parent,
1834     GstEvent * event)
1835 {
1836   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1837   gboolean ret;
1838
1839   switch (GST_EVENT_TYPE (event)) {
1840     case GST_EVENT_FLUSH_STOP:
1841     {
1842       GST_DEBUG_OBJECT (pad,
1843           "Resetting video segment because of flush-stop event");
1844       gst_segment_init (&self->video_segment, GST_FORMAT_UNDEFINED);
1845       self->fps_n = self->fps_d = 0;
1846       break;
1847     }
1848     case GST_EVENT_CAPS:
1849     {
1850       GstCaps *caps;
1851
1852       gst_event_parse_caps (event, &caps);
1853       ret = gst_subtitle_overlay_video_sink_setcaps (self, caps);
1854       if (!ret)
1855         goto done;
1856       break;
1857     }
1858     default:
1859       break;
1860   }
1861
1862   ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1863
1864   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1865     GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
1866     gst_event_copy_segment (event, &self->video_segment);
1867
1868     if (self->video_segment.format != GST_FORMAT_TIME)
1869       goto invalid_format;
1870   }
1871
1872 done:
1873   gst_event_unref (event);
1874
1875   return ret;
1876
1877   /* ERRORS */
1878 invalid_format:
1879   {
1880     GST_ERROR_OBJECT (pad, "Segment event in non-time format: %s",
1881         gst_format_get_name (self->video_segment.format));
1882     ret = FALSE;
1883     goto done;
1884   }
1885 }
1886
1887 static GstFlowReturn
1888 gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstObject * parent,
1889     GstBuffer * buffer)
1890 {
1891   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1892   GstFlowReturn ret = gst_proxy_pad_chain_default (pad, parent, buffer);
1893
1894   if (G_UNLIKELY (self->downstream_chain_error) || self->passthrough_identity) {
1895     return ret;
1896   } else if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
1897     GST_DEBUG_OBJECT (self, "Subtitle renderer produced chain error: %s",
1898         gst_flow_get_name (ret));
1899     GST_SUBTITLE_OVERLAY_LOCK (self);
1900     self->subtitle_error = TRUE;
1901     block_subtitle (self);
1902     block_video (self);
1903     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1904
1905     return GST_FLOW_OK;
1906   }
1907
1908   return ret;
1909 }
1910
1911 static GstFlowReturn
1912 gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstObject * parent,
1913     GstBuffer * buffer)
1914 {
1915   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
1916
1917   if (self->subtitle_error) {
1918     gst_buffer_unref (buffer);
1919     return GST_FLOW_OK;
1920   } else {
1921     GstFlowReturn ret = gst_proxy_pad_chain_default (pad, parent, buffer);
1922
1923     if (IS_SUBTITLE_CHAIN_IGNORE_ERROR (ret)) {
1924       GST_DEBUG_OBJECT (self, "Subtitle chain error: %s",
1925           gst_flow_get_name (ret));
1926       GST_SUBTITLE_OVERLAY_LOCK (self);
1927       self->subtitle_error = TRUE;
1928       block_subtitle (self);
1929       block_video (self);
1930       GST_SUBTITLE_OVERLAY_UNLOCK (self);
1931
1932       return GST_FLOW_OK;
1933     }
1934
1935     return ret;
1936   }
1937 }
1938
1939 static GstCaps *
1940 gst_subtitle_overlay_subtitle_sink_getcaps (GstPad * pad, GstCaps * filter)
1941 {
1942   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (gst_pad_get_parent (pad));
1943   GstCaps *ret;
1944
1945   g_mutex_lock (self->factories_lock);
1946   if (G_UNLIKELY (!gst_subtitle_overlay_update_factory_list (self)))
1947     ret = gst_caps_new_empty ();
1948   else if (filter)
1949     ret =
1950         gst_caps_intersect_full (filter, self->factory_caps,
1951         GST_CAPS_INTERSECT_FIRST);
1952   else
1953     ret = gst_caps_ref (self->factory_caps);
1954   g_mutex_unlock (self->factories_lock);
1955
1956   GST_DEBUG_OBJECT (pad, "Returning subtitle caps %" GST_PTR_FORMAT, ret);
1957
1958   gst_object_unref (self);
1959
1960   return ret;
1961 }
1962
1963 static gboolean
1964 gst_subtitle_overlay_subtitle_sink_setcaps (GstSubtitleOverlay * self,
1965     GstCaps * caps)
1966 {
1967   gboolean ret = TRUE;
1968   GstPad *target = NULL;;
1969
1970   GST_DEBUG_OBJECT (self, "Setting caps: %" GST_PTR_FORMAT, caps);
1971
1972   target =
1973       gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->subtitle_sinkpad));
1974
1975   GST_SUBTITLE_OVERLAY_LOCK (self);
1976   gst_caps_replace (&self->subcaps, caps);
1977
1978   if (target && gst_pad_query_accept_caps (target, caps)) {
1979     GST_DEBUG_OBJECT (self, "Target accepts caps");
1980     GST_SUBTITLE_OVERLAY_UNLOCK (self);
1981     goto out;
1982   }
1983
1984   GST_DEBUG_OBJECT (self, "Target did not accept caps");
1985
1986   self->subtitle_error = FALSE;
1987   block_subtitle (self);
1988   block_video (self);
1989   GST_SUBTITLE_OVERLAY_UNLOCK (self);
1990
1991 out:
1992   if (target)
1993     gst_object_unref (target);
1994
1995   return ret;
1996 }
1997
1998 static GstPadLinkReturn
1999 gst_subtitle_overlay_subtitle_sink_link (GstPad * pad, GstPad * peer)
2000 {
2001   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (gst_pad_get_parent (pad));
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   gst_object_unref (self);
2029
2030   return GST_PAD_LINK_OK;
2031 }
2032
2033 static void
2034 gst_subtitle_overlay_subtitle_sink_unlink (GstPad * pad)
2035 {
2036   GstSubtitleOverlay *self =
2037       GST_SUBTITLE_OVERLAY (gst_object_ref (GST_PAD_PARENT (pad)));
2038
2039   /* FIXME: Can't use gst_pad_get_parent() here because this is called with
2040    * the object lock from state changes
2041    */
2042
2043   GST_DEBUG_OBJECT (pad, "Pad unlinking");
2044   gst_caps_replace (&self->subcaps, NULL);
2045
2046   GST_SUBTITLE_OVERLAY_LOCK (self);
2047   self->subtitle_error = FALSE;
2048
2049   block_subtitle (self);
2050   block_video (self);
2051   GST_SUBTITLE_OVERLAY_UNLOCK (self);
2052
2053   gst_object_unref (self);
2054 }
2055
2056 static gboolean
2057 gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstObject * parent,
2058     GstEvent * event)
2059 {
2060   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (parent);
2061   gboolean ret;
2062
2063   GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2064
2065   if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB &&
2066       gst_event_has_name (event, "playsink-custom-subtitle-flush")) {
2067     GST_DEBUG_OBJECT (pad, "Custom subtitle flush event");
2068     GST_SUBTITLE_OVERLAY_LOCK (self);
2069     self->subtitle_flush = TRUE;
2070     self->subtitle_error = FALSE;
2071     block_subtitle (self);
2072     block_video (self);
2073     GST_SUBTITLE_OVERLAY_UNLOCK (self);
2074
2075     gst_event_unref (event);
2076     event = NULL;
2077     ret = TRUE;
2078     goto out;
2079   }
2080
2081   switch (GST_EVENT_TYPE (event)) {
2082     case GST_EVENT_CAPS:
2083     {
2084       GstCaps *caps;
2085
2086       gst_event_parse_caps (event, &caps);
2087       ret = gst_subtitle_overlay_subtitle_sink_setcaps (self, caps);
2088       if (!ret)
2089         goto out;
2090       break;
2091     }
2092     case GST_EVENT_FLUSH_STOP:
2093       GST_DEBUG_OBJECT (pad,
2094           "Resetting subtitle segment because of flush-stop");
2095       gst_segment_init (&self->subtitle_segment, GST_FORMAT_UNDEFINED);
2096       /* fall through */
2097     case GST_EVENT_FLUSH_START:
2098     case GST_EVENT_SEGMENT:
2099     case GST_EVENT_EOS:
2100     {
2101       GstStructure *structure;
2102
2103       /* Add our event marker to make sure no events from here go ever outside
2104        * the element, they're only interesting for our internal elements */
2105       event = GST_EVENT_CAST (gst_event_make_writable (event));
2106       structure = gst_event_writable_structure (event);
2107
2108       gst_structure_id_set (structure, _subtitle_overlay_event_marker_id,
2109           G_TYPE_BOOLEAN, TRUE, NULL);
2110       break;
2111     }
2112     default:
2113       break;
2114   }
2115
2116   ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2117
2118   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
2119     const GstSegment *eventsegment;
2120     GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
2121
2122     gst_event_parse_segment (event, &eventsegment);
2123
2124     if (eventsegment->format == GST_FORMAT_TIME) {
2125       gst_event_copy_segment (event, &self->subtitle_segment);
2126     } else {
2127       GstPad *peer;
2128       gboolean res = FALSE;
2129       gint64 tstart = 0;
2130       gint64 tstop = GST_CLOCK_TIME_NONE;
2131       gint64 tposition = 0;
2132       gint64 tduration = 0;
2133       gint64 ttime = 0;
2134       gint64 tbase = 0;
2135       GstFormat format = eventsegment->format;
2136
2137       GST_DEBUG_OBJECT (pad, "Subtitle newsegment event (%s) not in TIME "
2138           "format, converting", gst_format_get_name (format));
2139       peer = gst_pad_get_peer (pad);
2140
2141       if (peer) {
2142         res =
2143             _pad_query_convert_to_time (peer, format, eventsegment->start,
2144             &tstart);
2145         res = res
2146             && _pad_query_convert_to_time (peer, format, eventsegment->stop,
2147             &tstop);
2148         res = res
2149             && _pad_query_convert_to_time (peer, format, eventsegment->position,
2150             &tposition);
2151         res = res
2152             && _pad_query_convert_to_time (peer, format, eventsegment->base,
2153             &tbase);
2154         res = res
2155             && _pad_query_convert_to_time (peer, format, eventsegment->time,
2156             &ttime);
2157         res = res
2158             && _pad_query_convert_to_time (peer, format, eventsegment->duration,
2159             &tduration);
2160
2161         gst_object_unref (peer);
2162       }
2163
2164       if (!res) {
2165         tstart = 0;
2166         tstop = GST_CLOCK_TIME_NONE;
2167         tposition = 0;
2168         tduration = GST_CLOCK_TIME_NONE;
2169         ttime = 0;
2170         tbase = 0;
2171       }
2172
2173       gst_segment_init (&self->subtitle_segment, GST_FORMAT_TIME);
2174       self->subtitle_segment.start = tstart;
2175       self->subtitle_segment.stop = tstop;
2176       self->subtitle_segment.base = tbase;
2177       self->subtitle_segment.time = ttime;
2178       self->subtitle_segment.position = tposition;
2179       self->subtitle_segment.duration = tduration;
2180     }
2181     GST_DEBUG_OBJECT (pad, "New subtitle segment: %" GST_SEGMENT_FORMAT,
2182         &self->subtitle_segment);
2183   }
2184   gst_event_unref (event);
2185
2186 out:
2187   return ret;
2188 }
2189
2190 static gboolean
2191 gst_subtitle_overlay_subtitle_sink_query (GstPad * pad, GstObject * parent,
2192     GstQuery * query)
2193 {
2194   gboolean ret;
2195
2196   switch (GST_QUERY_TYPE (query)) {
2197     case GST_QUERY_ACCEPT_CAPS:
2198     {
2199       GstCaps *caps, *othercaps;
2200
2201       gst_query_parse_accept_caps (query, &caps);
2202       othercaps = gst_subtitle_overlay_subtitle_sink_getcaps (pad, NULL);
2203       ret = gst_caps_is_subset (caps, othercaps);
2204       gst_caps_unref (othercaps);
2205       gst_query_set_accept_caps_result (query, ret);
2206       ret = TRUE;
2207       break;
2208     }
2209     case GST_QUERY_CAPS:
2210     {
2211       GstCaps *filter, *caps;
2212
2213       gst_query_parse_caps (query, &filter);
2214       caps = gst_subtitle_overlay_subtitle_sink_getcaps (pad, filter);
2215       gst_query_set_caps_result (query, caps);
2216       gst_caps_unref (caps);
2217       ret = TRUE;
2218       break;
2219     }
2220     default:
2221       ret = gst_pad_query_default (pad, parent, query);
2222       break;
2223   }
2224
2225   return ret;
2226 }
2227
2228 static void
2229 gst_subtitle_overlay_init (GstSubtitleOverlay * self)
2230 {
2231   GstPadTemplate *templ;
2232   GstPad *proxypad = NULL;
2233
2234   self->lock = g_mutex_new ();
2235   self->factories_lock = g_mutex_new ();
2236
2237   templ = gst_static_pad_template_get (&srctemplate);
2238   self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
2239   gst_object_unref (templ);
2240
2241   proxypad =
2242       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->srcpad)));
2243   gst_pad_set_event_function (proxypad,
2244       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
2245   gst_pad_set_chain_function (proxypad,
2246       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
2247   gst_object_unref (proxypad);
2248
2249   gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
2250
2251   templ = gst_static_pad_template_get (&video_sinktemplate);
2252   self->video_sinkpad =
2253       gst_ghost_pad_new_no_target_from_template ("video_sink", templ);
2254   gst_object_unref (templ);
2255   gst_pad_set_event_function (self->video_sinkpad,
2256       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
2257   gst_pad_set_chain_function (self->video_sinkpad,
2258       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
2259
2260   proxypad =
2261       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2262           (self->video_sinkpad)));
2263   self->video_block_pad = proxypad;
2264   gst_object_unref (proxypad);
2265   gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad);
2266
2267   templ = gst_static_pad_template_get (&subtitle_sinktemplate);
2268   self->subtitle_sinkpad =
2269       gst_ghost_pad_new_no_target_from_template ("subtitle_sink", templ);
2270   gst_object_unref (templ);
2271   gst_pad_set_link_function (self->subtitle_sinkpad,
2272       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_link));
2273   gst_pad_set_unlink_function (self->subtitle_sinkpad,
2274       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_unlink));
2275   gst_pad_set_event_function (self->subtitle_sinkpad,
2276       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_event));
2277   gst_pad_set_query_function (self->subtitle_sinkpad,
2278       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_query));
2279   gst_pad_set_chain_function (self->subtitle_sinkpad,
2280       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain));
2281
2282   proxypad =
2283       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2284           (self->subtitle_sinkpad)));
2285   self->subtitle_block_pad = proxypad;
2286   gst_object_unref (proxypad);
2287
2288   gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad);
2289
2290   self->fps_n = 0;
2291   self->fps_d = 0;
2292 }
2293
2294 gboolean
2295 gst_subtitle_overlay_plugin_init (GstPlugin * plugin)
2296 {
2297   GST_DEBUG_CATEGORY_INIT (subtitle_overlay_debug, "subtitleoverlay", 0,
2298       "Subtitle Overlay");
2299
2300   _subtitle_overlay_event_marker_id =
2301       g_quark_from_static_string ("gst-subtitle-overlay-event-marker");
2302
2303   return gst_element_register (plugin, "subtitleoverlay", GST_RANK_NONE,
2304       GST_TYPE_SUBTITLE_OVERLAY);
2305 }