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