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