2 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
3 * @Author: Chengjun Wang <cjun.wang@samsung.com>
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.
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.
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.
25 #include <gst/video/video.h>
26 #include <gst/video/gstvideometa.h>
27 #include <gst/base/gstbytereader.h>
29 #include "gstceaccoverlay.h"
33 #define GST_CAT_DEFAULT gst_cea_cc_overlay_debug
34 GST_DEBUG_CATEGORY (gst_cea_cc_overlay_debug);
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
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
58 # define CAIRO_ARGB_A 0
59 # define CAIRO_ARGB_R 1
60 # define CAIRO_ARGB_G 2
61 # define CAIRO_ARGB_B 3
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; \
71 #define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS
73 #define CC_OVERLAY_CAPS GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
75 #define CC_OVERLAY_ALL_CAPS CC_OVERLAY_CAPS ";" \
76 GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
78 static GstStaticCaps sw_template_caps = GST_STATIC_CAPS (CC_OVERLAY_CAPS);
80 static GstStaticPadTemplate src_template_factory =
81 GST_STATIC_PAD_TEMPLATE ("src",
84 GST_STATIC_CAPS (CC_OVERLAY_ALL_CAPS)
87 static GstStaticPadTemplate video_sink_template_factory =
88 GST_STATIC_PAD_TEMPLATE ("video_sink",
91 GST_STATIC_CAPS (CC_OVERLAY_ALL_CAPS)
94 static GstStaticPadTemplate cc_sink_template_factory =
95 GST_STATIC_PAD_TEMPLATE ("cc_sink",
99 ("closedcaption/x-cea-708, format={ (string) cdp, (string) cc_data }")
103 #define GST_TYPE_CC_OVERLAY_WIN_H_POS (gst_cea_cc_overlay_h_pos_get_type())
105 gst_cea_cc_overlay_h_pos_get_type (void)
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"},
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);
120 return cc_overlay_win_h_pos_type;
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)))
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,
145 static gboolean gst_cea_cc_overlay_src_event (GstPad * pad, GstObject * parent,
147 static gboolean gst_cea_cc_overlay_src_query (GstPad * pad, GstObject * parent,
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);
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);
171 static gboolean gst_cea_cc_overlay_can_handle_caps (GstCaps * incaps);
174 gst_cea_cc_overlay_get_type (void)
176 static GType type = 0;
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,
183 (GClassInitFunc) gst_base_cea_cc_overlay_class_init,
186 sizeof (GstCeaCcOverlay),
188 (GInstanceInitFunc) gst_base_cea_cc_overlay_init,
191 g_once_init_leave ((gsize *) & type,
192 g_type_register_static (GST_TYPE_ELEMENT, "GstCeaCcOverlay", &info, 0));
199 gst_base_cea_cc_overlay_base_init (gpointer g_class)
201 GstCeaCcOverlayClass *klass = GST_CEA_CC_OVERLAY_CLASS (g_class);
202 PangoFontMap *fontmap;
204 /* Only lock for the subclasses here, the base class
205 * doesn't have this mutex yet and it's not necessary
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);
219 gst_base_cea_cc_overlay_class_init (GstCeaCcOverlayClass * klass)
221 GObjectClass *gobject_class;
222 GstElementClass *gstelement_class;
224 gobject_class = (GObjectClass *) klass;
225 gstelement_class = (GstElementClass *) klass;
227 GST_DEBUG_CATEGORY_INIT (gst_cea_cc_overlay_debug, "cc708overlay", 0,
230 parent_class = g_type_class_peek_parent (klass);
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;
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));
243 gstelement_class->change_state =
244 GST_DEBUG_FUNCPTR (gst_cea_cc_overlay_change_state);
246 klass->pango_lock = g_slice_new (GMutex);
247 g_mutex_init (klass->pango_lock);
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));
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));
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));
270 * GstCeaCcOverlay:silent:
272 * If set, no text is rendered. Useful to switch off text rendering
273 * temporarily without removing the textoverlay element from the pipeline.
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",
280 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
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 ();
291 gst_cea_cc_overlay_finalize (GObject * object)
293 GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
295 if (overlay->current_composition) {
296 gst_video_overlay_composition_unref (overlay->current_composition);
297 overlay->current_composition = NULL;
299 if (overlay->next_composition) {
300 gst_video_overlay_composition_unref (overlay->next_composition);
301 overlay->next_composition = NULL;
304 gst_cea708dec_free (overlay->decoder);
305 overlay->decoder = NULL;
307 g_mutex_clear (&overlay->lock);
308 g_cond_clear (&overlay->cond);
310 G_OBJECT_CLASS (parent_class)->finalize (object);
314 gst_base_cea_cc_overlay_init (GstCeaCcOverlay * overlay,
315 GstCeaCcOverlayClass * klass)
317 GstPadTemplate *template;
318 overlay->decoder = gst_cea708dec_create (GST_CEA_CC_OVERLAY_GET_CLASS
319 (overlay)->pango_context);
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);
335 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "cc_sink");
338 overlay->cc_sinkpad = gst_pad_new_from_template (template, "cc_sink");
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);
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);
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;
374 g_mutex_init (&overlay->lock);
375 g_cond_init (&overlay->cond);
376 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
379 /* only negotiate/query video overlay composition support for now */
381 gst_cea_cc_overlay_negotiate (GstCeaCcOverlay * overlay, GstCaps * caps)
384 gboolean attach = FALSE;
385 gboolean caps_has_meta = TRUE;
388 GstCaps *original_caps;
389 gboolean original_has_meta = FALSE;
390 gboolean allocation_ret = TRUE;
392 GST_DEBUG_OBJECT (overlay, "performing negotiation");
395 caps = gst_pad_get_current_caps (overlay->video_sinkpad);
399 if (!caps || gst_caps_is_empty (caps))
402 original_caps = caps;
404 /* Try to use the overlay meta if possible */
405 f = gst_caps_get_features (caps, 0);
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 */
411 || !gst_caps_features_contains (f,
412 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION)) {
413 GstCaps *overlay_caps;
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);
419 f = gst_caps_get_features (overlay_caps, 0);
420 gst_caps_features_add (f,
421 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
423 ret = gst_pad_peer_query_accept_caps (overlay->srcpad, overlay_caps);
424 GST_DEBUG_OBJECT (overlay, "Downstream accepts the overlay meta: %d", ret);
426 gst_caps_unref (caps);
430 /* fallback to the original */
431 gst_caps_unref (overlay_caps);
432 caps_has_meta = FALSE;
435 original_has_meta = TRUE;
437 GST_DEBUG_OBJECT (overlay, "Using caps %" GST_PTR_FORMAT, caps);
438 ret = gst_pad_set_caps (overlay->srcpad, caps);
441 /* find supported meta */
442 query = gst_query_new_allocation (caps, FALSE);
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;
450 if (caps_has_meta && gst_query_find_allocation_meta (query,
451 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
453 gst_query_unref (query);
456 overlay->attach_compo_to_buffer = attach;
458 if (!allocation_ret && overlay->video_flushing) {
460 } else if (original_caps && !original_has_meta && !attach) {
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))
474 GST_DEBUG_OBJECT (overlay, "negotiation failed, schedule reconfigure");
475 gst_pad_mark_reconfigure (overlay->srcpad);
477 gst_caps_unref (caps);
478 GST_DEBUG_OBJECT (overlay, "ret=%d", ret);
485 gst_caps_unref (caps);
491 gst_cea_cc_overlay_can_handle_caps (GstCaps * incaps)
495 static GstStaticCaps static_caps = GST_STATIC_CAPS (CC_OVERLAY_CAPS);
497 caps = gst_static_caps_get (&static_caps);
498 ret = gst_caps_is_subset (incaps, caps);
499 gst_caps_unref (caps);
505 gst_cea_cc_overlay_setcaps (GstCeaCcOverlay * overlay, GstCaps * caps)
508 gboolean ret = FALSE;
510 if (!gst_video_info_from_caps (&info, caps))
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,
519 ret = gst_cea_cc_overlay_negotiate (overlay, caps);
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);
529 g_mutex_unlock (GST_CEA_CC_OVERLAY_GET_CLASS (overlay)->pango_lock);
530 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
537 GST_DEBUG_OBJECT (overlay, "could not parse caps");
543 gst_cea_cc_overlay_set_property (GObject * object, guint prop_id,
544 const GValue * value, GParamSpec * pspec)
546 GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
547 Cea708Dec *decoder = overlay->decoder;
549 GST_CEA_CC_OVERLAY_LOCK (overlay);
551 case PROP_SERVICE_NUMBER:
553 int desired_service = g_value_get_int (value);
554 gst_cea708dec_set_service_number (decoder, desired_service);
559 PangoFontDescription *desc = NULL;
560 const gchar *fontdesc_str;
561 fontdesc_str = g_value_get_string (value);
563 GST_LOG_OBJECT (overlay, "Got font description '%s'", fontdesc_str);
565 desc = pango_font_description_from_string (fontdesc_str);
566 /* Only set if NULL or valid description */
567 if (desc || !fontdesc_str) {
569 GST_INFO_OBJECT (overlay, "Setting font description: '%s'",
571 pango_font_description_free (desc);
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);
580 overlay->silent = g_value_get_boolean (value);
582 case PROP_WINDOW_H_POS:
583 overlay->default_window_h_pos = g_value_get_enum (value);
586 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590 overlay->need_update = TRUE;
591 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
595 gst_cea_cc_overlay_get_property (GObject * object, guint prop_id,
596 GValue * value, GParamSpec * pspec)
598 GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (object);
599 Cea708Dec *decoder = overlay->decoder;
601 GST_CEA_CC_OVERLAY_LOCK (overlay);
603 case PROP_SERVICE_NUMBER:
604 g_value_set_int (value, decoder->desired_service);
607 g_value_set_boolean (value, overlay->silent);
610 g_value_set_string (value, decoder->default_font_desc);
612 case PROP_WINDOW_H_POS:
613 g_value_set_enum (value, overlay->default_window_h_pos);
616 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
620 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
624 gst_cea_cc_overlay_src_query (GstPad * pad, GstObject * parent,
627 gboolean ret = FALSE;
628 GstCeaCcOverlay *overlay;
630 overlay = GST_CEA_CC_OVERLAY (parent);
632 switch (GST_QUERY_TYPE (query)) {
635 GstCaps *filter, *caps;
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);
645 ret = gst_pad_query_default (pad, parent, query);
653 gst_cea_cc_overlay_src_event (GstPad * pad, GstObject * parent,
656 GstCeaCcOverlay *overlay;
659 overlay = GST_CEA_CC_OVERLAY (parent);
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);
666 ret = gst_pad_push_event (overlay->video_sinkpad, event);
673 * gst_cea_cc_overlay_add_feature_and_intersect:
675 * Creates a new #GstCaps containing the (given caps +
676 * given caps feature) + (given caps intersected by the
679 * Returns: the new #GstCaps
682 gst_cea_cc_overlay_add_feature_and_intersect (GstCaps * caps,
683 const gchar * feature, GstCaps * filter)
688 new_caps = gst_caps_copy (caps);
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);
694 if (!gst_caps_features_is_any (features)) {
695 gst_caps_features_add (features, feature);
699 gst_caps_append (new_caps, gst_caps_intersect_full (caps,
700 filter, GST_CAPS_INTERSECT_FIRST));
706 * gst_cea_cc_overlay_intersect_by_feature:
708 * Creates a new #GstCaps based on the following filtering rule.
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.
715 * Returns: the new #GstCaps
718 gst_cea_cc_overlay_intersect_by_feature (GstCaps * caps,
719 const gchar * feature, GstCaps * filter)
724 new_caps = gst_caps_new_empty ();
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);
736 if (gst_caps_features_contains (caps_features, feature)) {
737 gst_caps_append (new_caps, gst_caps_copy (simple_caps));
739 gst_caps_features_remove (caps_features, feature);
740 filtered_caps = gst_caps_ref (simple_caps);
742 filtered_caps = gst_caps_intersect_full (simple_caps, filter,
743 GST_CAPS_INTERSECT_FIRST);
745 gst_caps_unref (simple_caps);
746 gst_caps_append (new_caps, filtered_caps);
753 gst_cea_cc_overlay_get_videosink_caps (GstPad * pad,
754 GstCeaCcOverlay * overlay, GstCaps * filter)
756 GstPad *srcpad = overlay->srcpad;
757 GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
759 if (G_UNLIKELY (!overlay))
760 return gst_pad_get_pad_template_caps (pad);
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);
770 GST_DEBUG_OBJECT (overlay, "overlay filter %" GST_PTR_FORMAT,
774 peer_caps = gst_pad_peer_query_caps (srcpad, overlay_filter);
776 gst_caps_unref (overlay_filter);
779 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps);
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));
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);
794 gst_caps_unref (peer_caps);
797 /* no peer, our padtemplate is enough then */
798 caps = gst_pad_get_pad_template_caps (pad);
802 GstCaps *intersection = gst_caps_intersect_full (filter, caps,
803 GST_CAPS_INTERSECT_FIRST);
804 gst_caps_unref (caps);
808 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
814 gst_cea_cc_overlay_get_src_caps (GstPad * pad, GstCeaCcOverlay * overlay,
817 GstPad *sinkpad = overlay->video_sinkpad;
818 GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
820 if (G_UNLIKELY (!overlay))
821 return gst_pad_get_pad_template_caps (pad);
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
827 GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
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);
834 peer_caps = gst_pad_peer_query_caps (sinkpad, overlay_filter);
837 gst_caps_unref (overlay_filter);
841 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps);
843 if (gst_caps_is_any (peer_caps)) {
845 /* if peer returns ANY caps, return filtered sink pad template caps */
846 caps = gst_caps_copy (gst_pad_get_pad_template_caps (sinkpad));
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);
858 gst_caps_unref (peer_caps);
861 /* no peer, our padtemplate is enough then */
862 caps = gst_pad_get_pad_template_caps (pad);
866 GstCaps *intersection;
869 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
870 gst_caps_unref (caps);
873 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
878 /* FIXME: should probably be relative to width/height (adjusted for PAR) */
883 gst_cea_cc_overlay_push_frame (GstCeaCcOverlay * overlay,
884 GstBuffer * video_frame)
888 if (overlay->current_composition == NULL)
890 GST_LOG_OBJECT (overlay, "gst_cea_cc_overlay_push_frame");
892 if (gst_pad_check_reconfigure (overlay->srcpad))
893 gst_cea_cc_overlay_negotiate (overlay, NULL);
895 video_frame = gst_buffer_make_writable (video_frame);
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);
904 if (!gst_video_frame_map (&frame, &overlay->info, video_frame,
908 gst_video_overlay_composition_blend (overlay->current_composition, &frame);
910 gst_video_frame_unmap (&frame);
914 return gst_pad_push (overlay->srcpad, video_frame);
919 gst_buffer_unref (video_frame);
924 static GstPadLinkReturn
925 gst_cea_cc_overlay_cc_pad_link (GstPad * pad, GstObject * parent, GstPad * peer)
927 GstCeaCcOverlay *overlay;
929 overlay = GST_CEA_CC_OVERLAY (parent);
930 if (G_UNLIKELY (!overlay))
931 return GST_PAD_LINK_REFUSED;
933 GST_DEBUG_OBJECT (overlay, "Closed Caption pad linked");
935 overlay->cc_pad_linked = TRUE;
937 return GST_PAD_LINK_OK;
941 gst_cea_cc_overlay_cc_pad_unlink (GstPad * pad, GstObject * parent)
943 GstCeaCcOverlay *overlay;
945 /* don't use gst_pad_get_parent() here, will deadlock */
946 overlay = GST_CEA_CC_OVERLAY (parent);
948 GST_DEBUG_OBJECT (overlay, "Closed Caption pad unlinked");
950 overlay->cc_pad_linked = FALSE;
952 gst_segment_init (&overlay->cc_segment, GST_FORMAT_UNDEFINED);
956 gst_cea_cc_overlay_cc_event (GstPad * pad, GstObject * parent, GstEvent * event)
958 gboolean ret = FALSE;
959 GstCeaCcOverlay *overlay = NULL;
961 overlay = GST_CEA_CC_OVERLAY (parent);
963 GST_LOG_OBJECT (overlay, "received event %s", GST_EVENT_TYPE_NAME (event));
965 switch (GST_EVENT_TYPE (event)) {
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");
979 case GST_EVENT_SEGMENT:
981 const GstSegment *segment;
983 overlay->cc_eos = FALSE;
985 gst_event_parse_segment (event, &segment);
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);
994 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
995 ("received non-TIME newsegment event on text input"));
998 gst_event_unref (event);
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);
1010 GstClockTime start, duration;
1012 gst_event_parse_gap (event, &start, &duration);
1013 if (GST_CLOCK_TIME_IS_VALID (duration))
1015 /* we do not expect another buffer until after gap,
1016 * so that is our position now */
1017 overlay->cc_segment.position = start;
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);
1025 gst_event_unref (event);
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);
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);
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);
1061 ret = gst_pad_event_default (pad, parent, event);
1069 gst_cea_cc_overlay_video_event (GstPad * pad, GstObject * parent,
1072 gboolean ret = FALSE;
1073 GstCeaCcOverlay *overlay = NULL;
1075 overlay = GST_CEA_CC_OVERLAY (parent);
1077 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
1079 switch (GST_EVENT_TYPE (event)) {
1080 case GST_EVENT_CAPS:
1084 gst_event_parse_caps (event, &caps);
1085 ret = gst_cea_cc_overlay_setcaps (overlay, caps);
1086 gst_event_unref (event);
1089 case GST_EVENT_SEGMENT:
1091 const GstSegment *segment;
1093 GST_DEBUG_OBJECT (overlay, "received new segment");
1095 gst_event_parse_segment (event, &segment);
1097 if (segment->format == GST_FORMAT_TIME) {
1098 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
1101 gst_segment_copy_into (segment, &overlay->segment);
1103 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
1104 ("received non-TIME newsegment event on video input"));
1107 ret = gst_pad_event_default (pad, parent, event);
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);
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);
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);
1135 ret = gst_pad_event_default (pad, parent, event);
1143 gst_cea_cc_overlay_video_query (GstPad * pad, GstObject * parent,
1146 gboolean ret = FALSE;
1147 GstCeaCcOverlay *overlay;
1149 overlay = GST_CEA_CC_OVERLAY (parent);
1151 switch (GST_QUERY_TYPE (query)) {
1152 case GST_QUERY_CAPS:
1154 GstCaps *filter, *caps;
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);
1164 ret = gst_pad_query_default (pad, parent, query);
1171 /* Called with lock held */
1173 gst_cea_cc_overlay_pop_text (GstCeaCcOverlay * overlay)
1175 g_return_if_fail (GST_IS_CEA_CC_OVERLAY (overlay));
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;
1186 /* Let the text task know we used that buffer */
1187 GST_CEA_CC_OVERLAY_BROADCAST (overlay);
1191 gst_cea_cc_overlay_image_to_argb (guchar * pixbuf,
1192 cea708Window * window, int stride)
1198 width = window->image_width;
1199 height = window->image_height;
1201 for (i = 0; i < height; i++) {
1202 p = pixbuf + i * stride;
1203 bitp = window->text_image + i * width * 4;
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];
1211 /* Cairo uses pre-multiplied ARGB, unpremultiply it */
1212 CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]);
1221 gst_cea_cc_overlay_image_to_ayuv (guchar * pixbuf,
1222 cea708Window * window, int stride)
1224 int y; /* text bitmap coordinates */
1229 width = window->image_width;
1230 height = window->image_height;
1232 for (y = 0; y < height; y++) {
1234 p = pixbuf + y * stride;
1235 bitp = window->text_image + y * width * 4;
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];
1244 /* Cairo uses pre-multiplied ARGB, unpremultiply it */
1245 CAIRO_UNPREMULTIPLY (a, r, g, b);
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);
1259 gst_cea_cc_overlay_create_and_push_buffer (GstCeaCcOverlay * overlay)
1261 Cea708Dec *decoder = overlay->decoder;
1264 guint8 *window_image;
1267 cea708Window *window;
1270 GstVideoOverlayComposition *comp = NULL;
1271 GstVideoOverlayRectangle *rect = NULL;
1272 GST_CEA_CC_OVERLAY_LOCK (overlay);
1274 for (window_id = 0; window_id < 8; window_id++) {
1275 window = decoder->cc_windows[window_id];
1277 if (!window->updated) {
1280 if (!window->deleted && window->visible && window->text_image != NULL) {
1281 GST_DEBUG_OBJECT (overlay, "Allocating buffer");
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);
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;
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);
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;
1308 case GST_CEA_CC_OVERLAY_WIN_H_CENTER:
1309 window->h_offset = (overlay->width - window->image_width) / 2;
1311 case GST_CEA_CC_OVERLAY_WIN_H_RIGHT:
1312 window->h_offset = overlay->width - window->image_width;
1314 case GST_CEA_CC_OVERLAY_WIN_H_AUTO:
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;
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;
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;
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;
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;
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;
1361 if (decoder->use_ARGB) {
1362 gst_cea_cc_overlay_image_to_argb (window_image, window,
1363 window->image_width * 4);
1365 gst_cea_cc_overlay_image_to_ayuv (window_image, window,
1366 window->image_width * 4);
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);
1375 gst_video_overlay_rectangle_new_raw (outbuf, window->h_offset,
1376 window->v_offset, window->image_width, window->image_height, 0);
1378 comp = gst_video_overlay_composition_new (rect);
1380 gst_video_overlay_composition_add_rectangle (comp, rect);
1382 gst_video_overlay_rectangle_unref (rect);
1383 gst_buffer_unref (outbuf);
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));
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);
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;
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);
1421 gst_cea_cc_overlay_process_packet (GstCeaCcOverlay * overlay, guint8 cc_type)
1423 gint16 *index = NULL;
1424 guint8 *buffer = NULL;
1425 guint8 *dtvcc_buffer = NULL;
1426 gboolean need_render = FALSE;
1429 case CCTYPE_608_CC1:
1430 case CCTYPE_608_CC2:
1431 index = &overlay->cea608_index[cc_type];
1432 buffer = overlay->cea608_buffer[cc_type];
1435 case CCTYPE_708_ADD:
1436 case CCTYPE_708_START:
1437 index = &overlay->cea708_index;
1438 buffer = overlay->cea708_buffer;
1441 GST_ERROR_OBJECT (overlay,
1442 "attempted to process packet for unknown cc_type %d", cc_type);
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);
1455 gst_cea708dec_process_dtvcc_packet (overlay->decoder, dtvcc_buffer,
1457 g_free (dtvcc_buffer);
1459 gst_cea_cc_overlay_create_and_push_buffer (overlay);
1467 * gst_cea_cc_overlay_user_data_decode:
1468 * @overlay: The #GstCeaCcOverlay
1469 * @user_data: The #GstMpegVideoCCData to decode
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
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)
1482 * If cc_valid != 1, then ignore this packet
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
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)
1501 gst_cea_cc_overlay_user_data_decode (GstCeaCcOverlay * overlay,
1502 const guint8 * ccdata, gsize ccsize)
1511 cc_count = ccsize / 3;
1513 for (i = 0; i < cc_count; i++) {
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);
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]);
1524 /* accumulate dvtcc packet */
1526 case CCTYPE_608_CC1:
1527 case CCTYPE_608_CC2:
1529 if (overlay->cea608_index[cc_type] <= DTVCC_LENGTH - 2) {
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);
1535 overlay->cea608_buffer[cc_type][overlay->
1536 cea608_index[cc_type]++] = cc_data[j];
1539 GST_ERROR_OBJECT (overlay, "cea608_buffer[%d] overflow!", cc_type);
1544 case CCTYPE_708_ADD:
1545 case CCTYPE_708_START:
1547 if (cc_type == CCTYPE_708_START) {
1548 /* The previous packet is complete */
1549 gst_cea_cc_overlay_process_packet (overlay, cc_type);
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];
1556 GST_ERROR_OBJECT (overlay, "cea708_buffer overflow!");
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);
1568 /* FIXME : Move to GstVideo ANC/CC helper library */
1570 extract_ccdata_from_cdp (const guint8 * indata, gsize insize,
1571 const guint8 ** ccdata, gsize * ccsize)
1576 #ifndef GST_DISABLE_GST_DEBUG
1577 guint8 framerate_code;
1581 GST_MEMDUMP ("CDP", indata, insize);
1583 gst_byte_reader_init (&br, indata, insize);
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)
1591 if (gst_byte_reader_get_uint16_be_unchecked (&br) != 0x9669) {
1592 GST_WARNING ("Invalid CDP header");
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);
1601 #ifndef GST_DISABLE_GST_DEBUG
1602 framerate_code = gst_byte_reader_get_uint8_unchecked (&br) >> 4;
1604 gst_byte_reader_skip (&br, 1);
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);
1610 gst_byte_reader_skip (&br, 2);
1614 ("framerate_code : 0x%02x , flags : 0x%02x , sequencer_counter : %u",
1615 framerate_code, flags, seqhdr);
1617 /* Skip timecode if present */
1619 GST_LOG ("Skipping timecode section");
1620 gst_byte_reader_skip (&br, 5);
1625 guint8 ccid, cc_count;
1626 if (!gst_byte_reader_get_uint8 (&br, &ccid) ||
1627 !gst_byte_reader_get_uint8 (&br, &cc_count))
1630 GST_WARNING ("Invalid ccdata_id (expected 0x72, got 0x%02x)", ccid);
1634 if (!gst_byte_reader_get_data (&br, cc_count * 3, ccdata)) {
1635 GST_WARNING ("Not enough ccdata");
1640 *ccsize = cc_count * 3;
1643 /* FIXME : Parse/validate the rest of the CDP ! */
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,
1655 GstFlowReturn ret = GST_FLOW_OK;
1656 GstCeaCcOverlay *overlay = (GstCeaCcOverlay *) parent;
1657 gboolean in_seg = FALSE;
1658 guint64 clip_start = 0, clip_stop = 0;
1660 GST_CEA_CC_OVERLAY_LOCK (overlay);
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");
1669 if (overlay->cc_eos) {
1670 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1672 GST_LOG_OBJECT (overlay, "closed caption EOS");
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)));
1682 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
1685 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
1686 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
1688 stop = GST_CLOCK_TIME_NONE;
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);
1700 GstMapInfo buf_map = { 0 };
1701 const guint8 *ccdata = NULL;
1704 overlay->cc_segment.position = clip_start;
1705 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
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);
1711 ccdata = buf_map.data;
1712 ccsize = buf_map.size;
1715 gst_cea_cc_overlay_user_data_decode (overlay, ccdata, ccsize);
1716 overlay->decoder->current_time = GST_BUFFER_PTS (buffer);
1718 gst_buffer_unmap (buffer, &buf_map);
1720 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1724 gst_buffer_unref (buffer);
1728 static GstFlowReturn
1729 gst_cea_cc_overlay_video_chain (GstPad * pad, GstObject * parent,
1732 GstCeaCcOverlay *overlay;
1733 GstFlowReturn ret = GST_FLOW_OK;
1734 gboolean in_seg = FALSE;
1735 guint64 start, stop, clip_start = 0, clip_stop = 0;
1737 overlay = GST_CEA_CC_OVERLAY (parent);
1739 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
1740 goto missing_timestamp;
1742 /* ignore buffers that are outside of the current segment */
1743 start = GST_BUFFER_TIMESTAMP (buffer);
1745 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
1746 stop = GST_CLOCK_TIME_NONE;
1748 stop = start + GST_BUFFER_DURATION (buffer);
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));
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;
1760 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
1761 &clip_start, &clip_stop);
1764 goto out_of_segment;
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;
1772 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
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) */
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);
1784 GST_LOG_OBJECT (overlay, "no duration, assuming minimal duration");
1785 stop = start + 1; /* we need to assume some interval */
1789 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
1793 GST_CEA_CC_OVERLAY_LOCK (overlay);
1795 if (overlay->video_flushing)
1798 if (overlay->video_eos)
1801 if (overlay->silent) {
1802 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1803 ret = gst_pad_push (overlay->srcpad, buffer);
1805 /* Update position */
1806 overlay->segment.position = clip_start;
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);
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;
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;
1825 GstClockTime vid_running_time_end;
1827 #ifndef GST_DISABLE_GST_DEBUG
1829 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
1832 vid_running_time_end =
1833 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
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);
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");
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;
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");
1868 valid_text_time = FALSE;
1871 /* If timestamp and duration are valid */
1872 if (valid_text_time) {
1874 gst_segment_to_running_time (&overlay->cc_segment,
1875 GST_FORMAT_TIME, overlay->current_comp_start_time);
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));
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);
1890 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
1891 ret = gst_cea_cc_overlay_push_frame (overlay, buffer);
1894 GST_CEA_CC_OVERLAY_LOCK (overlay);
1895 gst_cea_cc_overlay_pop_text (overlay);
1896 GST_CEA_CC_OVERLAY_UNLOCK (overlay);
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);
1905 /* Update position */
1906 overlay->segment.position = clip_start;
1907 GST_DEBUG_OBJECT (overlay, "ret=%d", ret);
1913 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
1914 gst_buffer_unref (buffer);
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;
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;
1934 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
1935 gst_buffer_unref (buffer);
1940 static GstStateChangeReturn
1941 gst_cea_cc_overlay_change_state (GstElement * element,
1942 GstStateChange transition)
1944 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1945 GstCeaCcOverlay *overlay = GST_CEA_CC_OVERLAY (element);
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);
1961 ret = parent_class->change_state (element, transition);
1962 if (ret == GST_STATE_CHANGE_FAILURE)
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);