3 * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.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.
27 #include <gst/base/base.h>
28 #include <gst/video/video.h>
31 #include "gstccconverter.h"
33 GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
34 #define GST_CAT_DEFAULT gst_cc_converter_debug
37 * GstCCConverterCDPMode:
38 * @GST_CC_CONVERTER_CDP_MODE_TIME_CODE: Store time code information in CDP packets
39 * @GST_CC_CONVERTER_CDP_MODE_CC_DATA: Store CC data in CDP packets
40 * @GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO: Store CC service information in CDP packets
51 #define DEFAULT_CDP_MODE (GST_CC_CONVERTER_CDP_MODE_TIME_CODE | GST_CC_CONVERTER_CDP_MODE_CC_DATA | GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO)
53 /* Ordered by the amount of information they can contain */
55 "closedcaption/x-cea-708,format=(string) cdp; " \
56 "closedcaption/x-cea-708,format=(string) cc_data; " \
57 "closedcaption/x-cea-608,format=(string) s334-1a; " \
58 "closedcaption/x-cea-608,format=(string) raw"
60 #define VAL_OR_0(v) ((v) ? (*(v)) : 0)
62 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
65 GST_STATIC_CAPS (CC_CAPS));
67 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
70 GST_STATIC_CAPS (CC_CAPS));
72 G_DEFINE_TYPE (GstCCConverter, gst_cc_converter, GST_TYPE_BASE_TRANSFORM);
73 #define parent_class gst_cc_converter_parent_class
75 #define GST_TYPE_CC_CONVERTER_CDP_MODE (gst_cc_converter_cdp_mode_get_type())
77 gst_cc_converter_cdp_mode_get_type (void)
79 static const GFlagsValue values[] = {
80 {GST_CC_CONVERTER_CDP_MODE_TIME_CODE,
81 "Store time code information in CDP packets", "time-code"},
82 {GST_CC_CONVERTER_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
84 {GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO,
85 "Store CC service information in CDP packets", "cc-svc-info"},
88 static volatile GType id = 0;
90 if (g_once_init_enter ((gsize *) & id)) {
93 _id = g_flags_register_static ("GstCCConverterCDPMode", values);
95 g_once_init_leave ((gsize *) & id, _id);
102 gst_cc_converter_transform_size (GstBaseTransform * base,
103 GstPadDirection direction,
104 GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
106 /* We can't really convert from an output size to an input size */
107 if (direction != GST_PAD_SINK)
110 /* Assume worst-case here and over-allocate, and in ::transform() we then
111 * downsize the buffer as needed. The worst-case is one CDP packet, which
112 * can be up to MAX_CDP_PACKET_LEN bytes large */
114 *othersize = MAX_CDP_PACKET_LEN;
120 gst_cc_converter_transform_caps (GstBaseTransform * base,
121 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
123 static GstStaticCaps non_cdp_caps =
124 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cc_data; "
125 "closedcaption/x-cea-608,format=(string) s334-1a; "
126 "closedcaption/x-cea-608,format=(string) raw");
127 static GstStaticCaps cdp_caps =
128 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp");
129 static GstStaticCaps cdp_caps_framerate =
130 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp, "
131 "framerate=(fraction){60/1, 60000/1001, 50/1, 30/1, 30000/1001, 25/1, 24/1, 24000/1001}");
133 GstCCConverter *self = GST_CCCONVERTER (base);
135 GstCaps *res, *templ;
137 templ = gst_pad_get_pad_template_caps (base->srcpad);
139 res = gst_caps_new_empty ();
140 n = gst_caps_get_size (caps);
141 for (i = 0; i < n; i++) {
142 const GstStructure *s = gst_caps_get_structure (caps, i);
143 const GValue *framerate = gst_structure_get_value (s, "framerate");
145 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
147 if (direction == GST_PAD_SRC) {
148 /* SRC direction: We produce upstream caps
150 * Downstream wanted CEA608 caps. If it had a framerate, we
151 * also need upstream to provide exactly that same framerate
152 * and otherwise we don't care.
154 * We can convert everything to CEA608.
156 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
158 /* we can only keep the same framerate for non-cdp */
161 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
162 gst_caps_set_value (tmp, "framerate", framerate);
163 res = gst_caps_merge (res, tmp);
165 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
168 /* SINK: We produce downstream caps
170 * Upstream provided CEA608 caps. We can convert that to CDP if
171 * also a CDP compatible framerate was provided, and we can convert
172 * it to anything else regardless.
174 * If upstream provided a framerate we can pass that through, possibly
175 * filtered for the CDP case.
181 /* Create caps that contain the intersection of all framerates with
182 * the CDP allowed framerates */
184 gst_caps_make_writable (gst_static_caps_get
185 (&cdp_caps_framerate));
186 t = gst_caps_get_structure (tmp, 0);
187 gst_structure_set_name (t, "closedcaption/x-cea-608");
188 gst_structure_remove_field (t, "format");
189 if (gst_structure_can_intersect (s, t)) {
190 gst_caps_unref (tmp);
193 gst_caps_make_writable (gst_static_caps_get
194 (&cdp_caps_framerate));
196 res = gst_caps_merge (res, tmp);
198 gst_caps_unref (tmp);
200 /* And we can convert to everything else with the given framerate */
201 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
202 gst_caps_set_value (tmp, "framerate", framerate);
203 res = gst_caps_merge (res, tmp);
205 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
208 } else if (gst_structure_has_name (s, "closedcaption/x-cea-708")) {
209 if (direction == GST_PAD_SRC) {
210 /* SRC direction: We produce upstream caps
212 * Downstream wanted CEA708 caps. If downstream wants *only* CDP we
213 * either need CDP from upstream, or anything else with a CDP
215 * If downstream also wants non-CDP we can accept anything.
217 * We pass through any framerate as-is, except for filtering
218 * for CDP framerates if downstream wants only CDP.
221 if (g_strcmp0 (gst_structure_get_string (s, "format"), "cdp") == 0) {
222 /* Downstream wants only CDP */
224 /* We need CDP from upstream in that case */
225 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
227 /* Or anything else with a CDP framerate */
231 const GValue *cdp_framerate;
233 /* Create caps that contain the intersection of all framerates with
234 * the CDP allowed framerates */
236 gst_caps_make_writable (gst_static_caps_get
237 (&cdp_caps_framerate));
238 t = gst_caps_get_structure (tmp, 0);
240 /* There's an intersection between the framerates so we can convert
241 * into CDP with exactly those framerates from anything else */
242 cdp_framerate = gst_structure_get_value (t, "framerate");
243 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
244 gst_caps_set_value (tmp, "framerate", cdp_framerate);
245 res = gst_caps_merge (res, tmp);
247 GstCaps *tmp, *cdp_caps;
248 const GValue *cdp_framerate;
250 /* Get all CDP framerates, we can accept anything that has those
252 cdp_caps = gst_static_caps_get (&cdp_caps_framerate);
254 gst_structure_get_value (gst_caps_get_structure (cdp_caps, 0),
257 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
258 gst_caps_set_value (tmp, "framerate", cdp_framerate);
259 gst_caps_unref (cdp_caps);
261 res = gst_caps_merge (res, tmp);
264 /* Downstream wants not only CDP, we can do everything */
265 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
267 /* we can only keep the same framerate for non-cdp */
270 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
271 gst_caps_set_value (tmp, "framerate", framerate);
272 res = gst_caps_merge (res, tmp);
274 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
280 /* SINK: We produce downstream caps
282 * Upstream provided CEA708 caps. If upstream provided CDP we can
283 * output CDP, no matter what (-> passthrough). If upstream did not
284 * provide CDP, we can output CDP only if the framerate fits.
285 * We can always produce everything else apart from CDP.
287 * If upstream provided a framerate we pass that through for non-CDP
288 * output, and pass it through filtered for CDP output.
291 if (gst_structure_can_intersect (s,
292 gst_caps_get_structure (gst_static_caps_get (&cdp_caps), 0))) {
293 /* Upstream provided CDP caps, we can do everything independent of
295 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
296 } else if (framerate) {
297 const GValue *cdp_framerate;
300 /* Upstream did not provide CDP. We can only do CDP if upstream
301 * happened to have a CDP framerate */
303 /* Create caps that contain the intersection of all framerates with
304 * the CDP allowed framerates */
306 gst_caps_make_writable (gst_static_caps_get
307 (&cdp_caps_framerate));
308 t = gst_caps_get_structure (tmp, 0);
310 /* There's an intersection between the framerates so we can convert
311 * into CDP with exactly those framerates */
312 cdp_framerate = gst_structure_get_value (t, "framerate");
313 gst_caps_set_value (tmp, "framerate", cdp_framerate);
315 res = gst_caps_merge (res, tmp);
317 /* We can always convert CEA708 to all non-CDP formats */
319 /* we can only keep the same framerate for non-cdp */
322 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
323 gst_caps_set_value (tmp, "framerate", framerate);
324 res = gst_caps_merge (res, tmp);
326 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
330 g_assert_not_reached ();
334 GST_DEBUG_OBJECT (self, "pre filter caps %" GST_PTR_FORMAT, res);
336 /* We can convert anything into anything but it might involve loss of
337 * information so always filter according to the order in our template caps
341 filter = gst_caps_intersect_full (templ, filter, GST_CAPS_INTERSECT_FIRST);
343 tmp = gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
344 gst_caps_unref (res);
345 gst_caps_unref (filter);
349 gst_caps_unref (templ);
351 GST_DEBUG_OBJECT (self, "Transformed in direction %s caps %" GST_PTR_FORMAT,
352 direction == GST_PAD_SRC ? "src" : "sink", caps);
353 GST_DEBUG_OBJECT (self, "filter %" GST_PTR_FORMAT, filter);
354 GST_DEBUG_OBJECT (self, "to %" GST_PTR_FORMAT, res);
360 gst_cc_converter_fixate_caps (GstBaseTransform * base,
361 GstPadDirection direction, GstCaps * incaps, GstCaps * outcaps)
363 GstCCConverter *self = GST_CCCONVERTER (base);
364 const GstStructure *s;
366 const GValue *framerate;
367 GstCaps *intersection, *templ;
369 GST_DEBUG_OBJECT (self, "Fixating in direction %s incaps %" GST_PTR_FORMAT,
370 direction == GST_PAD_SRC ? "src" : "sink", incaps);
371 GST_DEBUG_OBJECT (self, "and outcaps %" GST_PTR_FORMAT, outcaps);
373 /* Prefer passthrough if we can */
374 if (gst_caps_is_subset (incaps, outcaps)) {
375 gst_caps_unref (outcaps);
376 return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base,
377 direction, incaps, gst_caps_ref (incaps));
380 /* Otherwise prefer caps in the order of our template caps */
381 templ = gst_pad_get_pad_template_caps (base->srcpad);
383 gst_caps_intersect_full (templ, outcaps, GST_CAPS_INTERSECT_FIRST);
384 gst_caps_unref (outcaps);
385 outcaps = intersection;
388 GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base, direction,
391 s = gst_caps_get_structure (incaps, 0);
392 framerate = gst_structure_get_value (s, "framerate");
393 outcaps = gst_caps_make_writable (outcaps);
394 t = gst_caps_get_structure (outcaps, 0);
396 /* remove any output framerate that might've been added by basetransform
397 * due to intersecting with downstream */
398 gst_structure_remove_field (t, "framerate");
400 /* or passthrough the input framerate if possible */
403 n = gst_value_get_fraction_numerator (framerate);
404 d = gst_value_get_fraction_denominator (framerate);
406 if (gst_structure_has_field (t, "framerate"))
407 gst_structure_fixate_field_nearest_fraction (t, "framerate", n, d);
409 gst_structure_set (t, "framerate", GST_TYPE_FRACTION, n, d, NULL);
412 GST_DEBUG_OBJECT (self,
413 "Fixated caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, incaps, outcaps);
419 gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps,
422 GstCCConverter *self = GST_CCCONVERTER (base);
423 const GstStructure *s;
424 gboolean passthrough;
426 self->input_caption_type = gst_video_caption_type_from_caps (incaps);
427 self->output_caption_type = gst_video_caption_type_from_caps (outcaps);
429 if (self->input_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN ||
430 self->output_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN)
433 s = gst_caps_get_structure (incaps, 0);
434 if (!gst_structure_get_fraction (s, "framerate", &self->in_fps_n,
436 self->in_fps_n = self->in_fps_d = 0;
438 s = gst_caps_get_structure (outcaps, 0);
439 if (!gst_structure_get_fraction (s, "framerate", &self->out_fps_n,
441 self->out_fps_n = self->out_fps_d = 0;
443 gst_video_time_code_clear (&self->current_output_timecode);
445 /* Caps can be different but we can passthrough as long as they can
446 * intersect, i.e. have same caps name and format */
447 passthrough = gst_caps_can_intersect (incaps, outcaps);
448 gst_base_transform_set_passthrough (base, passthrough);
450 GST_DEBUG_OBJECT (self,
451 "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)",
452 incaps, outcaps, passthrough);
458 GST_ERROR_OBJECT (self,
459 "Invalid caps: in %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, incaps,
471 guint max_cea608_count;
474 static const struct cdp_fps_entry cdp_fps_table[] = {
475 {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
476 {0x2f, 24, 1, 25, 22, 2},
477 {0x3f, 25, 1, 24, 22, 2},
478 {0x4f, 30000, 1001, 20, 18, 2},
479 {0x5f, 30, 1, 20, 18, 2},
480 {0x6f, 50, 1, 12, 11, 1},
481 {0x7f, 60000, 1001, 10, 9, 1},
482 {0x8f, 60, 1, 10, 9, 1},
484 static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
486 static const struct cdp_fps_entry *
487 cdp_fps_entry_from_id (guint8 id)
490 for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
491 if (cdp_fps_table[i].fps_idx == id)
492 return &cdp_fps_table[i];
494 return &null_fps_entry;
497 static const struct cdp_fps_entry *
498 cdp_fps_entry_from_fps (guint fps_n, guint fps_d)
501 for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
502 if (cdp_fps_table[i].fps_n == fps_n && cdp_fps_table[i].fps_d == fps_d)
503 return &cdp_fps_table[i];
505 return &null_fps_entry;
509 get_framerate_output_scale (GstCCConverter * self,
510 const struct cdp_fps_entry *in_fps_entry, gint * scale_n, gint * scale_d)
512 if (self->in_fps_n == 0 || self->out_fps_d == 0) {
518 /* compute the relative rates of the two framerates */
519 if (!gst_util_fraction_multiply (in_fps_entry->fps_d, in_fps_entry->fps_n,
520 self->out_fps_n, self->out_fps_d, scale_n, scale_d))
521 /* we should never overflow */
522 g_assert_not_reached ();
526 interpolate_time_code_with_framerate (GstCCConverter * self,
527 const GstVideoTimeCode * tc, gint out_fps_n, gint out_fps_d,
528 gint scale_n, gint scale_d, GstVideoTimeCode * out)
531 gint output_n, output_d;
533 GstVideoTimeCodeFlags flags;
535 g_return_val_if_fail (tc != NULL, FALSE);
536 g_return_val_if_fail (out != NULL, FALSE);
537 /* out_n/d can only be 0 if scale_n/d are 1/1 */
538 g_return_val_if_fail ((scale_n == 1 && scale_d == 1) || (out_fps_n != 0
539 && out_fps_d != 0), FALSE);
541 if (!tc || tc->config.fps_n == 0)
544 if (!gst_util_fraction_multiply (tc->frames, 1, scale_n, scale_d, &output_n,
546 /* we should never overflow */
547 g_assert_not_reached ();
549 tc_str = gst_video_time_code_to_string (tc);
550 GST_TRACE_OBJECT (self, "interpolating time code %s with scale %d/%d "
551 "to frame %d/%d", tc_str, scale_n, scale_d, output_n, output_d);
554 if (out_fps_n == 0 || out_fps_d == 0) {
555 out_fps_n = tc->config.fps_n;
556 out_fps_d = tc->config.fps_d;
559 flags = tc->config.flags;
560 if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0 && out_fps_d != 1001
561 && out_fps_n != 60000 && out_fps_n != 30000) {
562 flags &= ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
563 } else if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) == 0
564 && out_fps_d == 1001 && (out_fps_n == 60000 || out_fps_n == 30000)) {
565 /* XXX: theoretically, not quite correct however this is an assumption
566 * we have elsewhere that these framerates are always drop-framed */
567 flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
570 output_frame = output_n / output_d;
572 *out = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
574 /* here we try to find the next available valid timecode. The dropped
575 * (when they exist) frames in time codes are that the beginning of each
577 gst_video_time_code_clear (out);
578 gst_video_time_code_init (out, out_fps_n, out_fps_d,
579 tc->config.latest_daily_jam, flags, tc->hours, tc->minutes,
580 tc->seconds, output_frame, tc->field_count);
582 } while ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0
583 && output_frame < 10 && !gst_video_time_code_is_valid (out));
585 tc_str = gst_video_time_code_to_string (out);
586 GST_TRACE_OBJECT (self, "interpolated to %s", tc_str);
592 /* remove padding bytes from a cc_data packet. Returns the length of the new
593 * data in @cc_data */
595 compact_cc_data (guint8 * cc_data, guint cc_data_len)
597 gboolean started_ccp = FALSE;
601 if (cc_data_len % 3 != 0) {
602 GST_WARNING ("Invalid cc_data buffer size");
603 cc_data_len = cc_data_len - (cc_data_len % 3);
606 for (i = 0; i < cc_data_len / 3; i++) {
607 gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
608 guint8 cc_type = cc_data[i * 3] & 0x03;
610 if (!started_ccp && (cc_type == 0x00 || cc_type == 0x01)) {
612 /* copy over valid 608 data */
613 cc_data[out_len++] = cc_data[i * 3];
614 cc_data[out_len++] = cc_data[i * 3 + 1];
615 cc_data[out_len++] = cc_data[i * 3 + 2];
626 if (cc_type == 0x00 || cc_type == 0x01) {
627 GST_WARNING ("Invalid cc_data. cea608 bytes after cea708");
631 cc_data[out_len++] = cc_data[i * 3];
632 cc_data[out_len++] = cc_data[i * 3 + 1];
633 cc_data[out_len++] = cc_data[i * 3 + 2];
636 GST_LOG ("compacted cc_data from %u to %u", cc_data_len, out_len);
642 cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
643 guint8 * cea608_field1, guint * cea608_field1_len,
644 guint8 * cea608_field2, guint * cea608_field2_len)
646 guint i, field_1_len = 0, field_2_len = 0;
648 if (cea608_field1_len) {
649 field_1_len = *cea608_field1_len;
650 *cea608_field1_len = 0;
652 if (cea608_field2_len) {
653 field_2_len = *cea608_field2_len;
654 *cea608_field2_len = 0;
657 if (cc_data_len % 3 != 0) {
658 GST_WARNING ("Invalid cc_data buffer size %u. Truncating to a multiple "
659 "of 3", cc_data_len);
660 cc_data_len = cc_data_len - (cc_data_len % 3);
663 for (i = 0; i < cc_data_len / 3; i++) {
664 gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
665 guint8 cc_type = cc_data[i * 3] & 0x03;
667 GST_TRACE ("0x%02x 0x%02x 0x%02x, valid: %u, type: 0b%u%u",
668 cc_data[i * 3 + 0], cc_data[i * 3 + 1], cc_data[i * 3 + 2], cc_valid,
669 cc_type & 0x2, cc_type & 0x1);
671 if (cc_type == 0x00) {
675 if (cea608_field1 && cea608_field1_len) {
676 if (*cea608_field1_len + 2 > field_1_len) {
677 GST_WARNING ("Too many cea608 input bytes %u for field 1",
678 *cea608_field1_len + 2);
681 cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 1];
682 cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 2];
684 } else if (cc_type == 0x01) {
688 if (cea608_field2 && cea608_field2_len) {
689 if (*cea608_field2_len + 2 > field_2_len) {
690 GST_WARNING ("Too many cea608 input bytes %u for field 2",
691 *cea608_field2_len + 2);
694 cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 1];
695 cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 2];
698 /* all cea608 packets must be at the beginning of a cc_data */
703 g_assert_cmpint (i * 3, <=, cc_data_len);
705 GST_LOG ("Extracted cea608-1 of length %u and cea608-2 of length %u",
706 VAL_OR_0 (cea608_field1_len), VAL_OR_0 (cea608_field2_len));
712 store_cc_data (GstCCConverter * self, const guint8 * ccp_data,
713 guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len,
714 const guint8 * cea608_2, guint cea608_2_len)
716 GST_DEBUG_OBJECT (self, "holding data of len ccp:%u, cea608 1:%u, "
717 "cea608 2:%u until next input buffer", ccp_data_len, cea608_1_len,
720 if (ccp_data && ccp_data_len > 0) {
721 memcpy (self->scratch_ccp, ccp_data, ccp_data_len);
722 self->scratch_ccp_len = ccp_data_len;
724 self->scratch_ccp_len = 0;
726 g_assert_cmpint (self->scratch_ccp_len, <, sizeof (self->scratch_ccp));
728 if (cea608_1 && cea608_1_len > 0) {
729 memcpy (self->scratch_cea608_1, cea608_1, cea608_1_len);
730 self->scratch_cea608_1_len = cea608_1_len;
732 self->scratch_cea608_1_len = 0;
734 g_assert_cmpint (self->scratch_cea608_1_len, <,
735 sizeof (self->scratch_cea608_1));
737 if (cea608_2 && cea608_2_len > 0) {
738 memcpy (self->scratch_cea608_2, cea608_2, cea608_2_len);
739 self->scratch_cea608_2_len = cea608_2_len;
741 self->scratch_cea608_2_len = 0;
743 g_assert_cmpint (self->scratch_cea608_2_len, <,
744 sizeof (self->scratch_cea608_2));
748 combine_cc_data (GstCCConverter * self, gboolean pad_cea608,
749 const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data,
750 guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len,
751 const guint8 * cea608_2, guint cea608_2_len, guint8 * out, guint * out_size)
753 guint i = 0, out_i = 0, max_size = 0, cea608_1_i = 0, cea608_2_i = 0;
754 guint cea608_output_count;
755 guint total_cea608_1_count, total_cea608_2_count;
759 g_assert (!ccp_data || ccp_data_len % 3 == 0);
760 g_assert (!cea608_1 || cea608_1_len % 2 == 0);
761 g_assert (!cea608_2 || cea608_2_len % 2 == 0);
765 /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated,
766 * However that is not possible for 60fps (where only one cea608 field fits)
767 * without adding previous output buffer tracking */
768 g_assert_cmpint (cea608_1_len >= cea608_2_len);
770 g_assert_cmpint (cea608_1_len + cea608_2_len, <=,
771 out_fps_entry->max_cea608_count);
773 total_cea608_1_count = cea608_1_len;
774 total_cea608_2_count = cea608_2_len;
777 /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated. */
778 if (cea608_1_len < cea608_2_len)
779 total_cea608_1_count += cea608_2_len - cea608_1_len;
782 max_size = ccp_data_len + (total_cea608_1_count + total_cea608_2_count) * 3;
783 if (*out_size < max_size) {
784 GST_WARNING_OBJECT (self, "Output data too small (%u < %u)", *out_size,
789 /* FIXME: interlacing, tff, rff, ensuring cea608 field1 is generated if
790 * field2 exists even across packets */
792 cea608_output_count = cea608_1_len + cea608_2_len;
794 for (i = total_cea608_1_count + total_cea608_2_count;
795 i < out_fps_entry->max_cea608_count; i++) {
796 /* try to pad evenly */
797 if (i > cea608_1_len / 2)
798 total_cea608_1_count++;
800 total_cea608_2_count++;
801 cea608_output_count++;
805 GST_LOG ("writing %u cea608-1 fields and %u cea608-2 fields",
806 total_cea608_1_count, total_cea608_2_count);
807 g_assert_cmpint (total_cea608_1_count + total_cea608_2_count, <=,
808 out_fps_entry->max_cea608_count);
810 while (cea608_1_i + cea608_2_i < cea608_output_count) {
811 if (cea608_1_i < cea608_1_len) {
813 out[out_i++] = cea608_1[cea608_1_i * 2];
814 out[out_i++] = cea608_1[cea608_1_i * 2 + 1];
817 } else if (cea608_1_i < total_cea608_1_count) {
825 if (cea608_2_i < cea608_2_len) {
827 out[out_i++] = cea608_2[cea608_2_i * 2];
828 out[out_i++] = cea608_2[cea608_2_i * 2 + 1];
831 } else if (cea608_2_i < total_cea608_2_count) {
840 g_assert_cmpint (out_i / 3, <=, out_fps_entry->max_cea608_count);
845 memcpy (&out[out_i], ccp_data, ccp_data_len);
846 *out_size += ccp_data_len;
849 g_assert_cmpint (*out_size, <, MAX_CDP_PACKET_LEN);
854 /* takes cc_data cea608_1, cea608_2 and attempts to fit it into a hypothetical
855 * output packet. Any leftover data is stored for later addition. Returns
856 * whether any output can be generated. @ccp_data_len, @cea608_1_len,
857 * @cea608_2_len are also updated to reflect the size of that data to add to
858 * the output packet */
860 fit_and_scale_cc_data (GstCCConverter * self,
861 const struct cdp_fps_entry *in_fps_entry,
862 const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data,
863 guint * ccp_data_len, const guint8 * cea608_1, guint * cea608_1_len,
864 const guint8 * cea608_2, guint * cea608_2_len, const GstVideoTimeCode * tc)
866 if (!in_fps_entry || in_fps_entry->fps_n == 0) {
867 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
868 if (!in_fps_entry || in_fps_entry->fps_n == 0)
869 g_assert_not_reached ();
872 /* This is slightly looser than checking for the exact framerate as the cdp
873 * spec allow for 0.1% difference between framerates to be considered equal */
874 if (in_fps_entry->max_cc_count == out_fps_entry->max_cc_count) {
875 if (tc && tc->config.fps_n != 0)
876 interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
877 out_fps_entry->fps_d, 1, 1, &self->current_output_timecode);
879 int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
880 int output_time_cmp, scale_n, scale_d, rate_cmp;
882 /* TODO: handle input discont */
884 /* compute the relative frame count for each */
885 if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
886 self->input_frames, 1, &input_frame_n, &input_frame_d))
887 /* we should never overflow */
888 g_assert_not_reached ();
890 if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
891 self->output_frames, 1, &output_frame_n, &output_frame_d))
892 /* we should never overflow */
893 g_assert_not_reached ();
895 output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
896 output_frame_n, output_frame_d);
898 /* compute the relative rates of the two framerates */
899 get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
901 rate_cmp = gst_util_fraction_compare (scale_n, scale_d, 1, 1);
903 GST_TRACE_OBJECT (self, "performing framerate conversion at scale %d/%d "
904 "of cc data of with sizes, ccp:%u, cea608-1:%u, cea608-2:%u", scale_n,
905 scale_d, VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
906 VAL_OR_0 (cea608_2_len));
909 /* we are not scaling. Should never happen with current conditions
911 g_assert_not_reached ();
912 } else if (output_time_cmp < 0) {
913 /* we can't generate an output yet */
914 guint cd_len = ccp_data_len ? *ccp_data_len : 0;
915 guint c1_len = cea608_1_len ? *cea608_1_len : 0;
916 guint c2_len = cea608_2_len ? *cea608_2_len : 0;
918 store_cc_data (self, ccp_data, cd_len, cea608_1, c1_len, cea608_2,
927 } else if (rate_cmp != 0) {
928 /* we are changing the framerate and may overflow the max output packet
929 * size. Split them where necessary. */
930 gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0;
931 gint ccp_off = 0, cea608_1_off = 0, cea608_2_off = 0;
933 if (output_time_cmp == 0) {
934 /* we have completed a cycle and can reset our counters to avoid
935 * overflow. Anything that fits into the output packet will be written */
936 GST_LOG_OBJECT (self, "cycle completed, resetting frame counters");
937 self->scratch_ccp_len = 0;
938 self->scratch_cea608_1_len = 0;
939 self->scratch_cea608_2_len = 0;
940 self->input_frames = 0;
941 self->output_frames = 0;
945 extra_ccp = *ccp_data_len - 3 * out_fps_entry->max_ccp_count;
946 extra_ccp = MAX (0, extra_ccp);
947 ccp_off = *ccp_data_len - extra_ccp;
951 extra_cea608_1 = *cea608_1_len - 2 * out_fps_entry->max_cea608_count;
952 extra_cea608_1 = MAX (0, extra_cea608_1);
953 cea608_1_off = *cea608_1_len - extra_cea608_1;
957 /* this prefers using field1 data first. This may not be quite correct */
958 if (extra_cea608_1 > 0) {
959 /* all the cea608 space is for field 1 */
960 extra_cea608_2 = *cea608_2_len;
962 } else if (cea608_1_len) {
963 /* cea608 space is shared between field 1 and field 2 */
965 *cea608_1_len + *cea608_2_len -
966 2 * out_fps_entry->max_cea608_count;
967 extra_cea608_2 = MAX (0, extra_cea608_2);
968 cea608_2_off = *cea608_2_len - extra_cea608_2;
970 /* all of the cea608 space is for field 2 */
971 extra_cea608_2 = *cea608_2_len - 2 * out_fps_entry->max_cea608_count;
972 extra_cea608_2 = MAX (0, extra_cea608_2);
973 cea608_2_off = *cea608_2_len - extra_cea608_2;
977 if (extra_ccp > 0 || extra_cea608_1 > 0 || extra_cea608_2 > 0) {
978 /* packet would overflow, push extra bytes into the next packet */
979 GST_DEBUG_OBJECT (self, "buffer would overflow by %u ccp bytes, "
980 "%u cea608 field 1 bytes, or %u cea608 field 2 bytes", extra_ccp,
981 extra_cea608_1, extra_cea608_2);
982 store_cc_data (self, &ccp_data[ccp_off], extra_ccp,
983 &cea608_1[cea608_1_off], extra_cea608_1, &cea608_2[cea608_2_off],
986 *ccp_data_len = MIN (*ccp_data_len, ccp_off);
988 *cea608_1_len = MIN (*cea608_1_len, cea608_1_off);
990 *cea608_2_len = MIN (*cea608_2_len, cea608_2_off);
992 GST_DEBUG_OBJECT (self, "section sizes of %u ccp bytes, "
993 "%u cea608 field 1 bytes, and %u cea608 field 2 bytes fit within "
994 "output packet", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
995 VAL_OR_0 (cea608_2_len));
996 self->scratch_ccp_len = 0;
997 self->scratch_cea608_1_len = 0;
998 self->scratch_cea608_2_len = 0;
1001 g_assert_not_reached ();
1004 if (tc && tc->config.fps_n != 0)
1005 interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
1006 out_fps_entry->fps_d, scale_n, scale_d,
1007 &self->current_output_timecode);
1010 g_assert_cmpint (VAL_OR_0 (ccp_data_len) + (VAL_OR_0 (cea608_1_len) +
1011 VAL_OR_0 (cea608_2_len)) / 2 * 3, <=,
1012 3 * out_fps_entry->max_cc_count);
1014 GST_DEBUG_OBJECT (self, "write out packet with lengths ccp:%u, cea608-1:%u, "
1015 "cea608-2:%u", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
1016 VAL_OR_0 (cea608_2_len));
1021 /* Converts raw CEA708 cc_data and an optional timecode into CDP */
1023 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
1024 const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
1025 const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
1028 guint8 flags, checksum;
1031 GST_DEBUG_OBJECT (self, "writing out cdp packet from cc_data with length %u",
1034 gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
1035 gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
1036 /* Write a length of 0 for now */
1037 gst_byte_writer_put_uint8_unchecked (&bw, 0);
1039 gst_byte_writer_put_uint8_unchecked (&bw, fps_entry->fps_idx);
1041 if (cc_data_len / 3 > fps_entry->max_cc_count) {
1042 GST_WARNING_OBJECT (self, "Too many cc_data triplets for framerate: %u. "
1043 "Truncating to %u", cc_data_len / 3, fps_entry->max_cc_count);
1044 cc_data_len = 3 * fps_entry->max_cc_count;
1047 /* caption_service_active */
1050 /* ccdata_present */
1051 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA))
1054 /* time_code_present */
1055 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
1056 && tc->config.fps_n > 0)
1062 gst_byte_writer_put_uint8_unchecked (&bw, flags);
1064 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
1066 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
1067 && tc->config.fps_n > 0) {
1070 gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
1071 /* reserved 11 - 2 bits */
1073 /* tens of hours - 2 bits */
1074 u8 |= ((tc->hours / 10) & 0x3) << 4;
1075 /* units of hours - 4 bits */
1076 u8 |= (tc->hours % 10) & 0xf;
1077 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1079 /* reserved 1 - 1 bit */
1081 /* tens of minutes - 3 bits */
1082 u8 |= ((tc->minutes / 10) & 0x7) << 4;
1083 /* units of minutes - 4 bits */
1084 u8 |= (tc->minutes % 10) & 0xf;
1085 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1087 /* field flag - 1 bit */
1088 u8 = tc->field_count < 2 ? 0x00 : 0x80;
1089 /* tens of seconds - 3 bits */
1090 u8 |= ((tc->seconds / 10) & 0x7) << 4;
1091 /* units of seconds - 4 bits */
1092 u8 |= (tc->seconds % 10) & 0xf;
1093 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1095 /* drop frame flag - 1 bit */
1096 u8 = (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
1098 /* reserved0 - 1 bit */
1099 /* tens of frames - 2 bits */
1100 u8 |= ((tc->frames / 10) & 0x3) << 4;
1101 /* units of frames 4 bits */
1102 u8 |= (tc->frames % 10) & 0xf;
1103 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1106 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA)) {
1107 gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
1108 gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | fps_entry->max_cc_count);
1109 gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
1110 while (fps_entry->max_cc_count > cc_data_len / 3) {
1111 gst_byte_writer_put_uint8_unchecked (&bw, 0xfa);
1112 gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
1113 gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
1118 gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
1119 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
1120 self->cdp_hdr_sequence_cntr++;
1121 /* We calculate the checksum afterwards */
1122 gst_byte_writer_put_uint8_unchecked (&bw, 0);
1124 len = gst_byte_writer_get_pos (&bw);
1125 gst_byte_writer_set_pos (&bw, 2);
1126 gst_byte_writer_put_uint8_unchecked (&bw, len);
1129 for (i = 0; i < len; i++) {
1133 checksum = 256 - checksum;
1134 cdp[len - 1] = checksum;
1139 /* Converts CDP into raw CEA708 cc_data */
1141 convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
1142 const guint8 * cdp, guint cdp_len, guint8 cc_data[MAX_CDP_PACKET_LEN],
1143 GstVideoTimeCode * tc, const struct cdp_fps_entry **out_fps_entry)
1150 const struct cdp_fps_entry *fps_entry;
1152 *out_fps_entry = &null_fps_entry;
1153 memset (tc, 0, sizeof (*tc));
1155 /* Header + footer length */
1157 GST_WARNING_OBJECT (self, "cdp packet too short (%u). expected at "
1158 "least %u", cdp_len, 11);
1162 gst_byte_reader_init (&br, cdp, cdp_len);
1163 u16 = gst_byte_reader_get_uint16_be_unchecked (&br);
1164 if (u16 != 0x9669) {
1165 GST_WARNING_OBJECT (self, "cdp packet does not have initial magic bytes "
1170 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1171 if (u8 != cdp_len) {
1172 GST_WARNING_OBJECT (self, "cdp packet length (%u) does not match passed "
1173 "in value (%u)", u8, cdp_len);
1177 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1178 fps_entry = cdp_fps_entry_from_id (u8);
1179 if (!fps_entry || fps_entry->fps_n == 0) {
1180 GST_WARNING_OBJECT (self, "cdp packet does not have a valid framerate "
1185 flags = gst_byte_reader_get_uint8_unchecked (&br);
1187 if ((flags & 0x40) == 0) {
1188 GST_DEBUG_OBJECT (self, "cdp packet does have any cc_data");
1192 /* cdp_hdr_sequence_cntr */
1193 gst_byte_reader_skip_unchecked (&br, 2);
1195 /* time_code_present */
1197 guint8 hours, minutes, seconds, frames, fields;
1198 gboolean drop_frame;
1200 if (gst_byte_reader_get_remaining (&br) < 5) {
1201 GST_WARNING_OBJECT (self, "cdp packet does not have enough data to "
1202 "contain a timecode (%u). Need at least 5 bytes",
1203 gst_byte_reader_get_remaining (&br));
1206 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1208 GST_WARNING_OBJECT (self, "cdp packet does not have timecode start byte "
1209 "of 0x71, found 0x%02x", u8);
1213 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1214 if ((u8 & 0xc0) != 0xc0) {
1215 GST_WARNING_OBJECT (self, "reserved bits are not 0xc0, found 0x%02x", u8);
1219 hours = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
1221 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1222 if ((u8 & 0x80) != 0x80) {
1223 GST_WARNING_OBJECT (self, "reserved bit is not 0x80, found 0x%02x", u8);
1226 minutes = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
1228 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1233 seconds = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
1235 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1237 GST_WARNING_OBJECT (self, "reserved bit is not 0x0, found 0x%02x", u8);
1241 drop_frame = ! !(u8 & 0x80);
1242 frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
1244 gst_video_time_code_init (tc, fps_entry->fps_n, fps_entry->fps_d, NULL,
1245 drop_frame ? GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME :
1246 GST_VIDEO_TIME_CODE_FLAGS_NONE, hours, minutes, seconds, frames,
1250 /* ccdata_present */
1254 if (gst_byte_reader_get_remaining (&br) < 2) {
1255 GST_WARNING_OBJECT (self, "not enough data to contain valid cc_data");
1258 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1260 GST_WARNING_OBJECT (self, "missing cc_data start code of 0x72, "
1261 "found 0x%02x", u8);
1265 cc_count = gst_byte_reader_get_uint8_unchecked (&br);
1266 if ((cc_count & 0xe0) != 0xe0) {
1267 GST_WARNING_OBJECT (self, "reserved bits are not 0xe0, found 0x%02x", u8);
1273 if (gst_byte_reader_get_remaining (&br) < len)
1276 memcpy (cc_data, gst_byte_reader_get_data_unchecked (&br, len), len);
1279 *out_fps_entry = fps_entry;
1281 /* skip everything else we don't care about */
1286 copy_from_stored_data (GstCCConverter * self, guint8 * out_ccp,
1287 guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len,
1288 guint8 * cea608_2, guint * cea608_2_len)
1290 guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0;
1292 g_assert ((out_ccp && ccp_size) || (!out_ccp && !ccp_size));
1293 g_assert ((cea608_1 && cea608_1_len) || (!cea608_1 && !cea608_1_len));
1294 g_assert ((cea608_2 && cea608_2_len) || (!cea608_2 && !cea608_2_len));
1297 ccp_in_size = *ccp_size;
1301 cea608_1_in_size = *cea608_1_len;
1305 cea608_2_in_size = *cea608_2_len;
1309 if (out_ccp && self->scratch_ccp_len > 0) {
1310 GST_DEBUG_OBJECT (self, "copying from previous scratch ccp buffer of "
1311 "%u bytes", self->scratch_ccp_len);
1312 if (ccp_in_size < *ccp_size + self->scratch_ccp_len) {
1313 GST_WARNING_OBJECT (self, "output buffer too small %u < %u", ccp_in_size,
1314 *ccp_size + self->scratch_ccp_len);
1317 memcpy (&out_ccp[*ccp_size], self->scratch_ccp, self->scratch_ccp_len);
1318 *ccp_size += self->scratch_ccp_len;
1321 if (cea608_1 && self->scratch_cea608_1_len > 0) {
1322 GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 1 "
1323 "buffer of %u bytes", self->scratch_cea608_1_len);
1324 if (cea608_1_in_size < *cea608_1_len + self->scratch_cea608_1_len) {
1325 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1326 cea608_1_in_size, *cea608_1_len + self->scratch_cea608_1_len);
1329 memcpy (&cea608_1[*cea608_1_len], self->scratch_cea608_1,
1330 self->scratch_cea608_1_len);
1331 *cea608_1_len += self->scratch_cea608_1_len;
1334 if (cea608_2 && self->scratch_cea608_2_len > 0) {
1335 GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 2 "
1336 "buffer of %u bytes", self->scratch_cea608_2_len);
1337 if (cea608_2_in_size < *cea608_2_len + self->scratch_cea608_2_len) {
1338 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1339 cea608_2_in_size, *cea608_2_len + self->scratch_cea608_2_len);
1342 memcpy (&cea608_2[*cea608_2_len], self->scratch_cea608_2,
1343 self->scratch_cea608_2_len);
1344 *cea608_2_len += self->scratch_cea608_2_len;
1360 cc_data_to_cea608_ccp (GstCCConverter * self, guint8 * cc_data,
1361 guint cc_data_len, guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1,
1362 guint * cea608_1_len, guint8 * cea608_2, guint * cea608_2_len,
1363 const struct cdp_fps_entry *in_fps_entry)
1365 guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0;
1367 g_assert (cc_data || cc_data_len == 0);
1370 ccp_in_size = *ccp_size;
1372 cea608_1_in_size = *cea608_1_len;
1374 cea608_2_in_size = *cea608_2_len;
1376 if (!copy_from_stored_data (self, out_ccp, ccp_size, cea608_1, cea608_1_len,
1377 cea608_2, cea608_2_len))
1381 gint ccp_offset = 0;
1382 guint new_cea608_1_len = 0, new_cea608_2_len = 0;
1383 guint8 *new_cea608_1 = cea608_1, *new_cea608_2 = cea608_2;
1386 new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
1387 new_cea608_1 = &cea608_1[*cea608_1_len];
1390 new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
1391 new_cea608_2 = &cea608_2[*cea608_2_len];
1394 cc_data_len = compact_cc_data (cc_data, cc_data_len);
1396 if (cc_data_len / 3 > in_fps_entry->max_cc_count) {
1397 GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u. "
1398 "Truncating to %u", cc_data_len / 3, in_fps_entry->max_cc_count);
1399 cc_data_len = 3 * in_fps_entry->max_cc_count;
1402 ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
1403 &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
1404 if (ccp_offset < 0) {
1405 GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
1409 if ((new_cea608_1_len + new_cea608_2_len) / 2 >
1410 in_fps_entry->max_cea608_count) {
1411 GST_WARNING_OBJECT (self, "Too many cea608 triples in CDP packet %u. "
1412 "Truncating to %u", (new_cea608_1_len + new_cea608_2_len) / 2,
1413 in_fps_entry->max_cea608_count);
1414 if ((new_cea608_1_len + new_cea608_2_len) / 2 >
1415 in_fps_entry->max_cea608_count) {
1416 new_cea608_1_len = 2 * in_fps_entry->max_cea608_count;
1417 new_cea608_2_len = 0;
1420 2 * in_fps_entry->max_cea608_count - new_cea608_1_len;
1425 *cea608_1_len += new_cea608_1_len;
1427 *cea608_2_len += new_cea608_2_len;
1430 if (ccp_in_size < *ccp_size + cc_data_len - ccp_offset) {
1431 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1432 ccp_in_size, *ccp_size + cc_data_len - ccp_offset);
1435 memcpy (&out_ccp[*ccp_size], &cc_data[ccp_offset],
1436 cc_data_len - ccp_offset);
1437 *ccp_size += cc_data_len - ccp_offset;
1454 cdp_to_cea608_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1455 guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len,
1456 guint8 * cea608_2, guint * cea608_2_len, GstVideoTimeCode * out_tc,
1457 const struct cdp_fps_entry **in_fps_entry)
1459 guint8 cc_data[MAX_CDP_PACKET_LEN];
1460 guint cc_data_len = 0;
1464 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1467 convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1468 cc_data, out_tc, in_fps_entry);
1470 gst_buffer_unmap (inbuf, &in);
1471 self->input_frames++;
1474 return cc_data_to_cea608_ccp (self, inbuf ? cc_data : NULL, cc_data_len,
1475 out_ccp, ccp_size, cea608_1, cea608_1_len, cea608_2, cea608_2_len,
1476 inbuf ? *in_fps_entry : NULL);
1479 static GstFlowReturn
1480 convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1486 n = gst_buffer_get_size (inbuf);
1488 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1489 gst_buffer_set_size (outbuf, 0);
1496 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
1501 gst_buffer_set_size (outbuf, 3 * n);
1503 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1504 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1506 /* We have to assume that each value is from the first field and
1507 * don't know from which line offset it originally is */
1508 for (i = 0; i < n; i++) {
1509 out.data[i * 3] = 0x80;
1510 out.data[i * 3 + 1] = in.data[i * 2];
1511 out.data[i * 3 + 2] = in.data[i * 2 + 1];
1514 gst_buffer_unmap (inbuf, &in);
1515 gst_buffer_unmap (outbuf, &out);
1520 static GstFlowReturn
1521 convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1527 n = gst_buffer_get_size (inbuf);
1529 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1530 gst_buffer_set_size (outbuf, 0);
1537 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
1542 gst_buffer_set_size (outbuf, 3 * n);
1544 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1545 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1547 /* We have to assume that each value is from the first field and
1548 * don't know from which line offset it originally is */
1549 for (i = 0; i < n; i++) {
1550 out.data[i * 3] = 0xfc;
1551 out.data[i * 3 + 1] = in.data[i * 2];
1552 out.data[i * 3 + 2] = in.data[i * 2 + 1];
1555 gst_buffer_unmap (inbuf, &in);
1556 gst_buffer_unmap (outbuf, &out);
1561 static GstFlowReturn
1562 convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1563 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1566 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1567 guint cc_data_len = MAX_CDP_PACKET_LEN;
1568 guint cea608_1_len = MAX_CDP_PACKET_LEN;
1569 guint8 cc_data[MAX_CDP_PACKET_LEN], cea608_1[MAX_CEA608_LEN];
1571 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1572 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1573 g_assert_not_reached ();
1575 if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len, NULL, 0))
1581 n = gst_buffer_get_size (inbuf);
1583 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1584 gst_buffer_set_size (outbuf, 0);
1590 if (n > in_fps_entry->max_cea608_count) {
1591 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u",
1592 n, in_fps_entry->max_cea608_count);
1593 n = in_fps_entry->max_cea608_count;
1596 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1597 memcpy (&cea608_1[cea608_1_len], in.data, n * 2);
1598 gst_buffer_unmap (inbuf, &in);
1599 cea608_1_len += n * 2;
1600 self->input_frames++;
1603 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1604 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1605 g_assert_not_reached ();
1607 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1608 cea608_1, &cea608_1_len, NULL, 0, tc_meta ? &tc_meta->tc : NULL))
1611 if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1,
1612 cea608_1_len, NULL, 0, cc_data, &cc_data_len))
1615 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1617 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1618 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1619 self->output_frames++;
1620 gst_buffer_unmap (outbuf, &out);
1623 gst_buffer_set_size (outbuf, cc_data_len);
1632 static GstFlowReturn
1633 convert_cea608_s334_1a_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1640 n = gst_buffer_get_size (inbuf);
1642 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1649 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1653 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1654 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1656 for (i = 0; i < n; i++) {
1657 if (in.data[i * 3] & 0x80) {
1658 out.data[i * 2] = in.data[i * 3 + 1];
1659 out.data[i * 2 + 1] = in.data[i * 3 + 2];
1664 gst_buffer_unmap (inbuf, &in);
1665 gst_buffer_unmap (outbuf, &out);
1667 gst_buffer_set_size (outbuf, 2 * cea608);
1672 static GstFlowReturn
1673 convert_cea608_s334_1a_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1679 n = gst_buffer_get_size (inbuf);
1681 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1688 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1692 gst_buffer_set_size (outbuf, 3 * n);
1694 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1695 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1697 for (i = 0; i < n; i++) {
1698 out.data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
1699 out.data[i * 3 + 1] = in.data[i * 3 + 1];
1700 out.data[i * 3 + 2] = in.data[i * 3 + 2];
1703 gst_buffer_unmap (inbuf, &in);
1704 gst_buffer_unmap (outbuf, &out);
1709 static GstFlowReturn
1710 convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1711 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1714 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1715 guint cc_data_len = MAX_CDP_PACKET_LEN;
1716 guint cea608_1_len = MAX_CDP_PACKET_LEN, cea608_2_len = MAX_CDP_PACKET_LEN;
1717 guint8 cc_data[MAX_CDP_PACKET_LEN];
1718 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
1721 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1722 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1723 g_assert_not_reached ();
1725 if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len,
1726 cea608_2, &cea608_2_len))
1730 n = gst_buffer_get_size (inbuf);
1732 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1738 if (n > in_fps_entry->max_cea608_count) {
1739 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1740 n = in_fps_entry->max_cea608_count;
1743 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1745 for (i = 0; i < n; i++) {
1746 if (in.data[i * 3] & 0x80) {
1747 cea608_1[cea608_1_len++] = in.data[i * 3 + 1];
1748 cea608_1[cea608_1_len++] = in.data[i * 3 + 2];
1750 cea608_2[cea608_2_len++] = in.data[i * 3 + 1];
1751 cea608_2[cea608_2_len++] = in.data[i * 3 + 2];
1754 gst_buffer_unmap (inbuf, &in);
1755 self->input_frames++;
1758 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1759 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1760 g_assert_not_reached ();
1762 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1763 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1764 tc_meta ? &tc_meta->tc : NULL)) {
1768 if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1,
1769 cea608_1_len, cea608_2, cea608_2_len, cc_data, &cc_data_len)) {
1773 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1775 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1776 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1777 self->output_frames++;
1778 gst_buffer_unmap (outbuf, &out);
1781 gst_buffer_set_size (outbuf, cc_data_len);
1790 static GstFlowReturn
1791 convert_cea708_cc_data_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1798 n = gst_buffer_get_size (inbuf);
1800 GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1807 GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1811 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1812 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1814 for (i = 0; i < n; i++) {
1815 /* We can only really copy the first field here as there can't be any
1816 * signalling in raw CEA608 and we must not mix the streams of different
1819 if (in.data[i * 3] == 0xfc) {
1820 out.data[cea608 * 2] = in.data[i * 3 + 1];
1821 out.data[cea608 * 2 + 1] = in.data[i * 3 + 2];
1826 gst_buffer_unmap (inbuf, &in);
1827 gst_buffer_unmap (outbuf, &out);
1829 gst_buffer_set_size (outbuf, 2 * cea608);
1834 static GstFlowReturn
1835 convert_cea708_cc_data_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1842 n = gst_buffer_get_size (inbuf);
1844 GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1851 GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1855 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1856 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1858 for (i = 0; i < n; i++) {
1859 if (in.data[i * 3] == 0xfc || in.data[i * 3] == 0xfd) {
1860 /* We have to assume a line offset of 0 */
1861 out.data[cea608 * 3] = in.data[i * 3] == 0xfc ? 0x80 : 0x00;
1862 out.data[cea608 * 3 + 1] = in.data[i * 3 + 1];
1863 out.data[cea608 * 3 + 2] = in.data[i * 3 + 2];
1868 gst_buffer_unmap (inbuf, &in);
1869 gst_buffer_unmap (outbuf, &out);
1871 gst_buffer_set_size (outbuf, 3 * cea608);
1876 static GstFlowReturn
1877 convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1878 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1881 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1882 guint in_cc_data_len;
1883 guint cc_data_len = MAX_CDP_PACKET_LEN, ccp_data_len = MAX_CDP_PACKET_LEN;
1884 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
1885 guint8 cc_data[MAX_CDP_PACKET_LEN], ccp_data[MAX_CDP_PACKET_LEN];
1886 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
1890 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1891 in_cc_data = in.data;
1892 in_cc_data_len = in.size;
1893 self->input_frames++;
1899 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1900 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1901 g_assert_not_reached ();
1903 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1904 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1905 g_assert_not_reached ();
1907 if (!cc_data_to_cea608_ccp (self, in_cc_data, in_cc_data_len, ccp_data,
1908 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1911 gst_buffer_unmap (inbuf, &in);
1916 gst_buffer_unmap (inbuf, &in);
1918 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
1919 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1920 tc_meta ? &tc_meta->tc : NULL))
1923 if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len,
1924 cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data,
1928 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1930 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1931 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1932 self->output_frames++;
1933 gst_buffer_unmap (outbuf, &out);
1936 gst_buffer_set_size (outbuf, cc_data_len);
1945 static GstFlowReturn
1946 convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1947 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1950 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1952 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1954 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1955 cea608_1_len = (guint) out.size;
1956 if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, out.data, &cea608_1_len,
1957 NULL, NULL, &tc, &in_fps_entry)) {
1958 gst_buffer_set_size (outbuf, 0);
1962 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1963 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1964 out_fps_entry = in_fps_entry;
1966 if (fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1967 out.data, &cea608_1_len, NULL, NULL, &tc)) {
1968 self->output_frames++;
1972 gst_buffer_unmap (outbuf, &out);
1974 gst_buffer_set_size (outbuf, cea608_1_len);
1976 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
1977 gst_buffer_add_video_time_code_meta (outbuf,
1978 &self->current_output_timecode);
1979 gst_video_time_code_increment_frame (&self->current_output_timecode);
1985 static GstFlowReturn
1986 convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1987 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1990 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1991 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1992 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
1993 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
1994 guint i, cc_data_len;
1996 if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, cea608_1, &cea608_1_len,
1997 cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2000 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2001 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2002 out_fps_entry = in_fps_entry;
2004 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
2005 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2008 cc_data_len = gst_buffer_get_sizes (outbuf, NULL, NULL);
2010 gst_buffer_map (outbuf, &out, GST_MAP_READWRITE);
2011 if (!combine_cc_data (self, FALSE, out_fps_entry, NULL, 0, cea608_1,
2012 cea608_1_len, cea608_2, cea608_2_len, out.data, &cc_data_len)) {
2013 gst_buffer_unmap (outbuf, &out);
2017 for (i = 0; i < cc_data_len / 3; i++)
2018 /* We have to assume a line offset of 0 */
2019 out.data[i * 3] = out.data[i * 3] == 0xfc ? 0x80 : 0x00;
2021 gst_buffer_unmap (outbuf, &out);
2022 self->output_frames++;
2024 gst_buffer_set_size (outbuf, cc_data_len);
2026 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
2027 gst_buffer_add_video_time_code_meta (outbuf,
2028 &self->current_output_timecode);
2029 gst_video_time_code_increment_frame (&self->current_output_timecode);
2035 gst_buffer_set_size (outbuf, 0);
2039 static GstFlowReturn
2040 convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
2041 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
2044 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
2045 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
2046 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
2047 guint8 ccp_data[MAX_CDP_PACKET_LEN];
2048 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
2049 guint ccp_data_len = MAX_CDP_PACKET_LEN;
2052 if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len,
2053 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2056 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2057 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2058 out_fps_entry = in_fps_entry;
2060 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
2061 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2064 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
2065 out_len = (guint) out.size;
2066 if (!combine_cc_data (self, FALSE, out_fps_entry, ccp_data, ccp_data_len,
2067 cea608_1, cea608_1_len, cea608_2, cea608_2_len, out.data, &out_len)) {
2068 gst_buffer_unmap (outbuf, &out);
2073 gst_buffer_unmap (outbuf, &out);
2074 self->output_frames++;
2076 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
2077 gst_buffer_add_video_time_code_meta (outbuf,
2078 &self->current_output_timecode);
2079 gst_video_time_code_increment_frame (&self->current_output_timecode);
2083 gst_buffer_set_size (outbuf, out_len);
2088 static GstFlowReturn
2089 convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
2093 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
2094 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
2095 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
2096 guint8 ccp_data[MAX_CDP_PACKET_LEN], cc_data[MAX_CDP_PACKET_LEN];
2097 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
2098 guint ccp_data_len = MAX_CDP_PACKET_LEN, cc_data_len = MAX_CDP_PACKET_LEN;
2101 if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len,
2102 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2105 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2106 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2107 out_fps_entry = in_fps_entry;
2109 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
2110 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2113 if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len,
2114 cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data,
2119 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
2121 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
2122 out.data, out.size, &self->current_output_timecode, out_fps_entry);
2124 gst_buffer_unmap (outbuf, &out);
2125 self->output_frames++;
2128 gst_buffer_set_size (outbuf, out_len);
2133 static GstFlowReturn
2134 gst_cc_converter_transform (GstCCConverter * self, GstBuffer * inbuf,
2137 GstVideoTimeCodeMeta *tc_meta = NULL;
2138 GstFlowReturn ret = GST_FLOW_OK;
2140 GST_DEBUG_OBJECT (self, "Converting %" GST_PTR_FORMAT " from %u to %u", inbuf,
2141 self->input_caption_type, self->output_caption_type);
2144 tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
2147 if (self->current_output_timecode.config.fps_n <= 0) {
2148 /* XXX: this assumes the input time codes are well-formed and increase
2149 * at the rate of one frame for each input buffer */
2150 const struct cdp_fps_entry *in_fps_entry;
2151 gint scale_n, scale_d;
2153 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
2154 if (!in_fps_entry || in_fps_entry->fps_n == 0)
2155 scale_n = scale_d = 1;
2157 get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
2159 interpolate_time_code_with_framerate (self, &tc_meta->tc,
2160 self->out_fps_n, self->out_fps_d, scale_n, scale_d,
2161 &self->current_output_timecode);
2165 switch (self->input_caption_type) {
2166 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2168 switch (self->output_caption_type) {
2169 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2170 ret = convert_cea608_raw_cea608_s334_1a (self, inbuf, outbuf);
2172 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2173 ret = convert_cea608_raw_cea708_cc_data (self, inbuf, outbuf);
2175 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2176 ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf, tc_meta);
2178 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2180 g_assert_not_reached ();
2185 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2187 switch (self->output_caption_type) {
2188 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2189 ret = convert_cea608_s334_1a_cea608_raw (self, inbuf, outbuf);
2191 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2192 ret = convert_cea608_s334_1a_cea708_cc_data (self, inbuf, outbuf);
2194 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2196 convert_cea608_s334_1a_cea708_cdp (self, inbuf, outbuf, tc_meta);
2198 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2200 g_assert_not_reached ();
2205 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2207 switch (self->output_caption_type) {
2208 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2209 ret = convert_cea708_cc_data_cea608_raw (self, inbuf, outbuf);
2211 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2212 ret = convert_cea708_cc_data_cea608_s334_1a (self, inbuf, outbuf);
2214 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2216 convert_cea708_cc_data_cea708_cdp (self, inbuf, outbuf, tc_meta);
2218 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2220 g_assert_not_reached ();
2225 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2227 switch (self->output_caption_type) {
2228 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2229 ret = convert_cea708_cdp_cea608_raw (self, inbuf, outbuf, tc_meta);
2231 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2233 convert_cea708_cdp_cea608_s334_1a (self, inbuf, outbuf, tc_meta);
2235 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2237 convert_cea708_cdp_cea708_cc_data (self, inbuf, outbuf, tc_meta);
2239 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2240 ret = convert_cea708_cdp_cea708_cdp (self, inbuf, outbuf);
2243 g_assert_not_reached ();
2249 g_assert_not_reached ();
2253 if (ret != GST_FLOW_OK) {
2254 GST_DEBUG_OBJECT (self, "returning %s", gst_flow_get_name (ret));
2258 GST_DEBUG_OBJECT (self, "Converted to %" GST_PTR_FORMAT, outbuf);
2260 if (gst_buffer_get_size (outbuf) > 0) {
2261 if (self->current_output_timecode.config.fps_n > 0) {
2262 gst_buffer_add_video_time_code_meta (outbuf,
2263 &self->current_output_timecode);
2264 gst_video_time_code_increment_frame (&self->current_output_timecode);
2274 gst_cc_converter_transform_meta (GstBaseTransform * base, GstBuffer * outbuf,
2275 GstMeta * meta, GstBuffer * inbuf)
2277 const GstMetaInfo *info = meta->info;
2279 /* we do this manually for framerate scaling */
2280 if (info->api == GST_VIDEO_TIME_CODE_META_API_TYPE)
2283 return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (base, outbuf,
2288 can_generate_output (GstCCConverter * self)
2290 int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
2291 int output_time_cmp;
2293 if (self->in_fps_n == 0 || self->out_fps_n == 0)
2296 /* compute the relative frame count for each */
2297 if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
2298 self->input_frames, 1, &input_frame_n, &input_frame_d))
2299 /* we should never overflow */
2300 g_assert_not_reached ();
2302 if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
2303 self->output_frames, 1, &output_frame_n, &output_frame_d))
2304 /* we should never overflow */
2305 g_assert_not_reached ();
2307 output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
2308 output_frame_n, output_frame_d);
2310 /* if the next output frame is at or before the current input frame */
2311 if (output_time_cmp >= 0)
2318 reset_counters (GstCCConverter * self)
2320 self->scratch_ccp_len = 0;
2321 self->scratch_cea608_1_len = 0;
2322 self->scratch_cea608_2_len = 0;
2323 self->input_frames = 0;
2324 self->output_frames = 1;
2325 gst_video_time_code_clear (&self->current_output_timecode);
2326 gst_clear_buffer (&self->previous_buffer);
2329 static GstFlowReturn
2330 drain_input (GstCCConverter * self)
2332 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (self);
2333 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
2334 GstFlowReturn ret = GST_FLOW_OK;
2336 while (self->scratch_ccp_len > 0 || self->scratch_cea608_1_len > 0
2337 || self->scratch_cea608_2_len > 0 || can_generate_output (self)) {
2340 if (!self->previous_buffer) {
2341 GST_WARNING_OBJECT (self, "Attempt to draining without a previous "
2342 "buffer. Aborting");
2346 outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
2348 if (bclass->copy_metadata) {
2349 if (!bclass->copy_metadata (trans, self->previous_buffer, outbuf)) {
2350 /* something failed, post a warning */
2351 GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
2352 ("could not copy metadata"), (NULL));
2356 ret = gst_cc_converter_transform (self, NULL, outbuf);
2357 if (gst_buffer_get_size (outbuf) <= 0) {
2358 /* try to move the output along */
2359 self->input_frames++;
2360 gst_buffer_unref (outbuf);
2362 } else if (ret != GST_FLOW_OK) {
2363 gst_buffer_unref (outbuf);
2367 ret = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (trans), outbuf);
2368 if (ret != GST_FLOW_OK) {
2376 static GstFlowReturn
2377 gst_cc_converter_generate_output (GstBaseTransform * base, GstBuffer ** outbuf)
2379 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (base);
2380 GstCCConverter *self = GST_CCCONVERTER (base);
2381 GstBuffer *inbuf = base->queued_buf;
2385 base->queued_buf = NULL;
2386 if (!inbuf && !can_generate_output (self)) {
2390 if (gst_base_transform_is_passthrough (base)) {
2394 if (inbuf && GST_BUFFER_IS_DISCONT (inbuf)) {
2395 ret = drain_input (self);
2396 reset_counters (self);
2397 if (ret != GST_FLOW_OK)
2401 *outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
2402 if (*outbuf == NULL)
2406 gst_buffer_replace (&self->previous_buffer, inbuf);
2408 if (bclass->copy_metadata) {
2409 if (!bclass->copy_metadata (base, self->previous_buffer, *outbuf)) {
2410 /* something failed, post a warning */
2411 GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
2412 ("could not copy metadata"), (NULL));
2416 ret = gst_cc_converter_transform (self, inbuf, *outbuf);
2417 if (gst_buffer_get_size (*outbuf) <= 0) {
2418 gst_buffer_unref (*outbuf);
2424 gst_buffer_unref (inbuf);
2432 gst_buffer_unref (inbuf);
2434 GST_WARNING_OBJECT (self, "could not allocate buffer");
2435 return GST_FLOW_ERROR;
2440 gst_cc_converter_sink_event (GstBaseTransform * trans, GstEvent * event)
2442 GstCCConverter *self = GST_CCCONVERTER (trans);
2444 switch (GST_EVENT_TYPE (event)) {
2446 GST_DEBUG_OBJECT (self, "received EOS");
2451 case GST_EVENT_FLUSH_START:
2452 reset_counters (self);
2458 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2462 gst_cc_converter_start (GstBaseTransform * base)
2464 GstCCConverter *self = GST_CCCONVERTER (base);
2466 /* Resetting this is not really needed but makes debugging easier */
2467 self->cdp_hdr_sequence_cntr = 0;
2468 self->current_output_timecode = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
2469 self->input_frames = 0;
2470 self->output_frames = 1;
2471 self->scratch_ccp_len = 0;
2472 self->scratch_cea608_1_len = 0;
2473 self->scratch_cea608_2_len = 0;
2479 gst_cc_converter_stop (GstBaseTransform * base)
2481 GstCCConverter *self = GST_CCCONVERTER (base);
2483 gst_video_time_code_clear (&self->current_output_timecode);
2484 gst_clear_buffer (&self->previous_buffer);
2490 gst_cc_converter_set_property (GObject * object, guint prop_id,
2491 const GValue * value, GParamSpec * pspec)
2493 GstCCConverter *filter = GST_CCCONVERTER (object);
2497 filter->cdp_mode = g_value_get_flags (value);
2500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2506 gst_cc_converter_get_property (GObject * object, guint prop_id, GValue * value,
2509 GstCCConverter *filter = GST_CCCONVERTER (object);
2513 g_value_set_flags (value, filter->cdp_mode);
2516 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2522 gst_cc_converter_class_init (GstCCConverterClass * klass)
2524 GObjectClass *gobject_class;
2525 GstElementClass *gstelement_class;
2526 GstBaseTransformClass *basetransform_class;
2528 gobject_class = (GObjectClass *) klass;
2529 gstelement_class = (GstElementClass *) klass;
2530 basetransform_class = (GstBaseTransformClass *) klass;
2532 gobject_class->set_property = gst_cc_converter_set_property;
2533 gobject_class->get_property = gst_cc_converter_get_property;
2536 * GstCCConverter:cdp-mode
2538 * Only insert the selection sections into CEA 708 CDP packets.
2540 * Various software does not handle any other information than CC data
2541 * contained in CDP packets and might fail parsing the packets otherwise.
2545 g_object_class_install_property (G_OBJECT_CLASS (klass),
2546 PROP_CDP_MODE, g_param_spec_flags ("cdp-mode",
2548 "Select which CDP sections to store in CDP packets",
2549 GST_TYPE_CC_CONVERTER_CDP_MODE, DEFAULT_CDP_MODE,
2550 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2552 gst_element_class_set_static_metadata (gstelement_class,
2553 "Closed Caption Converter",
2554 "Filter/ClosedCaption",
2555 "Converts Closed Captions between different formats",
2556 "Sebastian Dröge <sebastian@centricular.com>");
2558 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
2559 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
2561 basetransform_class->start = GST_DEBUG_FUNCPTR (gst_cc_converter_start);
2562 basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_cc_converter_stop);
2563 basetransform_class->sink_event =
2564 GST_DEBUG_FUNCPTR (gst_cc_converter_sink_event);
2565 basetransform_class->transform_size =
2566 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_size);
2567 basetransform_class->transform_caps =
2568 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_caps);
2569 basetransform_class->fixate_caps =
2570 GST_DEBUG_FUNCPTR (gst_cc_converter_fixate_caps);
2571 basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_cc_converter_set_caps);
2572 basetransform_class->transform_meta =
2573 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_meta);
2574 basetransform_class->generate_output =
2575 GST_DEBUG_FUNCPTR (gst_cc_converter_generate_output);
2576 basetransform_class->passthrough_on_same_caps = TRUE;
2578 GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
2579 0, "Closed Caption converter");
2581 gst_type_mark_as_plugin_api (GST_TYPE_CC_CONVERTER_CDP_MODE, 0);
2585 gst_cc_converter_init (GstCCConverter * self)
2587 self->cdp_mode = DEFAULT_CDP_MODE;