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