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