hlsdemux: Enable support for external subtitles
[platform/upstream/gstreamer.git] / ext / closedcaption / gstceaccoverlay.c
1 /* GStreamer
2  * Copyright (C) 2015 Samsung Electronics Co., Ltd.
3  *     @Author: Chengjun Wang <cjun.wang@samsung.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <gst/video/video.h>
26 #include <gst/video/gstvideometa.h>
27 #include <gst/base/gstbytereader.h>
28
29 #include "gstceaccoverlay.h"
30 #include <string.h>
31
32
33 #define GST_CAT_DEFAULT gst_cea_cc_overlay_debug
34 GST_DEBUG_CATEGORY (gst_cea_cc_overlay_debug);
35
36
37 #define DEFAULT_PROP_FONT_DESC  ""
38 #define DEFAULT_PROP_SILENT     FALSE
39 #define DEFAULT_PROP_SERVICE_NUMBER 1
40 #define DEFAULT_PROP_WINDOW_H_POS GST_CEA_CC_OVERLAY_WIN_H_CENTER
41
42 enum
43 {
44   PROP_0,
45   PROP_FONT_DESC,
46   PROP_SILENT,
47   PROP_SERVICE_NUMBER,
48   PROP_WINDOW_H_POS,
49   PROP_LAST
50 };
51
52 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
53 # define CAIRO_ARGB_A 3
54 # define CAIRO_ARGB_R 2
55 # define CAIRO_ARGB_G 1
56 # define CAIRO_ARGB_B 0
57 #else
58 # define CAIRO_ARGB_A 0
59 # define CAIRO_ARGB_R 1
60 # define CAIRO_ARGB_G 2
61 # define CAIRO_ARGB_B 3
62 #endif
63
64 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
65   b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
66   g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
67   r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
68 } G_STMT_END
69
70
71 #define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS
72
73 #define CC_OVERLAY_CAPS GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
74
75 #define CC_OVERLAY_ALL_CAPS CC_OVERLAY_CAPS ";" \
76     GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
77
78 static GstStaticCaps sw_template_caps = GST_STATIC_CAPS (CC_OVERLAY_CAPS);
79
80 static GstStaticPadTemplate src_template_factory =
81 GST_STATIC_PAD_TEMPLATE ("src",
82     GST_PAD_SRC,
83     GST_PAD_ALWAYS,
84     GST_STATIC_CAPS (CC_OVERLAY_ALL_CAPS)
85     );
86
87 static GstStaticPadTemplate video_sink_template_factory =
88 GST_STATIC_PAD_TEMPLATE ("video_sink",
89     GST_PAD_SINK,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS (CC_OVERLAY_ALL_CAPS)
92     );
93
94 static GstStaticPadTemplate cc_sink_template_factory =
95 GST_STATIC_PAD_TEMPLATE ("cc_sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS
99     ("closedcaption/x-cea-708, format={ (string) cdp, (string) cc_data }")
100     );
101
102
103 #define GST_TYPE_CC_OVERLAY_WIN_H_POS (gst_cea_cc_overlay_h_pos_get_type())
104 static GType
105 gst_cea_cc_overlay_h_pos_get_type (void)
106 {
107   static GType cc_overlay_win_h_pos_type = 0;
108   static const GEnumValue cc_overlay_win_h_pos[] = {
109     {GST_CEA_CC_OVERLAY_WIN_H_LEFT, "left", "left"},
110     {GST_CEA_CC_OVERLAY_WIN_H_CENTER, "center", "center"},
111     {GST_CEA_CC_OVERLAY_WIN_H_RIGHT, "right", "right"},
112     {GST_CEA_CC_OVERLAY_WIN_H_AUTO, "auto", "auto"},
113     {0, NULL, NULL},
114   };
115
116   if (!cc_overlay_win_h_pos_type) {
117     cc_overlay_win_h_pos_type =
118         g_enum_register_static ("GstCeaCcOverlayWinHPos", cc_overlay_win_h_pos);
119   }
120   return cc_overlay_win_h_pos_type;
121 }
122
123
124 #define GST_CEA_CC_OVERLAY_GET_LOCK(ov) (&GST_CEA_CC_OVERLAY (ov)->lock)
125 #define GST_CEA_CC_OVERLAY_GET_COND(ov) (&GST_CEA_CC_OVERLAY (ov)->cond)
126 #define GST_CEA_CC_OVERLAY_LOCK(ov)     (g_mutex_lock (GST_CEA_CC_OVERLAY_GET_LOCK (ov)))
127 #define GST_CEA_CC_OVERLAY_UNLOCK(ov)   (g_mutex_unlock (GST_CEA_CC_OVERLAY_GET_LOCK (ov)))
128 #define GST_CEA_CC_OVERLAY_WAIT(ov)     (g_cond_wait (GST_CEA_CC_OVERLAY_GET_COND (ov), GST_CEA_CC_OVERLAY_GET_LOCK (ov)))
129 #define GST_CEA_CC_OVERLAY_SIGNAL(ov)   (g_cond_signal (GST_CEA_CC_OVERLAY_GET_COND (ov)))
130 #define GST_CEA_CC_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_CEA_CC_OVERLAY_GET_COND (ov)))
131
132 static GstElementClass *parent_class = NULL;
133 static void gst_base_cea_cc_overlay_base_init (gpointer g_class);
134 static void gst_base_cea_cc_overlay_class_init (GstCeaCcOverlayClass * klass);
135 static void gst_base_cea_cc_overlay_init (GstCeaCcOverlay * overlay,
136     GstCeaCcOverlayClass * klass);
137 static GstStateChangeReturn gst_cea_cc_overlay_change_state (GstElement *
138     element, GstStateChange transition);
139 static GstCaps *gst_cea_cc_overlay_get_videosink_caps (GstPad * pad,
140     GstCeaCcOverlay * overlay, GstCaps * filter);
141 static GstCaps *gst_cea_cc_overlay_get_src_caps (GstPad * pad,
142     GstCeaCcOverlay * overlay, GstCaps * filter);
143 static gboolean gst_cea_cc_overlay_setcaps (GstCeaCcOverlay * overlay,
144     GstCaps * caps);
145 static gboolean gst_cea_cc_overlay_src_event (GstPad * pad, GstObject * parent,
146     GstEvent * event);
147 static gboolean gst_cea_cc_overlay_src_query (GstPad * pad, GstObject * parent,
148     GstQuery * query);
149
150 static gboolean gst_cea_cc_overlay_video_event (GstPad * pad,
151     GstObject * parent, GstEvent * event);
152 static gboolean gst_cea_cc_overlay_video_query (GstPad * pad,
153     GstObject * parent, GstQuery * query);
154 static GstFlowReturn gst_cea_cc_overlay_video_chain (GstPad * pad,
155     GstObject * parent, GstBuffer * buffer);
156
157 static gboolean gst_cea_cc_overlay_cc_event (GstPad * pad,
158     GstObject * parent, GstEvent * event);
159 static GstFlowReturn gst_cea_cc_overlay_cc_chain (GstPad * pad,
160     GstObject * parent, GstBuffer * buffer);
161 static GstPadLinkReturn gst_cea_cc_overlay_cc_pad_link (GstPad * pad,
162     GstObject * parent, GstPad * peer);
163 static void gst_cea_cc_overlay_cc_pad_unlink (GstPad * pad, GstObject * parent);
164 static void gst_cea_cc_overlay_pop_text (GstCeaCcOverlay * overlay);
165 static void gst_cea_cc_overlay_finalize (GObject * object);
166 static void gst_cea_cc_overlay_set_property (GObject * object, guint prop_id,
167     const GValue * value, GParamSpec * pspec);
168 static void gst_cea_cc_overlay_get_property (GObject * object, guint prop_id,
169     GValue * value, GParamSpec * pspec);
170
171 static gboolean gst_cea_cc_overlay_can_handle_caps (GstCaps * incaps);
172
173 GType
174 gst_cea_cc_overlay_get_type (void)
175 {
176   static GType type = 0;
177
178   if (g_once_init_enter ((gsize *) & type)) {
179     static const GTypeInfo info = {
180       sizeof (GstCeaCcOverlayClass),
181       (GBaseInitFunc) gst_base_cea_cc_overlay_base_init,
182       NULL,
183       (GClassInitFunc) gst_base_cea_cc_overlay_class_init,
184       NULL,
185       NULL,
186       sizeof (GstCeaCcOverlay),
187       0,
188       (GInstanceInitFunc) gst_base_cea_cc_overlay_init,
189     };
190
191     g_once_init_leave ((gsize *) & type,
192         g_type_register_static (GST_TYPE_ELEMENT, "GstCeaCcOverlay", &info, 0));
193   }
194
195   return type;
196 }
197
198 static void
199 gst_base_cea_cc_overlay_base_init (gpointer g_class)
200 {
201   GstCeaCcOverlayClass *klass = GST_CEA_CC_OVERLAY_CLASS (g_class);
202   PangoFontMap *fontmap;
203
204   /* Only lock for the subclasses here, the base class
205    * doesn't have this mutex yet and it's not necessary
206    * here */
207   /* FIXME : Not needed anymore since pango 1.32.6 ! */
208   if (klass->pango_lock)
209     g_mutex_lock (klass->pango_lock);
210   fontmap = pango_cairo_font_map_get_default ();
211   klass->pango_context =
212       pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
213   if (klass->pango_lock)
214     g_mutex_unlock (klass->pango_lock);
215
216 }
217
218 static void
219 gst_base_cea_cc_overlay_class_init (GstCeaCcOverlayClass * klass)
220 {
221   GObjectClass *gobject_class;
222   GstElementClass *gstelement_class;
223
224   gobject_class = (GObjectClass *) klass;
225   gstelement_class = (GstElementClass *) klass;
226
227   GST_DEBUG_CATEGORY_INIT (gst_cea_cc_overlay_debug, "cc708overlay", 0,
228       "cc708overlay");
229
230   parent_class = g_type_class_peek_parent (klass);
231
232   gobject_class->finalize = gst_cea_cc_overlay_finalize;
233   gobject_class->set_property = gst_cea_cc_overlay_set_property;
234   gobject_class->get_property = gst_cea_cc_overlay_get_property;
235
236   gst_element_class_add_pad_template (gstelement_class,
237       gst_static_pad_template_get (&src_template_factory));
238   gst_element_class_add_pad_template (gstelement_class,
239       gst_static_pad_template_get (&video_sink_template_factory));
240   gst_element_class_add_pad_template (gstelement_class,
241       gst_static_pad_template_get (&cc_sink_template_factory));
242
243   gstelement_class->change_state =
244       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_change_state);
245
246   klass->pango_lock = g_slice_new (GMutex);
247   g_mutex_init (klass->pango_lock);
248
249   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SERVICE_NUMBER,
250       g_param_spec_int ("service-number", "service-number",
251           "Service number. Service 1 is designated as the Primary Caption Service,"
252           " Service 2 is the Secondary Language Service.",
253           -1, 63, DEFAULT_PROP_SERVICE_NUMBER,
254           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WINDOW_H_POS,
257       g_param_spec_enum ("window-h-pos", "window-h-pos",
258           "Window's Horizontal position", GST_TYPE_CC_OVERLAY_WIN_H_POS,
259           DEFAULT_PROP_WINDOW_H_POS,
260           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
261
262   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
263       g_param_spec_string ("font-desc", "font description",
264           "Pango font description of font to be used for rendering.\n"
265           "See documentation of pango_font_description_from_string for syntax.\n"
266           "this will override closed caption stream specified font style/pen size.",
267           DEFAULT_PROP_FONT_DESC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
268
269   /**
270    * GstCeaCcOverlay:silent:
271    *
272    * If set, no text is rendered. Useful to switch off text rendering
273    * temporarily without removing the textoverlay element from the pipeline.
274    */
275   /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
276   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
277       g_param_spec_boolean ("silent", "silent",
278           "Whether to render the text string",
279           DEFAULT_PROP_SILENT,
280           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
281
282   gst_element_class_set_static_metadata (gstelement_class,
283       "Closed Caption overlay", "Mixer/Video/Overlay/Subtitle",
284       "Decode cea608/cea708 data and overlay on proper position of a video buffer",
285       "Chengjun Wang <cjun.wang@samsung.com>");
286   gst_cea708_decoder_init_debug ();
287
288 }
289
290 static void
291 gst_cea_cc_overlay_finalize (GObject * object)
292 {
293   GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
294
295   if (overlay->current_composition) {
296     gst_video_overlay_composition_unref (overlay->current_composition);
297     overlay->current_composition = NULL;
298   }
299   if (overlay->next_composition) {
300     gst_video_overlay_composition_unref (overlay->next_composition);
301     overlay->next_composition = NULL;
302   }
303
304   gst_cea708dec_free (overlay->decoder);
305   overlay->decoder = NULL;
306
307   g_mutex_clear (&overlay->lock);
308   g_cond_clear (&overlay->cond);
309
310   G_OBJECT_CLASS (parent_class)->finalize (object);
311 }
312
313 static void
314 gst_base_cea_cc_overlay_init (GstCeaCcOverlay * overlay,
315     GstCeaCcOverlayClass * klass)
316 {
317   GstPadTemplate *template;
318   overlay->decoder = gst_cea708dec_create (GST_CEA_CC_OVERLAY_GET_CLASS
319       (overlay)->pango_context);
320
321   /* video sink */
322   template = gst_static_pad_template_get (&video_sink_template_factory);
323   overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
324   gst_object_unref (template);
325   gst_pad_set_event_function (overlay->video_sinkpad,
326       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_video_event));
327   gst_pad_set_chain_function (overlay->video_sinkpad,
328       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_video_chain));
329   gst_pad_set_query_function (overlay->video_sinkpad,
330       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_video_query));
331   GST_PAD_SET_PROXY_ALLOCATION (overlay->video_sinkpad);
332   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
333
334   template =
335       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "cc_sink");
336   if (template) {
337     /* text sink */
338     overlay->cc_sinkpad = gst_pad_new_from_template (template, "cc_sink");
339
340     gst_pad_set_event_function (overlay->cc_sinkpad,
341         GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_cc_event));
342     gst_pad_set_chain_function (overlay->cc_sinkpad,
343         GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_cc_chain));
344     gst_pad_set_link_function (overlay->cc_sinkpad,
345         GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_cc_pad_link));
346     gst_pad_set_unlink_function (overlay->cc_sinkpad,
347         GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_cc_pad_unlink));
348     gst_element_add_pad (GST_ELEMENT (overlay), overlay->cc_sinkpad);
349   }
350
351   /* (video) source */
352   template = gst_static_pad_template_get (&src_template_factory);
353   overlay->srcpad = gst_pad_new_from_template (template, "src");
354   gst_object_unref (template);
355   gst_pad_set_event_function (overlay->srcpad,
356       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_src_event));
357   gst_pad_set_query_function (overlay->srcpad,
358       GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_src_query));
359   gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
360
361
362   overlay->silent = DEFAULT_PROP_SILENT;
363   overlay->need_update = TRUE;
364   overlay->current_composition = NULL;
365   overlay->next_composition = NULL;
366   overlay->cc_pad_linked = FALSE;
367   overlay->current_comp_start_time = GST_CLOCK_TIME_NONE;
368   overlay->next_comp_start_time = GST_CLOCK_TIME_NONE;
369   overlay->cea608_index[0] = 0;
370   overlay->cea608_index[1] = 0;
371   overlay->cea708_index = 0;
372   overlay->default_window_h_pos = DEFAULT_PROP_WINDOW_H_POS;
373
374   g_mutex_init (&overlay->lock);
375   g_cond_init (&overlay->cond);
376   gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
377 }
378
379 /* only negotiate/query video overlay composition support for now */
380 static gboolean
381 gst_cea_cc_overlay_negotiate (GstCeaCcOverlay * overlay, GstCaps * caps)
382 {
383   GstQuery *query;
384   gboolean attach = FALSE;
385   gboolean caps_has_meta = TRUE;
386   gboolean ret;
387   GstCapsFeatures *f;
388   GstCaps *original_caps;
389   gboolean original_has_meta = FALSE;
390   gboolean allocation_ret = TRUE;
391
392   GST_DEBUG_OBJECT (overlay, "performing negotiation");
393
394   if (!caps)
395     caps = gst_pad_get_current_caps (overlay->video_sinkpad);
396   else
397     gst_caps_ref (caps);
398
399   if (!caps || gst_caps_is_empty (caps))
400     goto no_format;
401
402   original_caps = caps;
403
404   /* Try to use the overlay meta if possible */
405   f = gst_caps_get_features (caps, 0);
406
407   /* if the caps doesn't have the overlay meta, we query if downstream
408    * accepts it before trying the version without the meta
409    * If upstream already is using the meta then we can only use it */
410   if (!f
411       || !gst_caps_features_contains (f,
412           GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION)) {
413     GstCaps *overlay_caps;
414
415     /* In this case we added the meta, but we can work without it
416      * so preserve the original caps so we can use it as a fallback */
417     overlay_caps = gst_caps_copy (caps);
418
419     f = gst_caps_get_features (overlay_caps, 0);
420     gst_caps_features_add (f,
421         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
422
423     ret = gst_pad_peer_query_accept_caps (overlay->srcpad, overlay_caps);
424     GST_DEBUG_OBJECT (overlay, "Downstream accepts the overlay meta: %d", ret);
425     if (ret) {
426       gst_caps_unref (caps);
427       caps = overlay_caps;
428
429     } else {
430       /* fallback to the original */
431       gst_caps_unref (overlay_caps);
432       caps_has_meta = FALSE;
433     }
434   } else {
435     original_has_meta = TRUE;
436   }
437   GST_DEBUG_OBJECT (overlay, "Using caps %" GST_PTR_FORMAT, caps);
438   ret = gst_pad_set_caps (overlay->srcpad, caps);
439
440   if (ret) {
441     /* find supported meta */
442     query = gst_query_new_allocation (caps, FALSE);
443
444     if (!gst_pad_peer_query (overlay->srcpad, query)) {
445       /* no problem, we use the query defaults */
446       GST_DEBUG_OBJECT (overlay, "ALLOCATION query failed");
447       allocation_ret = FALSE;
448     }
449
450     if (caps_has_meta && gst_query_find_allocation_meta (query,
451             GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
452       attach = TRUE;
453     gst_query_unref (query);
454   }
455
456   overlay->attach_compo_to_buffer = attach;
457
458   if (!allocation_ret && overlay->video_flushing) {
459     ret = FALSE;
460   } else if (original_caps && !original_has_meta && !attach) {
461     if (caps_has_meta) {
462       /* Some elements (fakesink) claim to accept the meta on caps but won't
463          put it in the allocation query result, this leads below
464          check to fail. Prevent this by removing the meta from caps */
465       gst_caps_unref (caps);
466       caps = gst_caps_ref (original_caps);
467       ret = gst_pad_set_caps (overlay->srcpad, caps);
468       if (ret && !gst_cea_cc_overlay_can_handle_caps (caps))
469         ret = FALSE;
470     }
471   }
472
473   if (!ret) {
474     GST_DEBUG_OBJECT (overlay, "negotiation failed, schedule reconfigure");
475     gst_pad_mark_reconfigure (overlay->srcpad);
476   }
477   gst_caps_unref (caps);
478   GST_DEBUG_OBJECT (overlay, "ret=%d", ret);
479
480   return ret;
481
482 no_format:
483   {
484     if (caps)
485       gst_caps_unref (caps);
486     return FALSE;
487   }
488 }
489
490 static gboolean
491 gst_cea_cc_overlay_can_handle_caps (GstCaps * incaps)
492 {
493   gboolean ret;
494   GstCaps *caps;
495   static GstStaticCaps static_caps = GST_STATIC_CAPS (CC_OVERLAY_CAPS);
496
497   caps = gst_static_caps_get (&static_caps);
498   ret = gst_caps_is_subset (incaps, caps);
499   gst_caps_unref (caps);
500
501   return ret;
502 }
503
504 static gboolean
505 gst_cea_cc_overlay_setcaps (GstCeaCcOverlay * overlay, GstCaps * caps)
506 {
507   GstVideoInfo info;
508   gboolean ret = FALSE;
509
510   if (!gst_video_info_from_caps (&info, caps))
511     goto invalid_caps;
512
513   overlay->info = info;
514   overlay->format = GST_VIDEO_INFO_FORMAT (&info);
515   overlay->width = GST_VIDEO_INFO_WIDTH (&info);
516   overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
517   gst_cea708dec_set_video_width_height (overlay->decoder, overlay->width,
518       overlay->height);
519   ret = gst_cea_cc_overlay_negotiate (overlay, caps);
520
521   GST_CEA_CC_OVERLAY_LOCK (overlay);
522   g_mutex_lock (GST_CEA_CC_OVERLAY_GET_CLASS (overlay)->pango_lock);
523   if (!overlay->attach_compo_to_buffer &&
524       !gst_cea_cc_overlay_can_handle_caps (caps)) {
525     GST_DEBUG_OBJECT (overlay, "unsupported caps %" GST_PTR_FORMAT, caps);
526     ret = FALSE;
527   }
528
529   g_mutex_unlock (GST_CEA_CC_OVERLAY_GET_CLASS (overlay)->pango_lock);
530   GST_CEA_CC_OVERLAY_UNLOCK (overlay);
531
532   return ret;
533
534   /* ERRORS */
535 invalid_caps:
536   {
537     GST_DEBUG_OBJECT (overlay, "could not parse caps");
538     return FALSE;
539   }
540 }
541
542 static void
543 gst_cea_cc_overlay_set_property (GObject * object, guint prop_id,
544     const GValue * value, GParamSpec * pspec)
545 {
546   GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
547   Cea708Dec *decoder = overlay->decoder;
548
549   GST_CEA_CC_OVERLAY_LOCK (overlay);
550   switch (prop_id) {
551     case PROP_SERVICE_NUMBER:
552     {
553       int desired_service = g_value_get_int (value);
554       gst_cea708dec_set_service_number (decoder, desired_service);
555       break;
556     }
557     case PROP_FONT_DESC:
558     {
559       PangoFontDescription *desc = NULL;
560       const gchar *fontdesc_str;
561       fontdesc_str = g_value_get_string (value);
562
563       GST_LOG_OBJECT (overlay, "Got font description '%s'", fontdesc_str);
564       if (fontdesc_str)
565         desc = pango_font_description_from_string (fontdesc_str);
566       /* Only set if NULL or valid description */
567       if (desc || !fontdesc_str) {
568         if (desc) {
569           GST_INFO_OBJECT (overlay, "Setting font description: '%s'",
570               fontdesc_str);
571           pango_font_description_free (desc);
572         } else
573           GST_INFO_OBJECT (overlay, "Resetting default font description");
574         g_free (decoder->default_font_desc);
575         decoder->default_font_desc = g_strdup (fontdesc_str);
576       }
577       break;
578     }
579     case PROP_SILENT:
580       overlay->silent = g_value_get_boolean (value);
581       break;
582     case PROP_WINDOW_H_POS:
583       overlay->default_window_h_pos = g_value_get_enum (value);
584       break;
585     default:
586       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587       break;
588   }
589
590   overlay->need_update = TRUE;
591   GST_CEA_CC_OVERLAY_UNLOCK (overlay);
592 }
593
594 static void
595 gst_cea_cc_overlay_get_property (GObject * object, guint prop_id,
596     GValue * value, GParamSpec * pspec)
597 {
598   GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
599   Cea708Dec *decoder = overlay->decoder;
600
601   GST_CEA_CC_OVERLAY_LOCK (overlay);
602   switch (prop_id) {
603     case PROP_SERVICE_NUMBER:
604       g_value_set_int (value, decoder->desired_service);
605       break;
606     case PROP_SILENT:
607       g_value_set_boolean (value, overlay->silent);
608       break;
609     case PROP_FONT_DESC:
610       g_value_set_string (value, decoder->default_font_desc);
611       break;
612     case PROP_WINDOW_H_POS:
613       g_value_set_enum (value, overlay->default_window_h_pos);
614       break;
615     default:
616       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
617       break;
618   }
619
620   GST_CEA_CC_OVERLAY_UNLOCK (overlay);
621 }
622
623 static gboolean
624 gst_cea_cc_overlay_src_query (GstPad * pad, GstObject * parent,
625     GstQuery * query)
626 {
627   gboolean ret = FALSE;
628   GstCeaCcOverlay *overlay;
629
630   overlay = GST_CEA_CC_OVERLAY (parent);
631
632   switch (GST_QUERY_TYPE (query)) {
633     case GST_QUERY_CAPS:
634     {
635       GstCaps *filter, *caps;
636
637       gst_query_parse_caps (query, &filter);
638       caps = gst_cea_cc_overlay_get_src_caps (pad, overlay, filter);
639       gst_query_set_caps_result (query, caps);
640       gst_caps_unref (caps);
641       ret = TRUE;
642       break;
643     }
644     default:
645       ret = gst_pad_query_default (pad, parent, query);
646       break;
647   }
648
649   return ret;
650 }
651
652 static gboolean
653 gst_cea_cc_overlay_src_event (GstPad * pad, GstObject * parent,
654     GstEvent * event)
655 {
656   GstCeaCcOverlay *overlay;
657   gboolean ret;
658
659   overlay = GST_CEA_CC_OVERLAY (parent);
660
661   if (overlay->cc_pad_linked) {
662     gst_event_ref (event);
663     ret = gst_pad_push_event (overlay->video_sinkpad, event);
664     gst_pad_push_event (overlay->cc_sinkpad, event);
665   } else {
666     ret = gst_pad_push_event (overlay->video_sinkpad, event);
667   }
668
669   return ret;
670 }
671
672 /**
673  * gst_cea_cc_overlay_add_feature_and_intersect:
674  *
675  * Creates a new #GstCaps containing the (given caps +
676  * given caps feature) + (given caps intersected by the
677  * given filter).
678  *
679  * Returns: the new #GstCaps
680  */
681 static GstCaps *
682 gst_cea_cc_overlay_add_feature_and_intersect (GstCaps * caps,
683     const gchar * feature, GstCaps * filter)
684 {
685   int i, caps_size;
686   GstCaps *new_caps;
687
688   new_caps = gst_caps_copy (caps);
689
690   caps_size = gst_caps_get_size (new_caps);
691   for (i = 0; i < caps_size; i++) {
692     GstCapsFeatures *features = gst_caps_get_features (new_caps, i);
693
694     if (!gst_caps_features_is_any (features)) {
695       gst_caps_features_add (features, feature);
696     }
697   }
698
699   gst_caps_append (new_caps, gst_caps_intersect_full (caps,
700           filter, GST_CAPS_INTERSECT_FIRST));
701
702   return new_caps;
703 }
704
705 /**
706  * gst_cea_cc_overlay_intersect_by_feature:
707  *
708  * Creates a new #GstCaps based on the following filtering rule.
709  *
710  * For each individual caps contained in given caps, if the
711  * caps uses the given caps feature, keep a version of the caps
712  * with the feature and an another one without. Otherwise, intersect
713  * the caps with the given filter.
714  *
715  * Returns: the new #GstCaps
716  */
717 static GstCaps *
718 gst_cea_cc_overlay_intersect_by_feature (GstCaps * caps,
719     const gchar * feature, GstCaps * filter)
720 {
721   int i, caps_size;
722   GstCaps *new_caps;
723
724   new_caps = gst_caps_new_empty ();
725
726   caps_size = gst_caps_get_size (caps);
727   for (i = 0; i < caps_size; i++) {
728     GstStructure *caps_structure = gst_caps_get_structure (caps, i);
729     GstCapsFeatures *caps_features =
730         gst_caps_features_copy (gst_caps_get_features (caps, i));
731     GstCaps *filtered_caps;
732     GstCaps *simple_caps =
733         gst_caps_new_full (gst_structure_copy (caps_structure), NULL);
734     gst_caps_set_features (simple_caps, 0, caps_features);
735
736     if (gst_caps_features_contains (caps_features, feature)) {
737       gst_caps_append (new_caps, gst_caps_copy (simple_caps));
738
739       gst_caps_features_remove (caps_features, feature);
740       filtered_caps = gst_caps_ref (simple_caps);
741     } else {
742       filtered_caps = gst_caps_intersect_full (simple_caps, filter,
743           GST_CAPS_INTERSECT_FIRST);
744     }
745     gst_caps_unref (simple_caps);
746     gst_caps_append (new_caps, filtered_caps);
747   }
748
749   return new_caps;
750 }
751
752 static GstCaps *
753 gst_cea_cc_overlay_get_videosink_caps (GstPad * pad,
754     GstCeaCcOverlay * overlay, GstCaps * filter)
755 {
756   GstPad *srcpad = overlay->srcpad;
757   GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
758
759   if (G_UNLIKELY (!overlay))
760     return gst_pad_get_pad_template_caps (pad);
761
762   if (filter) {
763     /* filter caps + composition feature + filter caps
764      * filtered by the software caps. */
765     GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
766     overlay_filter = gst_cea_cc_overlay_add_feature_and_intersect (filter,
767         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
768     gst_caps_unref (sw_caps);
769
770     GST_DEBUG_OBJECT (overlay, "overlay filter %" GST_PTR_FORMAT,
771         overlay_filter);
772   }
773
774   peer_caps = gst_pad_peer_query_caps (srcpad, overlay_filter);
775   if (overlay_filter)
776     gst_caps_unref (overlay_filter);
777   if (peer_caps) {
778
779     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, peer_caps);
780
781     if (gst_caps_is_any (peer_caps)) {
782       /* if peer returns ANY caps, return filtered src pad template caps */
783       caps = gst_caps_copy (gst_pad_get_pad_template_caps (srcpad));
784     } else {
785
786       /* duplicate caps which contains the composition into one version with
787        * the meta and one without. Filter the other caps by the software caps */
788       GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
789       caps = gst_cea_cc_overlay_intersect_by_feature (peer_caps,
790           GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
791       gst_caps_unref (sw_caps);
792     }
793
794     gst_caps_unref (peer_caps);
795
796   } else {
797     /* no peer, our padtemplate is enough then */
798     caps = gst_pad_get_pad_template_caps (pad);
799   }
800
801   if (filter) {
802     GstCaps *intersection = gst_caps_intersect_full (filter, caps,
803         GST_CAPS_INTERSECT_FIRST);
804     gst_caps_unref (caps);
805     caps = intersection;
806   }
807
808   GST_DEBUG_OBJECT (overlay, "returning  %" GST_PTR_FORMAT, caps);
809
810   return caps;
811 }
812
813 static GstCaps *
814 gst_cea_cc_overlay_get_src_caps (GstPad * pad, GstCeaCcOverlay * overlay,
815     GstCaps * filter)
816 {
817   GstPad *sinkpad = overlay->video_sinkpad;
818   GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
819
820   if (G_UNLIKELY (!overlay))
821     return gst_pad_get_pad_template_caps (pad);
822
823   if (filter) {
824     /* duplicate filter caps which contains the composition into one version
825      * with the meta and one without. Filter the other caps by the software
826      * caps */
827     GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
828     overlay_filter =
829         gst_cea_cc_overlay_intersect_by_feature (filter,
830         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
831     gst_caps_unref (sw_caps);
832   }
833
834   peer_caps = gst_pad_peer_query_caps (sinkpad, overlay_filter);
835
836   if (overlay_filter)
837     gst_caps_unref (overlay_filter);
838
839   if (peer_caps) {
840
841     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, peer_caps);
842
843     if (gst_caps_is_any (peer_caps)) {
844
845       /* if peer returns ANY caps, return filtered sink pad template caps */
846       caps = gst_caps_copy (gst_pad_get_pad_template_caps (sinkpad));
847
848     } else {
849
850       /* return upstream caps + composition feature + upstream caps
851        * filtered by the software caps. */
852       GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
853       caps = gst_cea_cc_overlay_add_feature_and_intersect (peer_caps,
854           GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
855       gst_caps_unref (sw_caps);
856     }
857
858     gst_caps_unref (peer_caps);
859
860   } else {
861     /* no peer, our padtemplate is enough then */
862     caps = gst_pad_get_pad_template_caps (pad);
863   }
864
865   if (filter) {
866     GstCaps *intersection;
867
868     intersection =
869         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
870     gst_caps_unref (caps);
871     caps = intersection;
872   }
873   GST_DEBUG_OBJECT (overlay, "returning  %" GST_PTR_FORMAT, caps);
874
875   return caps;
876 }
877
878 /* FIXME: should probably be relative to width/height (adjusted for PAR) */
879 #define BOX_XPAD  6
880 #define BOX_YPAD  6
881
882 static GstFlowReturn
883 gst_cea_cc_overlay_push_frame (GstCeaCcOverlay * overlay,
884     GstBuffer * video_frame)
885 {
886   GstVideoFrame frame;
887
888   if (overlay->current_composition == NULL)
889     goto done;
890   GST_LOG_OBJECT (overlay, "gst_cea_cc_overlay_push_frame");
891
892   if (gst_pad_check_reconfigure (overlay->srcpad))
893     gst_cea_cc_overlay_negotiate (overlay, NULL);
894
895   video_frame = gst_buffer_make_writable (video_frame);
896
897   if (overlay->attach_compo_to_buffer) {
898     GST_DEBUG_OBJECT (overlay, "Attaching text overlay image to video buffer");
899     gst_buffer_add_video_overlay_composition_meta (video_frame,
900         overlay->current_composition);
901     goto done;
902   }
903
904   if (!gst_video_frame_map (&frame, &overlay->info, video_frame,
905           GST_MAP_READWRITE))
906     goto invalid_frame;
907
908   gst_video_overlay_composition_blend (overlay->current_composition, &frame);
909
910   gst_video_frame_unmap (&frame);
911
912 done:
913
914   return gst_pad_push (overlay->srcpad, video_frame);
915
916   /* ERRORS */
917 invalid_frame:
918   {
919     gst_buffer_unref (video_frame);
920     return GST_FLOW_OK;
921   }
922 }
923
924 static GstPadLinkReturn
925 gst_cea_cc_overlay_cc_pad_link (GstPad * pad, GstObject * parent, GstPad * peer)
926 {
927   GstCeaCcOverlay *overlay;
928
929   overlay = GST_CEA_CC_OVERLAY (parent);
930   if (G_UNLIKELY (!overlay))
931     return GST_PAD_LINK_REFUSED;
932
933   GST_DEBUG_OBJECT (overlay, "Closed Caption pad linked");
934
935   overlay->cc_pad_linked = TRUE;
936
937   return GST_PAD_LINK_OK;
938 }
939
940 static void
941 gst_cea_cc_overlay_cc_pad_unlink (GstPad * pad, GstObject * parent)
942 {
943   GstCeaCcOverlay *overlay;
944
945   /* don't use gst_pad_get_parent() here, will deadlock */
946   overlay = GST_CEA_CC_OVERLAY (parent);
947
948   GST_DEBUG_OBJECT (overlay, "Closed Caption pad unlinked");
949
950   overlay->cc_pad_linked = FALSE;
951
952   gst_segment_init (&overlay->cc_segment, GST_FORMAT_UNDEFINED);
953 }
954
955 static gboolean
956 gst_cea_cc_overlay_cc_event (GstPad * pad, GstObject * parent, GstEvent * event)
957 {
958   gboolean ret = FALSE;
959   GstCeaCcOverlay *overlay = NULL;
960
961   overlay = GST_CEA_CC_OVERLAY (parent);
962
963   GST_LOG_OBJECT (overlay, "received event %s", GST_EVENT_TYPE_NAME (event));
964
965   switch (GST_EVENT_TYPE (event)) {
966     case GST_EVENT_CAPS:
967     {
968       GstCaps *caps;
969       GstStructure *st;
970       const gchar *cctype;
971
972       gst_event_parse_caps (event, &caps);
973       st = gst_caps_get_structure (caps, 0);
974       cctype = gst_structure_get_string (st, "format");
975       overlay->is_cdp = !g_strcmp0 (cctype, "cdp");
976       ret = TRUE;
977       break;
978     }
979     case GST_EVENT_SEGMENT:
980     {
981       const GstSegment *segment;
982
983       overlay->cc_eos = FALSE;
984
985       gst_event_parse_segment (event, &segment);
986
987       if (segment->format == GST_FORMAT_TIME) {
988         GST_CEA_CC_OVERLAY_LOCK (overlay);
989         gst_segment_copy_into (segment, &overlay->cc_segment);
990         GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
991             &overlay->cc_segment);
992         GST_CEA_CC_OVERLAY_UNLOCK (overlay);
993       } else {
994         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
995             ("received non-TIME newsegment event on text input"));
996       }
997
998       gst_event_unref (event);
999       ret = TRUE;
1000
1001       /* wake up the video chain, it might be waiting for a text buffer or
1002        * a text segment update */
1003       GST_CEA_CC_OVERLAY_LOCK (overlay);
1004       GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1005       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1006       break;
1007     }
1008     case GST_EVENT_GAP:
1009     {
1010       GstClockTime start, duration;
1011
1012       gst_event_parse_gap (event, &start, &duration);
1013       if (GST_CLOCK_TIME_IS_VALID (duration))
1014         start += duration;
1015       /* we do not expect another buffer until after gap,
1016        * so that is our position now */
1017       overlay->cc_segment.position = start;
1018
1019       /* wake up the video chain, it might be waiting for a text buffer or
1020        * a text segment update */
1021       GST_CEA_CC_OVERLAY_LOCK (overlay);
1022       GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1023       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1024
1025       gst_event_unref (event);
1026       ret = TRUE;
1027       break;
1028     }
1029     case GST_EVENT_FLUSH_STOP:
1030       GST_CEA_CC_OVERLAY_LOCK (overlay);
1031       GST_INFO_OBJECT (overlay, "text flush stop");
1032       overlay->cc_flushing = FALSE;
1033       overlay->cc_eos = FALSE;
1034       gst_cea_cc_overlay_pop_text (overlay);
1035       gst_segment_init (&overlay->cc_segment, GST_FORMAT_TIME);
1036       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1037       gst_event_unref (event);
1038       ret = TRUE;
1039       break;
1040     case GST_EVENT_FLUSH_START:
1041       GST_CEA_CC_OVERLAY_LOCK (overlay);
1042       GST_INFO_OBJECT (overlay, "text flush start");
1043       overlay->cc_flushing = TRUE;
1044       GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1045       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1046       gst_event_unref (event);
1047       ret = TRUE;
1048       break;
1049     case GST_EVENT_EOS:
1050       GST_CEA_CC_OVERLAY_LOCK (overlay);
1051       overlay->cc_eos = TRUE;
1052       GST_INFO_OBJECT (overlay, "closed caption EOS");
1053       /* wake up the video chain, it might be waiting for a text buffer or
1054        * a text segment update */
1055       GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1056       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1057       gst_event_unref (event);
1058       ret = TRUE;
1059       break;
1060     default:
1061       ret = gst_pad_event_default (pad, parent, event);
1062       break;
1063   }
1064
1065   return ret;
1066 }
1067
1068 static gboolean
1069 gst_cea_cc_overlay_video_event (GstPad * pad, GstObject * parent,
1070     GstEvent * event)
1071 {
1072   gboolean ret = FALSE;
1073   GstCeaCcOverlay *overlay = NULL;
1074
1075   overlay = GST_CEA_CC_OVERLAY (parent);
1076
1077   GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
1078
1079   switch (GST_EVENT_TYPE (event)) {
1080     case GST_EVENT_CAPS:
1081     {
1082       GstCaps *caps;
1083
1084       gst_event_parse_caps (event, &caps);
1085       ret = gst_cea_cc_overlay_setcaps (overlay, caps);
1086       gst_event_unref (event);
1087       break;
1088     }
1089     case GST_EVENT_SEGMENT:
1090     {
1091       const GstSegment *segment;
1092
1093       GST_DEBUG_OBJECT (overlay, "received new segment");
1094
1095       gst_event_parse_segment (event, &segment);
1096
1097       if (segment->format == GST_FORMAT_TIME) {
1098         GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
1099             &overlay->segment);
1100
1101         gst_segment_copy_into (segment, &overlay->segment);
1102       } else {
1103         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
1104             ("received non-TIME newsegment event on video input"));
1105       }
1106
1107       ret = gst_pad_event_default (pad, parent, event);
1108       break;
1109     }
1110     case GST_EVENT_EOS:
1111       GST_CEA_CC_OVERLAY_LOCK (overlay);
1112       GST_INFO_OBJECT (overlay, "video EOS");
1113       overlay->video_eos = TRUE;
1114       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1115       ret = gst_pad_event_default (pad, parent, event);
1116       break;
1117     case GST_EVENT_FLUSH_START:
1118       GST_CEA_CC_OVERLAY_LOCK (overlay);
1119       GST_INFO_OBJECT (overlay, "video flush start");
1120       overlay->video_flushing = TRUE;
1121       GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1122       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1123       ret = gst_pad_event_default (pad, parent, event);
1124       break;
1125     case GST_EVENT_FLUSH_STOP:
1126       GST_CEA_CC_OVERLAY_LOCK (overlay);
1127       GST_INFO_OBJECT (overlay, "video flush stop");
1128       overlay->video_flushing = FALSE;
1129       overlay->video_eos = FALSE;
1130       gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
1131       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1132       ret = gst_pad_event_default (pad, parent, event);
1133       break;
1134     default:
1135       ret = gst_pad_event_default (pad, parent, event);
1136       break;
1137   }
1138
1139   return ret;
1140 }
1141
1142 static gboolean
1143 gst_cea_cc_overlay_video_query (GstPad * pad, GstObject * parent,
1144     GstQuery * query)
1145 {
1146   gboolean ret = FALSE;
1147   GstCeaCcOverlay *overlay;
1148
1149   overlay = GST_CEA_CC_OVERLAY (parent);
1150
1151   switch (GST_QUERY_TYPE (query)) {
1152     case GST_QUERY_CAPS:
1153     {
1154       GstCaps *filter, *caps;
1155
1156       gst_query_parse_caps (query, &filter);
1157       caps = gst_cea_cc_overlay_get_videosink_caps (pad, overlay, filter);
1158       gst_query_set_caps_result (query, caps);
1159       gst_caps_unref (caps);
1160       ret = TRUE;
1161       break;
1162     }
1163     default:
1164       ret = gst_pad_query_default (pad, parent, query);
1165       break;
1166   }
1167
1168   return ret;
1169 }
1170
1171 /* Called with lock held */
1172 static void
1173 gst_cea_cc_overlay_pop_text (GstCeaCcOverlay * overlay)
1174 {
1175   g_return_if_fail (GST_IS_CEA_CC_OVERLAY (overlay));
1176
1177   if (GST_CLOCK_TIME_IS_VALID (overlay->current_comp_start_time)
1178       && overlay->current_composition) {
1179     GST_DEBUG_OBJECT (overlay, "releasing composition %p",
1180         overlay->current_composition);
1181     gst_video_overlay_composition_unref (overlay->current_composition);
1182     overlay->current_composition = NULL;
1183     overlay->current_comp_start_time = GST_CLOCK_TIME_NONE;
1184   }
1185
1186   /* Let the text task know we used that buffer */
1187   GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1188 }
1189
1190 static void
1191 gst_cea_cc_overlay_image_to_argb (guchar * pixbuf,
1192     cea708Window * window, int stride)
1193 {
1194   int i, j;
1195   guchar *p, *bitp;
1196   int width, height;
1197
1198   width = window->image_width;
1199   height = window->image_height;
1200
1201   for (i = 0; i < height; i++) {
1202     p = pixbuf + i * stride;
1203     bitp = window->text_image + i * width * 4;
1204
1205     for (j = 0; j < width; j++) {
1206       p[0] = bitp[CAIRO_ARGB_A];
1207       p[1] = bitp[CAIRO_ARGB_R];
1208       p[2] = bitp[CAIRO_ARGB_G];
1209       p[3] = bitp[CAIRO_ARGB_B];
1210
1211       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
1212       CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]);
1213
1214       bitp += 4;
1215       p += 4;
1216     }
1217   }
1218 }
1219
1220 static void
1221 gst_cea_cc_overlay_image_to_ayuv (guchar * pixbuf,
1222     cea708Window * window, int stride)
1223 {
1224   int y;                        /* text bitmap coordinates */
1225   guchar *p, *bitp;
1226   guchar a, r, g, b;
1227   int width, height;
1228
1229   width = window->image_width;
1230   height = window->image_height;
1231
1232   for (y = 0; y < height; y++) {
1233     int n;
1234     p = pixbuf + y * stride;
1235     bitp = window->text_image + y * width * 4;
1236
1237     for (n = 0; n < width; n++) {
1238       b = bitp[CAIRO_ARGB_B];
1239       g = bitp[CAIRO_ARGB_G];
1240       r = bitp[CAIRO_ARGB_R];
1241       a = bitp[CAIRO_ARGB_A];
1242       bitp += 4;
1243
1244       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
1245       CAIRO_UNPREMULTIPLY (a, r, g, b);
1246
1247       *p++ = a;
1248       *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
1249               ((7471 * b) >> 16)), 0, 255);
1250       *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
1251               ((32768 * b) >> 16) + 128), 0, 255);
1252       *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
1253               ((5329 * b) >> 16) + 128), 0, 255);
1254     }
1255   }
1256 }
1257
1258 static void
1259 gst_cea_cc_overlay_create_and_push_buffer (GstCeaCcOverlay * overlay)
1260 {
1261   Cea708Dec *decoder = overlay->decoder;
1262   GstBuffer *outbuf;
1263   GstMapInfo map;
1264   guint8 *window_image;
1265   gint n;
1266   guint window_id;
1267   cea708Window *window;
1268   guint v_anchor = 0;
1269   guint h_anchor = 0;
1270   GstVideoOverlayComposition *comp = NULL;
1271   GstVideoOverlayRectangle *rect = NULL;
1272   GST_CEA_CC_OVERLAY_LOCK (overlay);
1273
1274   for (window_id = 0; window_id < 8; window_id++) {
1275     window = decoder->cc_windows[window_id];
1276
1277     if (!window->updated) {
1278       continue;
1279     }
1280     if (!window->deleted && window->visible && window->text_image != NULL) {
1281       GST_DEBUG_OBJECT (overlay, "Allocating buffer");
1282       outbuf =
1283           gst_buffer_new_and_alloc (window->image_width *
1284           window->image_height * 4);
1285       gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
1286       window_image = map.data;
1287       if (decoder->use_ARGB) {
1288         memset (window_image, 0,
1289             window->image_width * window->image_height * 4);
1290         gst_buffer_add_video_meta (outbuf, GST_VIDEO_FRAME_FLAG_NONE,
1291             GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB, window->image_width,
1292             window->image_height);
1293       } else {
1294         for (n = 0; n < window->image_width * window->image_height; n++) {
1295           window_image[n * 4] = window_image[n * 4 + 1] = 0;
1296           window_image[n * 4 + 2] = window_image[n * 4 + 3] = 128;
1297         }
1298         gst_buffer_add_video_meta (outbuf, GST_VIDEO_FRAME_FLAG_NONE,
1299             GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV, window->image_width,
1300             window->image_height);
1301       }
1302
1303       v_anchor = window->screen_vertical * overlay->height / 100;
1304       switch (overlay->default_window_h_pos) {
1305         case GST_CEA_CC_OVERLAY_WIN_H_LEFT:
1306           window->h_offset = 0;
1307           break;
1308         case GST_CEA_CC_OVERLAY_WIN_H_CENTER:
1309           window->h_offset = (overlay->width - window->image_width) / 2;
1310           break;
1311         case GST_CEA_CC_OVERLAY_WIN_H_RIGHT:
1312           window->h_offset = overlay->width - window->image_width;
1313           break;
1314         case GST_CEA_CC_OVERLAY_WIN_H_AUTO:
1315         default:
1316           switch (window->anchor_point) {
1317             case ANCHOR_PT_TOP_LEFT:
1318             case ANCHOR_PT_MIDDLE_LEFT:
1319             case ANCHOR_PT_BOTTOM_LEFT:
1320               window->h_offset = h_anchor;
1321               break;
1322
1323             case ANCHOR_PT_TOP_CENTER:
1324             case ANCHOR_PT_CENTER:
1325             case ANCHOR_PT_BOTTOM_CENTER:
1326               window->h_offset = h_anchor - window->image_width / 2;
1327               break;
1328
1329             case ANCHOR_PT_TOP_RIGHT:
1330             case ANCHOR_PT_MIDDLE_RIGHT:
1331             case ANCHOR_PT_BOTTOM_RIGHT:
1332               window->h_offset = h_anchor - window->image_width;
1333               break;
1334             default:
1335               break;
1336           }
1337           break;
1338       }
1339
1340       switch (window->anchor_point) {
1341         case ANCHOR_PT_TOP_LEFT:
1342         case ANCHOR_PT_TOP_CENTER:
1343         case ANCHOR_PT_TOP_RIGHT:
1344           window->v_offset = v_anchor;
1345           break;
1346
1347         case ANCHOR_PT_MIDDLE_LEFT:
1348         case ANCHOR_PT_CENTER:
1349         case ANCHOR_PT_MIDDLE_RIGHT:
1350           window->v_offset = v_anchor - window->image_height / 2;
1351           break;
1352
1353         case ANCHOR_PT_BOTTOM_LEFT:
1354         case ANCHOR_PT_BOTTOM_CENTER:
1355         case ANCHOR_PT_BOTTOM_RIGHT:
1356           window->v_offset = v_anchor - window->image_height;
1357           break;
1358         default:
1359           break;
1360       }
1361       if (decoder->use_ARGB) {
1362         gst_cea_cc_overlay_image_to_argb (window_image, window,
1363             window->image_width * 4);
1364       } else {
1365         gst_cea_cc_overlay_image_to_ayuv (window_image, window,
1366             window->image_width * 4);
1367       }
1368       gst_buffer_unmap (outbuf, &map);
1369       GST_INFO_OBJECT (overlay,
1370           "window->anchor_point=%d,v_anchor=%d,h_anchor=%d,window->image_height=%d,window->image_width=%d, window->v_offset=%d, window->h_offset=%d,window->justify_mode=%d",
1371           window->anchor_point, v_anchor, h_anchor, window->image_height,
1372           window->image_width, window->v_offset, window->h_offset,
1373           window->justify_mode);
1374       rect =
1375           gst_video_overlay_rectangle_new_raw (outbuf, window->h_offset,
1376           window->v_offset, window->image_width, window->image_height, 0);
1377       if (comp == NULL) {
1378         comp = gst_video_overlay_composition_new (rect);
1379       } else {
1380         gst_video_overlay_composition_add_rectangle (comp, rect);
1381       }
1382       gst_video_overlay_rectangle_unref (rect);
1383       gst_buffer_unref (outbuf);
1384     }
1385   }
1386
1387   /* Wait for the previous buffer to go away */
1388   if (GST_CLOCK_TIME_IS_VALID (overlay->current_comp_start_time)) {
1389     overlay->next_composition = comp;
1390     overlay->next_comp_start_time = decoder->current_time;
1391     GST_DEBUG_OBJECT (overlay,
1392         "wait for render next %p, current is %p BUFFER: next ts=%"
1393         GST_TIME_FORMAT ",current ts=%" GST_TIME_FORMAT,
1394         overlay->next_composition, overlay->current_composition,
1395         GST_TIME_ARGS (overlay->next_comp_start_time),
1396         GST_TIME_ARGS (overlay->current_comp_start_time));
1397
1398     GST_DEBUG_OBJECT (overlay, "has a closed caption buffer queued, waiting");
1399     GST_CEA_CC_OVERLAY_WAIT (overlay);
1400     GST_DEBUG_OBJECT (overlay, "resuming");
1401     if (overlay->cc_flushing) {
1402       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1403       return;
1404     }
1405   }
1406
1407   overlay->next_composition = NULL;
1408   overlay->next_comp_start_time = GST_CLOCK_TIME_NONE;
1409   overlay->current_composition = comp;
1410   overlay->current_comp_start_time = decoder->current_time;
1411   GST_DEBUG_OBJECT (overlay, "T: %" GST_TIME_FORMAT,
1412       GST_TIME_ARGS (overlay->current_comp_start_time));
1413   overlay->need_update = FALSE;
1414
1415   /* in case the video chain is waiting for a text buffer, wake it up */
1416   GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1417   GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1418 }
1419
1420 static void
1421 gst_cea_cc_overlay_process_packet (GstCeaCcOverlay * overlay, guint8 cc_type)
1422 {
1423   gint16 *index = NULL;
1424   guint8 *buffer = NULL;
1425   guint8 *dtvcc_buffer = NULL;
1426   gboolean need_render = FALSE;
1427
1428   switch (cc_type) {
1429     case CCTYPE_608_CC1:
1430     case CCTYPE_608_CC2:
1431       index = &overlay->cea608_index[cc_type];
1432       buffer = overlay->cea608_buffer[cc_type];
1433       break;
1434
1435     case CCTYPE_708_ADD:
1436     case CCTYPE_708_START:
1437       index = &overlay->cea708_index;
1438       buffer = overlay->cea708_buffer;
1439       break;
1440     default:
1441       GST_ERROR_OBJECT (overlay,
1442           "attempted to process packet for unknown cc_type %d", cc_type);
1443       return;
1444   }
1445
1446   if (*index > 0) {
1447     /*TODO: in future need add 608 decoder, currently only deal with 708 */
1448     if (cc_type == CCTYPE_708_ADD || cc_type == CCTYPE_708_START) {
1449       GST_LOG_OBJECT (overlay,
1450           "called - buf[%" G_GINT16_FORMAT "] = %02X:%02X:%02X:%02X", *index,
1451           buffer[0], buffer[1], buffer[2], buffer[3]);
1452       dtvcc_buffer = g_malloc0 (*index + 1);
1453       memcpy (dtvcc_buffer, buffer, *index);
1454       need_render =
1455           gst_cea708dec_process_dtvcc_packet (overlay->decoder, dtvcc_buffer,
1456           *index);
1457       g_free (dtvcc_buffer);
1458       if (need_render)
1459         gst_cea_cc_overlay_create_and_push_buffer (overlay);
1460     }
1461   }
1462   *index = 0;
1463 }
1464
1465
1466 /**
1467  * gst_cea_cc_overlay_user_data_decode:
1468  * @overlay: The #GstCeaCcOverlay 
1469  * @user_data: The #GstMpegVideoCCData to decode
1470  *
1471  * decode closed caption data and render when neccesary
1472  * in struct GstMpegVideoCCData type's user_data's data field, 3 byte's data construct 1 cc_data_pkt
1473  *
1474  * A cc_data_pkt is 3 bytes as follows:
1475  * -------------------------------------------
1476  *   5 bits (b7-b3)  marker_bits (should be all 1's)
1477  *   1 bit (b2)      cc_valid
1478  *   2 bits (b1-b0)  cc_type (bslbf)
1479  *   8 bits          cc_data_1 (bslbf)
1480  *   8 bits          cc_data_2 (bslbf)
1481  *
1482  *   If cc_valid != 1, then ignore this packet
1483  *
1484  *   cc_type has these values:
1485  *   0   NTSC_CC_FIELD_1     - CEA-608
1486  *   1   NTSC_CC_FIELD_2     - CEA-608
1487  *   2   DTVCC_PACKET_DATA   - CEA-708
1488  *   3   DTVCC_PACKET_START  - CEA-708
1489  *
1490  *   DTVCC packet (aka. caption channel packet)
1491  *   This is formed by accumulating cc_data_1/cc_data_2 from each cc_data_pkt
1492  *   starting with a packet where cc_type = 3, and ending with a packet
1493  *   where again cc_type = 3 (start of next buffer), or cc_valid=0 && cc_type=2
1494  *   DTVCC packet's structure is:
1495  *   --------------------------------------------------------------------------
1496  *   2 bits (b6-b7)  sequence_number
1497  *   6 bits (b0-b5)  packet_size
1498  *   ((packet_size*2-1)&0xFF) * 8 bits packet_data (Service Block)
1499  */
1500 static void
1501 gst_cea_cc_overlay_user_data_decode (GstCeaCcOverlay * overlay,
1502     const guint8 * ccdata, gsize ccsize)
1503 {
1504   guint8 temp;
1505   guint8 cc_count;
1506   guint i;
1507   guint8 cc_type;
1508   guint8 cc_valid;
1509   guint8 cc_data[2];
1510
1511   cc_count = ccsize / 3;
1512
1513   for (i = 0; i < cc_count; i++) {
1514     temp = *ccdata++;
1515     cc_data[0] = *ccdata++;
1516     cc_data[1] = *ccdata++;
1517     cc_valid = (temp & CCTYPE_VALID_MASK) ? TRUE : FALSE;
1518     cc_type = (temp & CCTYPE_TYPE_MASK);
1519
1520     GST_LOG_OBJECT (overlay, "cc_data_pkt(%d): cc_valid=%d cc_type=%d "
1521         "cc_data[0]=0x%02X cc_data[1]=0x%02X",
1522         i, cc_valid, cc_type, cc_data[0], cc_data[1]);
1523
1524     /* accumulate dvtcc packet */
1525     switch (cc_type) {
1526       case CCTYPE_608_CC1:
1527       case CCTYPE_608_CC2:
1528         if (cc_valid) {
1529           if (overlay->cea608_index[cc_type] <= DTVCC_LENGTH - 2) {
1530             size_t j;
1531             for (j = 0; j < 2; ++j) {
1532               if ((cc_data[j] < ' ') || (cc_data[j] > '~')) {
1533                 gst_cea_cc_overlay_process_packet (overlay, cc_type);
1534               }
1535               overlay->cea608_buffer[cc_type][overlay->
1536                   cea608_index[cc_type]++] = cc_data[j];
1537             }
1538           } else {
1539             GST_ERROR_OBJECT (overlay, "cea608_buffer[%d] overflow!", cc_type);
1540           }
1541         }
1542         break;
1543
1544       case CCTYPE_708_ADD:
1545       case CCTYPE_708_START:
1546         if (cc_valid) {
1547           if (cc_type == CCTYPE_708_START) {
1548             /* The previous packet is complete */
1549             gst_cea_cc_overlay_process_packet (overlay, cc_type);
1550           }
1551           /* Add on to the current DTVCC packet */
1552           if (overlay->cea708_index <= DTVCC_LENGTH - 2) {
1553             overlay->cea708_buffer[overlay->cea708_index++] = cc_data[0];
1554             overlay->cea708_buffer[overlay->cea708_index++] = cc_data[1];
1555           } else {
1556             GST_ERROR_OBJECT (overlay, "cea708_buffer overflow!");
1557           }
1558         } else if (cc_type == CCTYPE_708_ADD) {
1559           /* This packet should be ignored, but if there is a current */
1560           /* DTVCC packet then this is the end. */
1561           gst_cea_cc_overlay_process_packet (overlay, cc_type);
1562         }
1563         break;
1564     }
1565   }
1566 }
1567
1568 /* FIXME : Move to GstVideo ANC/CC helper library */
1569 static gboolean
1570 extract_ccdata_from_cdp (const guint8 * indata, gsize insize,
1571     const guint8 ** ccdata, gsize * ccsize)
1572 {
1573   GstByteReader br;
1574   guint8 cdp_length;
1575   guint8 flags;
1576 #ifndef GST_DISABLE_GST_DEBUG
1577   guint8 framerate_code;
1578   guint16 seqhdr;
1579 #endif
1580
1581   GST_MEMDUMP ("CDP", indata, insize);
1582
1583   gst_byte_reader_init (&br, indata, insize);
1584
1585   /* The smallest valid CDP we are interested in is 7 (header) + 2 (cc
1586    * section) + 4 (footer) bytes long */
1587   if (gst_byte_reader_get_remaining (&br) < 13)
1588     return FALSE;
1589
1590   /* Check header */
1591   if (gst_byte_reader_get_uint16_be_unchecked (&br) != 0x9669) {
1592     GST_WARNING ("Invalid CDP header");
1593     return FALSE;
1594   }
1595   cdp_length = gst_byte_reader_get_uint8_unchecked (&br);
1596   if (cdp_length > insize) {
1597     GST_WARNING ("CDP too small (need %d bytes, have %" G_GSIZE_FORMAT ")",
1598         cdp_length, insize);
1599     return FALSE;
1600   }
1601 #ifndef GST_DISABLE_GST_DEBUG
1602   framerate_code = gst_byte_reader_get_uint8_unchecked (&br) >> 4;
1603 #else
1604   gst_byte_reader_skip (&br, 1);
1605 #endif
1606   flags = gst_byte_reader_get_uint8_unchecked (&br);
1607 #ifndef GST_DISABLE_GST_DEBUG
1608   seqhdr = gst_byte_reader_get_uint16_be_unchecked (&br);
1609 #else
1610   gst_byte_reader_skip (&br, 2);
1611 #endif
1612
1613   GST_DEBUG
1614       ("framerate_code : 0x%02x , flags : 0x%02x , sequencer_counter : %u",
1615       framerate_code, flags, seqhdr);
1616
1617   /* Skip timecode if present */
1618   if (flags & 0x80) {
1619     GST_LOG ("Skipping timecode section");
1620     gst_byte_reader_skip (&br, 5);
1621   }
1622
1623   /* cc data */
1624   if (flags & 0x40) {
1625     guint8 ccid, cc_count;
1626     if (!gst_byte_reader_get_uint8 (&br, &ccid) ||
1627         !gst_byte_reader_get_uint8 (&br, &cc_count))
1628       return FALSE;
1629     if (ccid != 0x72) {
1630       GST_WARNING ("Invalid ccdata_id (expected 0x72, got 0x%02x)", ccid);
1631       return FALSE;
1632     }
1633     cc_count &= 0x1f;
1634     if (!gst_byte_reader_get_data (&br, cc_count * 3, ccdata)) {
1635       GST_WARNING ("Not enough ccdata");
1636       *ccdata = NULL;
1637       *ccsize = 0;
1638       return FALSE;
1639     }
1640     *ccsize = cc_count * 3;
1641   }
1642
1643   /* FIXME : Parse/validate the rest of the CDP ! */
1644
1645   return TRUE;
1646 }
1647
1648 /* We receive text buffers here. If they are out of segment we just ignore them.
1649    If the buffer is in our segment we keep it internally except if another one
1650    is already waiting here, in that case we wait that it gets kicked out */
1651 static GstFlowReturn
1652 gst_cea_cc_overlay_cc_chain (GstPad * pad, GstObject * parent,
1653     GstBuffer * buffer)
1654 {
1655   GstFlowReturn ret = GST_FLOW_OK;
1656   GstCeaCcOverlay *overlay = (GstCeaCcOverlay *) parent;
1657   gboolean in_seg = FALSE;
1658   guint64 clip_start = 0, clip_stop = 0;
1659
1660   GST_CEA_CC_OVERLAY_LOCK (overlay);
1661
1662   if (overlay->cc_flushing) {
1663     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1664     ret = GST_FLOW_FLUSHING;
1665     GST_LOG_OBJECT (overlay, "closed caption flushing");
1666     goto beach;
1667   }
1668
1669   if (overlay->cc_eos) {
1670     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1671     ret = GST_FLOW_EOS;
1672     GST_LOG_OBJECT (overlay, "closed caption EOS");
1673     goto beach;
1674   }
1675
1676   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
1677       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
1678       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
1679       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
1680           GST_BUFFER_DURATION (buffer)));
1681
1682   if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
1683     GstClockTime stop;
1684
1685     if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
1686       stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
1687     else
1688       stop = GST_CLOCK_TIME_NONE;
1689
1690     in_seg = gst_segment_clip (&overlay->cc_segment, GST_FORMAT_TIME,
1691         GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
1692     GST_LOG_OBJECT (overlay, "stop:%" GST_TIME_FORMAT ", in_seg: %d",
1693         GST_TIME_ARGS (stop), in_seg);
1694   } else {
1695     in_seg = TRUE;
1696   }
1697
1698
1699   if (in_seg) {
1700     GstMapInfo buf_map = { 0 };
1701     const guint8 *ccdata = NULL;
1702     gsize ccsize = 0;
1703
1704     overlay->cc_segment.position = clip_start;
1705     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1706
1707     gst_buffer_map (buffer, &buf_map, GST_MAP_READ);
1708     if (overlay->is_cdp) {
1709       extract_ccdata_from_cdp (buf_map.data, buf_map.size, &ccdata, &ccsize);
1710     } else {
1711       ccdata = buf_map.data;
1712       ccsize = buf_map.size;
1713     }
1714     if (ccsize) {
1715       gst_cea_cc_overlay_user_data_decode (overlay, ccdata, ccsize);
1716       overlay->decoder->current_time = GST_BUFFER_PTS (buffer);
1717     }
1718     gst_buffer_unmap (buffer, &buf_map);
1719   } else {
1720     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1721   }
1722
1723 beach:
1724   gst_buffer_unref (buffer);
1725   return ret;
1726 }
1727
1728 static GstFlowReturn
1729 gst_cea_cc_overlay_video_chain (GstPad * pad, GstObject * parent,
1730     GstBuffer * buffer)
1731 {
1732   GstCeaCcOverlay *overlay;
1733   GstFlowReturn ret = GST_FLOW_OK;
1734   gboolean in_seg = FALSE;
1735   guint64 start, stop, clip_start = 0, clip_stop = 0;
1736
1737   overlay = GST_CEA_CC_OVERLAY (parent);
1738
1739   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
1740     goto missing_timestamp;
1741
1742   /* ignore buffers that are outside of the current segment */
1743   start = GST_BUFFER_TIMESTAMP (buffer);
1744
1745   if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
1746     stop = GST_CLOCK_TIME_NONE;
1747   } else {
1748     stop = start + GST_BUFFER_DURATION (buffer);
1749   }
1750
1751   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
1752       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
1753       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
1754
1755   /* segment_clip() will adjust start unconditionally to segment_start if
1756    * no stop time is provided, so handle this ourselves */
1757   if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
1758     goto out_of_segment;
1759
1760   in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
1761       &clip_start, &clip_stop);
1762
1763   if (!in_seg)
1764     goto out_of_segment;
1765
1766   /* if the buffer is only partially in the segment, fix up stamps */
1767   if (clip_start != start || (stop != -1 && clip_stop != stop)) {
1768     GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
1769     buffer = gst_buffer_make_writable (buffer);
1770     GST_BUFFER_TIMESTAMP (buffer) = clip_start;
1771     if (stop != -1)
1772       GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
1773   }
1774
1775   /* now, after we've done the clipping, fix up end time if there's no
1776    * duration (we only use those estimated values internally though, we
1777    * don't want to set bogus values on the buffer itself) */
1778   if (stop == -1) {
1779     if (overlay->info.fps_n && overlay->info.fps_d) {
1780       GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
1781       stop = start + gst_util_uint64_scale_int (GST_SECOND,
1782           overlay->info.fps_d, overlay->info.fps_n);
1783     } else {
1784       GST_LOG_OBJECT (overlay, "no duration, assuming minimal duration");
1785       stop = start + 1;         /* we need to assume some interval */
1786     }
1787   }
1788
1789   gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
1790
1791 wait_for_text_buf:
1792
1793   GST_CEA_CC_OVERLAY_LOCK (overlay);
1794
1795   if (overlay->video_flushing)
1796     goto flushing;
1797
1798   if (overlay->video_eos)
1799     goto have_eos;
1800
1801   if (overlay->silent) {
1802     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1803     ret = gst_pad_push (overlay->srcpad, buffer);
1804
1805     /* Update position */
1806     overlay->segment.position = clip_start;
1807
1808     return ret;
1809   }
1810
1811   /* Closed Caption pad not linked, rendering video only */
1812   if (!overlay->cc_pad_linked) {
1813     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1814     ret = gst_pad_push (overlay->srcpad, buffer);
1815   } else {
1816     /* Closed Caption pad linked, check if we have a text buffer queued */
1817     if (GST_CLOCK_TIME_IS_VALID (overlay->current_comp_start_time)) {
1818       gboolean pop_text = FALSE, valid_text_time = TRUE;
1819
1820       GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
1821       GstClockTime next_buffer_text_running_time = GST_CLOCK_TIME_NONE;
1822 #ifndef GST_DISABLE_GST_DEBUG
1823       GstClockTime vid_running_time;
1824 #endif
1825       GstClockTime vid_running_time_end;
1826
1827 #ifndef GST_DISABLE_GST_DEBUG
1828       vid_running_time =
1829           gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
1830           start);
1831 #endif
1832       vid_running_time_end =
1833           gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
1834           stop);
1835       if (GST_CLOCK_TIME_IS_VALID (overlay->next_comp_start_time)) {
1836         next_buffer_text_running_time =
1837             gst_segment_to_running_time (&overlay->cc_segment, GST_FORMAT_TIME,
1838             overlay->next_comp_start_time);
1839
1840         if (next_buffer_text_running_time < vid_running_time_end) {
1841           /* text buffer should be force updated, popping  */
1842           GST_DEBUG_OBJECT (overlay,
1843               "T: next_buffer_text_running_time: %" GST_TIME_FORMAT
1844               " - overlay->next_comp_start_time: %" GST_TIME_FORMAT,
1845               GST_TIME_ARGS (next_buffer_text_running_time),
1846               GST_TIME_ARGS (overlay->next_comp_start_time));
1847           GST_DEBUG_OBJECT (overlay,
1848               "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
1849               GST_TIME_ARGS (vid_running_time),
1850               GST_TIME_ARGS (vid_running_time_end));
1851           GST_LOG_OBJECT (overlay,
1852               "text buffer should be force updated, popping");
1853           pop_text = FALSE;
1854           gst_cea_cc_overlay_pop_text (overlay);
1855           GST_CEA_CC_OVERLAY_WAIT (overlay);
1856           GST_DEBUG_OBJECT (overlay, "resuming");
1857           GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1858           goto wait_for_text_buf;
1859         }
1860
1861       }
1862
1863       /* if the text buffer isn't stamped right, pop it off the
1864        * queue and display it for the current video frame only */
1865       if (!GST_CLOCK_TIME_IS_VALID (overlay->current_comp_start_time)) {
1866         GST_WARNING_OBJECT (overlay, "Got text buffer with invalid timestamp");
1867         pop_text = TRUE;
1868         valid_text_time = FALSE;
1869       }
1870
1871       /* If timestamp and duration are valid */
1872       if (valid_text_time) {
1873         text_running_time =
1874             gst_segment_to_running_time (&overlay->cc_segment,
1875             GST_FORMAT_TIME, overlay->current_comp_start_time);
1876       }
1877
1878       GST_DEBUG_OBJECT (overlay, "T: %" GST_TIME_FORMAT,
1879           GST_TIME_ARGS (text_running_time));
1880       GST_DEBUG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
1881           GST_TIME_ARGS (vid_running_time),
1882           GST_TIME_ARGS (vid_running_time_end));
1883
1884       if (valid_text_time && vid_running_time_end <= text_running_time) {
1885         GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
1886         GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1887         /* Push the video frame */
1888         ret = gst_pad_push (overlay->srcpad, buffer);
1889       } else {
1890         GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1891         ret = gst_cea_cc_overlay_push_frame (overlay, buffer);
1892       }
1893       if (pop_text) {
1894         GST_CEA_CC_OVERLAY_LOCK (overlay);
1895         gst_cea_cc_overlay_pop_text (overlay);
1896         GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1897       }
1898     } else {
1899       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1900       GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
1901       ret = gst_pad_push (overlay->srcpad, buffer);
1902     }
1903   }
1904
1905   /* Update position */
1906   overlay->segment.position = clip_start;
1907   GST_DEBUG_OBJECT (overlay, "ret=%d", ret);
1908
1909   return ret;
1910
1911 missing_timestamp:
1912   {
1913     GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
1914     gst_buffer_unref (buffer);
1915     return GST_FLOW_OK;
1916   }
1917
1918 flushing:
1919   {
1920     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1921     GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
1922     gst_buffer_unref (buffer);
1923     return GST_FLOW_FLUSHING;
1924   }
1925 have_eos:
1926   {
1927     GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1928     GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
1929     gst_buffer_unref (buffer);
1930     return GST_FLOW_EOS;
1931   }
1932 out_of_segment:
1933   {
1934     GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
1935     gst_buffer_unref (buffer);
1936     return GST_FLOW_OK;
1937   }
1938 }
1939
1940 static GstStateChangeReturn
1941 gst_cea_cc_overlay_change_state (GstElement * element,
1942     GstStateChange transition)
1943 {
1944   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1945   GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (element);
1946
1947   switch (transition) {
1948     case GST_STATE_CHANGE_PAUSED_TO_READY:
1949       GST_CEA_CC_OVERLAY_LOCK (overlay);
1950       overlay->cc_flushing = TRUE;
1951       overlay->video_flushing = TRUE;
1952       /* pop_text will broadcast on the GCond and thus also make the video
1953        * chain exit if it's waiting for a text buffer */
1954       gst_cea_cc_overlay_pop_text (overlay);
1955       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1956       break;
1957     default:
1958       break;
1959   }
1960
1961   ret = parent_class->change_state (element, transition);
1962   if (ret == GST_STATE_CHANGE_FAILURE)
1963     return ret;
1964
1965   switch (transition) {
1966     case GST_STATE_CHANGE_READY_TO_PAUSED:
1967       GST_CEA_CC_OVERLAY_LOCK (overlay);
1968       overlay->cc_flushing = FALSE;
1969       overlay->video_flushing = FALSE;
1970       overlay->video_eos = FALSE;
1971       overlay->cc_eos = FALSE;
1972       gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
1973       gst_segment_init (&overlay->cc_segment, GST_FORMAT_TIME);
1974       GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1975       break;
1976     default:
1977       break;
1978   }
1979
1980   return ret;
1981 }