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