1 /*
2 * GStreamer
3 * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <gst/gst.h>
27 #include <gst/base/base.h>
28 #include <gst/video/video.h>
29 #include <string.h>
30
31 #include "gstccconverter.h"
32
33 GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
34 #define GST_CAT_DEFAULT gst_cc_converter_debug
35
36 /**
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
41 *
42 * Since: 1.20
43 */
44
45 enum
46 {
47 PROP_0,
48 PROP_CDP_MODE,
49 };
50
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)
52
53 /* Ordered by the amount of information they can contain */
54 #define CC_CAPS \
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"
59
60 #define VAL_OR_0(v) ((v) ? (*(v)) : 0)
61
62 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
63 GST_PAD_SINK,
64 GST_PAD_ALWAYS,
65 GST_STATIC_CAPS (CC_CAPS));
66
67 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
68 GST_PAD_SRC,
69 GST_PAD_ALWAYS,
70 GST_STATIC_CAPS (CC_CAPS));
71
72 #define parent_class gst_cc_converter_parent_class
73 G_DEFINE_TYPE (GstCCConverter, gst_cc_converter, GST_TYPE_BASE_TRANSFORM);
74 GST_ELEMENT_REGISTER_DEFINE (ccconverter, "ccconverter",
75 GST_RANK_NONE, GST_TYPE_CCCONVERTER);
76
77 #define GST_TYPE_CC_CONVERTER_CDP_MODE (gst_cc_converter_cdp_mode_get_type())
78 static GType
gst_cc_converter_cdp_mode_get_type(void)79 gst_cc_converter_cdp_mode_get_type (void)
80 {
81 static const GFlagsValue values[] = {
82 {GST_CC_CONVERTER_CDP_MODE_TIME_CODE,
83 "Store time code information in CDP packets", "time-code"},
84 {GST_CC_CONVERTER_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
85 "cc-data"},
86 {GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO,
87 "Store CC service information in CDP packets", "cc-svc-info"},
88 {0, NULL, NULL}
89 };
90 static GType id = 0;
91
92 if (g_once_init_enter ((gsize *) & id)) {
93 GType _id;
94
95 _id = g_flags_register_static ("GstCCConverterCDPMode", values);
96
97 g_once_init_leave ((gsize *) & id, _id);
98 }
99
100 return id;
101 }
102
103 static gboolean
gst_cc_converter_transform_size(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)104 gst_cc_converter_transform_size (GstBaseTransform * base,
105 GstPadDirection direction,
106 GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
107 {
108 /* We can't really convert from an output size to an input size */
109 if (direction != GST_PAD_SINK)
110 return FALSE;
111
112 /* Assume worst-case here and over-allocate, and in ::transform() we then
113 * downsize the buffer as needed. The worst-case is one CDP packet, which
114 * can be up to MAX_CDP_PACKET_LEN bytes large */
115
116 *othersize = MAX_CDP_PACKET_LEN;
117
118 return TRUE;
119 }
120
121 static GstCaps *
gst_cc_converter_transform_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,GstCaps * filter)122 gst_cc_converter_transform_caps (GstBaseTransform * base,
123 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
124 {
125 static GstStaticCaps non_cdp_caps =
126 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cc_data; "
127 "closedcaption/x-cea-608,format=(string) s334-1a; "
128 "closedcaption/x-cea-608,format=(string) raw");
129 static GstStaticCaps cdp_caps =
130 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp");
131 static GstStaticCaps cdp_caps_framerate =
132 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp, "
133 "framerate=(fraction){60/1, 60000/1001, 50/1, 30/1, 30000/1001, 25/1, 24/1, 24000/1001}");
134
135 GstCCConverter *self = GST_CCCONVERTER (base);
136 guint i, n;
137 GstCaps *res, *templ;
138
139 templ = gst_pad_get_pad_template_caps (base->srcpad);
140
141 GST_DEBUG_OBJECT (self, "direction %s from caps %" GST_PTR_FORMAT,
142 direction == GST_PAD_SRC ? "src" : "sink", caps);
143
144 res = gst_caps_new_empty ();
145 n = gst_caps_get_size (caps);
146 for (i = 0; i < n; i++) {
147 const GstStructure *s = gst_caps_get_structure (caps, i);
148 const GValue *framerate = gst_structure_get_value (s, "framerate");
149
150 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
151
152 if (direction == GST_PAD_SRC) {
153 /* SRC direction: We produce upstream caps
154 *
155 * Downstream wanted CEA608 caps. If it had a framerate, we
156 * also need upstream to provide exactly that same framerate
157 * and otherwise we don't care.
158 *
159 * We can convert everything to CEA608.
160 */
161 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
162 if (framerate) {
163 /* we can only keep the same framerate for non-cdp */
164 GstCaps *tmp;
165
166 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
167 gst_caps_set_value (tmp, "framerate", framerate);
168 res = gst_caps_merge (res, tmp);
169 } else {
170 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
171 }
172 } else {
173 /* SINK: We produce downstream caps
174 *
175 * Upstream provided CEA608 caps. We can convert that to CDP if
176 * also a CDP compatible framerate was provided, and we can convert
177 * it to anything else regardless.
178 *
179 * If upstream provided a framerate we can pass that through, possibly
180 * filtered for the CDP case.
181 */
182 if (framerate) {
183 GstCaps *tmp;
184 GstStructure *t;
185
186 /* Create caps that contain the intersection of all framerates with
187 * the CDP allowed framerates */
188 tmp =
189 gst_caps_make_writable (gst_static_caps_get
190 (&cdp_caps_framerate));
191 t = gst_caps_get_structure (tmp, 0);
192 gst_structure_set_name (t, "closedcaption/x-cea-608");
193 gst_structure_remove_field (t, "format");
194 if (gst_structure_can_intersect (s, t)) {
195 gst_caps_unref (tmp);
196
197 tmp =
198 gst_caps_make_writable (gst_static_caps_get
199 (&cdp_caps_framerate));
200
201 res = gst_caps_merge (res, tmp);
202 } else {
203 gst_caps_unref (tmp);
204 }
205 /* And we can convert to everything else with the given framerate */
206 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
207 gst_caps_set_value (tmp, "framerate", framerate);
208 res = gst_caps_merge (res, tmp);
209 } else {
210 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
211 }
212 }
213 } else if (gst_structure_has_name (s, "closedcaption/x-cea-708")) {
214 if (direction == GST_PAD_SRC) {
215 /* SRC direction: We produce upstream caps
216 *
217 * Downstream wanted CEA708 caps. If downstream wants *only* CDP we
218 * either need CDP from upstream, or anything else with a CDP
219 * framerate.
220 * If downstream also wants non-CDP we can accept anything.
221 *
222 * We pass through any framerate as-is, except for filtering
223 * for CDP framerates if downstream wants only CDP.
224 */
225
226 if (g_strcmp0 (gst_structure_get_string (s, "format"), "cdp") == 0) {
227 /* Downstream wants only CDP */
228
229 /* We need CDP from upstream in that case */
230 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
231
232 /* Or anything else with a CDP framerate */
233 if (framerate) {
234 GstCaps *tmp;
235 GstStructure *t;
236 const GValue *cdp_framerate;
237
238 /* Create caps that contain the intersection of all framerates with
239 * the CDP allowed framerates */
240 tmp =
241 gst_caps_make_writable (gst_static_caps_get
242 (&cdp_caps_framerate));
243 t = gst_caps_get_structure (tmp, 0);
244
245 /* There's an intersection between the framerates so we can convert
246 * into CDP with exactly those framerates from anything else */
247 cdp_framerate = gst_structure_get_value (t, "framerate");
248 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
249 gst_caps_set_value (tmp, "framerate", cdp_framerate);
250 res = gst_caps_merge (res, tmp);
251 } else {
252 GstCaps *tmp, *cdp_caps;
253 const GValue *cdp_framerate;
254
255 /* Get all CDP framerates, we can accept anything that has those
256 * framerates */
257 cdp_caps = gst_static_caps_get (&cdp_caps_framerate);
258 cdp_framerate =
259 gst_structure_get_value (gst_caps_get_structure (cdp_caps, 0),
260 "framerate");
261
262 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
263 gst_caps_set_value (tmp, "framerate", cdp_framerate);
264 gst_caps_unref (cdp_caps);
265
266 res = gst_caps_merge (res, tmp);
267 }
268 } else {
269 /* Downstream wants not only CDP, we can do everything */
270 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
271 if (framerate) {
272 /* we can only keep the same framerate for non-cdp */
273 GstCaps *tmp;
274
275 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
276 gst_caps_set_value (tmp, "framerate", framerate);
277 res = gst_caps_merge (res, tmp);
278 } else {
279 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
280 }
281 }
282 } else {
283 GstCaps *tmp;
284
285 /* SINK: We produce downstream caps
286 *
287 * Upstream provided CEA708 caps. If upstream provided CDP we can
288 * output CDP, no matter what (-> passthrough). If upstream did not
289 * provide CDP, we can output CDP only if the framerate fits.
290 * We can always produce everything else apart from CDP.
291 *
292 * If upstream provided a framerate we pass that through for non-CDP
293 * output, and pass it through filtered for CDP output.
294 */
295
296 if (gst_structure_can_intersect (s,
297 gst_caps_get_structure (gst_static_caps_get (&cdp_caps), 0))) {
298 /* Upstream provided CDP caps, we can do everything independent of
299 * framerate */
300 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
301 } else if (framerate) {
302 const GValue *cdp_framerate;
303 GstStructure *t;
304
305 /* Upstream did not provide CDP. We can only do CDP if upstream
306 * happened to have a CDP framerate */
307
308 /* Create caps that contain the intersection of all framerates with
309 * the CDP allowed framerates */
310 tmp =
311 gst_caps_make_writable (gst_static_caps_get
312 (&cdp_caps_framerate));
313 t = gst_caps_get_structure (tmp, 0);
314
315 /* There's an intersection between the framerates so we can convert
316 * into CDP with exactly those framerates */
317 cdp_framerate = gst_structure_get_value (t, "framerate");
318 if (gst_value_intersect (NULL, cdp_framerate, framerate)) {
319 gst_caps_set_value (tmp, "framerate", cdp_framerate);
320
321 res = gst_caps_merge (res, tmp);
322 } else {
323 gst_clear_caps (&tmp);
324 }
325 }
326 /* We can always convert CEA708 to all non-CDP formats */
327 if (framerate) {
328 /* we can only keep the same framerate for non-cdp */
329 GstCaps *tmp;
330
331 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
332 gst_caps_set_value (tmp, "framerate", framerate);
333 res = gst_caps_merge (res, tmp);
334 } else {
335 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
336 }
337 }
338 } else {
339 g_assert_not_reached ();
340 }
341 }
342
343 GST_DEBUG_OBJECT (self, "pre filter caps %" GST_PTR_FORMAT, res);
344
345 /* We can convert anything into anything but it might involve loss of
346 * information so always filter according to the order in our template caps
347 * in the end */
348 if (filter) {
349 GstCaps *tmp;
350 filter = gst_caps_intersect_full (templ, filter, GST_CAPS_INTERSECT_FIRST);
351
352 tmp = gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
353 gst_caps_unref (res);
354 gst_caps_unref (filter);
355 res = tmp;
356 }
357
358 gst_caps_unref (templ);
359
360 GST_DEBUG_OBJECT (self, "Transformed in direction %s caps %" GST_PTR_FORMAT,
361 direction == GST_PAD_SRC ? "src" : "sink", caps);
362 GST_DEBUG_OBJECT (self, "filter %" GST_PTR_FORMAT, filter);
363 GST_DEBUG_OBJECT (self, "to %" GST_PTR_FORMAT, res);
364
365 return res;
366 }
367
368 static GstCaps *
gst_cc_converter_fixate_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * incaps,GstCaps * outcaps)369 gst_cc_converter_fixate_caps (GstBaseTransform * base,
370 GstPadDirection direction, GstCaps * incaps, GstCaps * outcaps)
371 {
372 GstCCConverter *self = GST_CCCONVERTER (base);
373 const GstStructure *s;
374 GstStructure *t;
375 const GValue *framerate;
376 GstCaps *intersection, *templ;
377
378 GST_DEBUG_OBJECT (self, "Fixating in direction %s incaps %" GST_PTR_FORMAT,
379 direction == GST_PAD_SRC ? "src" : "sink", incaps);
380 GST_DEBUG_OBJECT (self, "and outcaps %" GST_PTR_FORMAT, outcaps);
381
382 /* Prefer passthrough if we can */
383 if (gst_caps_is_subset (incaps, outcaps)) {
384 gst_caps_unref (outcaps);
385 return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base,
386 direction, incaps, gst_caps_ref (incaps));
387 }
388
389 /* Otherwise prefer caps in the order of our template caps */
390 templ = gst_pad_get_pad_template_caps (base->srcpad);
391 intersection =
392 gst_caps_intersect_full (templ, outcaps, GST_CAPS_INTERSECT_FIRST);
393 gst_caps_unref (outcaps);
394 outcaps = intersection;
395
396 outcaps =
397 GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base, direction,
398 incaps, outcaps);
399
400 s = gst_caps_get_structure (incaps, 0);
401 framerate = gst_structure_get_value (s, "framerate");
402 outcaps = gst_caps_make_writable (outcaps);
403 t = gst_caps_get_structure (outcaps, 0);
404 if (!framerate) {
405 /* remove any output framerate that might've been added by basetransform
406 * due to intersecting with downstream */
407 gst_structure_remove_field (t, "framerate");
408 } else {
409 /* or passthrough the input framerate if possible */
410 guint n, d;
411
412 n = gst_value_get_fraction_numerator (framerate);
413 d = gst_value_get_fraction_denominator (framerate);
414
415 if (gst_structure_has_field (t, "framerate"))
416 gst_structure_fixate_field_nearest_fraction (t, "framerate", n, d);
417 else
418 gst_structure_set (t, "framerate", GST_TYPE_FRACTION, n, d, NULL);
419 }
420
421 GST_DEBUG_OBJECT (self,
422 "Fixated caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, incaps, outcaps);
423
424 return outcaps;
425 }
426
427 static gboolean
gst_cc_converter_set_caps(GstBaseTransform * base,GstCaps * incaps,GstCaps * outcaps)428 gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps,
429 GstCaps * outcaps)
430 {
431 GstCCConverter *self = GST_CCCONVERTER (base);
432 const GstStructure *s;
433 gboolean passthrough;
434
435 self->input_caption_type = gst_video_caption_type_from_caps (incaps);
436 self->output_caption_type = gst_video_caption_type_from_caps (outcaps);
437
438 if (self->input_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN ||
439 self->output_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN)
440 goto invalid_caps;
441
442 s = gst_caps_get_structure (incaps, 0);
443 if (!gst_structure_get_fraction (s, "framerate", &self->in_fps_n,
444 &self->in_fps_d))
445 self->in_fps_n = self->in_fps_d = 0;
446
447 s = gst_caps_get_structure (outcaps, 0);
448 if (!gst_structure_get_fraction (s, "framerate", &self->out_fps_n,
449 &self->out_fps_d))
450 self->out_fps_n = self->out_fps_d = 0;
451
452 gst_video_time_code_clear (&self->current_output_timecode);
453
454 /* Caps can be different but we can passthrough as long as they can
455 * intersect, i.e. have same caps name and format */
456 passthrough = gst_caps_can_intersect (incaps, outcaps);
457 gst_base_transform_set_passthrough (base, passthrough);
458
459 GST_DEBUG_OBJECT (self,
460 "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)",
461 incaps, outcaps, passthrough);
462
463 return TRUE;
464
465 invalid_caps:
466 {
467 GST_ERROR_OBJECT (self,
468 "Invalid caps: in %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, incaps,
469 outcaps);
470 return FALSE;
471 }
472 }
473
474 struct cdp_fps_entry
475 {
476 guint8 fps_idx;
477 guint fps_n, fps_d;
478 guint max_cc_count;
479 guint max_ccp_count;
480 guint max_cea608_count;
481 };
482
483 static const struct cdp_fps_entry cdp_fps_table[] = {
484 {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
485 {0x2f, 24, 1, 25, 22, 2},
486 {0x3f, 25, 1, 24, 22, 2},
487 {0x4f, 30000, 1001, 20, 18, 2},
488 {0x5f, 30, 1, 20, 18, 2},
489 {0x6f, 50, 1, 12, 11, 1},
490 {0x7f, 60000, 1001, 10, 9, 1},
491 {0x8f, 60, 1, 10, 9, 1},
492 };
493 static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
494
495 static const struct cdp_fps_entry *
cdp_fps_entry_from_id(guint8 id)496 cdp_fps_entry_from_id (guint8 id)
497 {
498 int i;
499 for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
500 if (cdp_fps_table[i].fps_idx == id)
501 return &cdp_fps_table[i];
502 }
503 return &null_fps_entry;
504 }
505
506 static const struct cdp_fps_entry *
cdp_fps_entry_from_fps(guint fps_n,guint fps_d)507 cdp_fps_entry_from_fps (guint fps_n, guint fps_d)
508 {
509 int i;
510 for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
511 if (cdp_fps_table[i].fps_n == fps_n && cdp_fps_table[i].fps_d == fps_d)
512 return &cdp_fps_table[i];
513 }
514 return &null_fps_entry;
515 }
516
517 static void
get_framerate_output_scale(GstCCConverter * self,const struct cdp_fps_entry * in_fps_entry,gint * scale_n,gint * scale_d)518 get_framerate_output_scale (GstCCConverter * self,
519 const struct cdp_fps_entry *in_fps_entry, gint * scale_n, gint * scale_d)
520 {
521 if (self->in_fps_n == 0 || self->out_fps_d == 0) {
522 *scale_n = 1;
523 *scale_d = 1;
524 return;
525 }
526
527 /* compute the relative rates of the two framerates */
528 if (!gst_util_fraction_multiply (in_fps_entry->fps_d, in_fps_entry->fps_n,
529 self->out_fps_n, self->out_fps_d, scale_n, scale_d))
530 /* we should never overflow */
531 g_assert_not_reached ();
532 }
533
534 static gboolean
interpolate_time_code_with_framerate(GstCCConverter * self,const GstVideoTimeCode * tc,gint out_fps_n,gint out_fps_d,gint scale_n,gint scale_d,GstVideoTimeCode * out)535 interpolate_time_code_with_framerate (GstCCConverter * self,
536 const GstVideoTimeCode * tc, gint out_fps_n, gint out_fps_d,
537 gint scale_n, gint scale_d, GstVideoTimeCode * out)
538 {
539 gchar *tc_str;
540 gint output_n, output_d;
541 guint output_frame;
542 GstVideoTimeCodeFlags flags;
543
544 g_return_val_if_fail (tc != NULL, FALSE);
545 g_return_val_if_fail (out != NULL, FALSE);
546 /* out_n/d can only be 0 if scale_n/d are 1/1 */
547 g_return_val_if_fail ((scale_n == 1 && scale_d == 1) || (out_fps_n != 0
548 && out_fps_d != 0), FALSE);
549
550 if (!tc || tc->config.fps_n == 0)
551 return FALSE;
552
553 if (!gst_util_fraction_multiply (tc->frames, 1, scale_n, scale_d, &output_n,
554 &output_d))
555 /* we should never overflow */
556 g_assert_not_reached ();
557
558 tc_str = gst_video_time_code_to_string (tc);
559 GST_TRACE_OBJECT (self, "interpolating time code %s with scale %d/%d "
560 "to frame %d/%d", tc_str, scale_n, scale_d, output_n, output_d);
561 g_free (tc_str);
562
563 if (out_fps_n == 0 || out_fps_d == 0) {
564 out_fps_n = tc->config.fps_n;
565 out_fps_d = tc->config.fps_d;
566 }
567
568 flags = tc->config.flags;
569 if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0 && out_fps_d != 1001
570 && out_fps_n != 60000 && out_fps_n != 30000) {
571 flags &= ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
572 } else if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) == 0
573 && out_fps_d == 1001 && (out_fps_n == 60000 || out_fps_n == 30000)) {
574 /* XXX: theoretically, not quite correct however this is an assumption
575 * we have elsewhere that these framerates are always drop-framed */
576 flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
577 }
578
579 output_frame = output_n / output_d;
580
581 *out = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
582 do {
583 /* here we try to find the next available valid timecode. The dropped
584 * (when they exist) frames in time codes are that the beginning of each
585 * minute */
586 gst_video_time_code_clear (out);
587 gst_video_time_code_init (out, out_fps_n, out_fps_d,
588 tc->config.latest_daily_jam, flags, tc->hours, tc->minutes,
589 tc->seconds, output_frame, tc->field_count);
590 output_frame++;
591 } while ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0
592 && output_frame < 10 && !gst_video_time_code_is_valid (out));
593
594 tc_str = gst_video_time_code_to_string (out);
595 GST_TRACE_OBJECT (self, "interpolated to %s", tc_str);
596 g_free (tc_str);
597
598 return TRUE;
599 }
600
601 /* remove padding bytes from a cc_data packet. Returns the length of the new
602 * data in @cc_data */
603 static guint
compact_cc_data(guint8 * cc_data,guint cc_data_len)604 compact_cc_data (guint8 * cc_data, guint cc_data_len)
605 {
606 gboolean started_ccp = FALSE;
607 guint out_len = 0;
608 guint i;
609
610 if (cc_data_len % 3 != 0) {
611 GST_WARNING ("Invalid cc_data buffer size");
612 cc_data_len = cc_data_len - (cc_data_len % 3);
613 }
614
615 for (i = 0; i < cc_data_len / 3; i++) {
616 gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
617 guint8 cc_type = cc_data[i * 3] & 0x03;
618
619 if (!started_ccp && (cc_type == 0x00 || cc_type == 0x01)) {
620 if (cc_valid) {
621 /* copy over valid 608 data */
622 cc_data[out_len++] = cc_data[i * 3];
623 cc_data[out_len++] = cc_data[i * 3 + 1];
624 cc_data[out_len++] = cc_data[i * 3 + 2];
625 }
626 continue;
627 }
628
629 if (cc_type & 0x10)
630 started_ccp = TRUE;
631
632 if (!cc_valid)
633 continue;
634
635 if (cc_type == 0x00 || cc_type == 0x01) {
636 GST_WARNING ("Invalid cc_data. cea608 bytes after cea708");
637 return 0;
638 }
639
640 cc_data[out_len++] = cc_data[i * 3];
641 cc_data[out_len++] = cc_data[i * 3 + 1];
642 cc_data[out_len++] = cc_data[i * 3 + 2];
643 }
644
645 GST_LOG ("compacted cc_data from %u to %u", cc_data_len, out_len);
646
647 return out_len;
648 }
649
650 static gint
cc_data_extract_cea608(guint8 * cc_data,guint cc_data_len,guint8 * cea608_field1,guint * cea608_field1_len,guint8 * cea608_field2,guint * cea608_field2_len)651 cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
652 guint8 * cea608_field1, guint * cea608_field1_len,
653 guint8 * cea608_field2, guint * cea608_field2_len)
654 {
655 guint i, field_1_len = 0, field_2_len = 0;
656
657 if (cea608_field1_len) {
658 field_1_len = *cea608_field1_len;
659 *cea608_field1_len = 0;
660 }
661 if (cea608_field2_len) {
662 field_2_len = *cea608_field2_len;
663 *cea608_field2_len = 0;
664 }
665
666 if (cc_data_len % 3 != 0) {
667 GST_WARNING ("Invalid cc_data buffer size %u. Truncating to a multiple "
668 "of 3", cc_data_len);
669 cc_data_len = cc_data_len - (cc_data_len % 3);
670 }
671
672 for (i = 0; i < cc_data_len / 3; i++) {
673 gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
674 guint8 cc_type = cc_data[i * 3] & 0x03;
675
676 GST_TRACE ("0x%02x 0x%02x 0x%02x, valid: %u, type: 0b%u%u",
677 cc_data[i * 3 + 0], cc_data[i * 3 + 1], cc_data[i * 3 + 2], cc_valid,
678 cc_type & 0x2, cc_type & 0x1);
679
680 if (cc_type == 0x00) {
681 if (!cc_valid)
682 continue;
683
684 if (cea608_field1 && cea608_field1_len) {
685 if (*cea608_field1_len + 2 > field_1_len) {
686 GST_WARNING ("Too many cea608 input bytes %u for field 1",
687 *cea608_field1_len + 2);
688 return -1;
689 }
690 cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 1];
691 cea608_field1[(*cea608_field1_len)++] = cc_data[i * 3 + 2];
692 }
693 } else if (cc_type == 0x01) {
694 if (!cc_valid)
695 continue;
696
697 if (cea608_field2 && cea608_field2_len) {
698 if (*cea608_field2_len + 2 > field_2_len) {
699 GST_WARNING ("Too many cea608 input bytes %u for field 2",
700 *cea608_field2_len + 2);
701 return -1;
702 }
703 cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 1];
704 cea608_field2[(*cea608_field2_len)++] = cc_data[i * 3 + 2];
705 }
706 } else {
707 /* all cea608 packets must be at the beginning of a cc_data */
708 break;
709 }
710 }
711
712 g_assert_cmpint (i * 3, <=, cc_data_len);
713
714 GST_LOG ("Extracted cea608-1 of length %u and cea608-2 of length %u",
715 VAL_OR_0 (cea608_field1_len), VAL_OR_0 (cea608_field2_len));
716
717 return i * 3;
718 }
719
720 static void
store_cc_data(GstCCConverter * self,const guint8 * ccp_data,guint ccp_data_len,const guint8 * cea608_1,guint cea608_1_len,const guint8 * cea608_2,guint cea608_2_len)721 store_cc_data (GstCCConverter * self, const guint8 * ccp_data,
722 guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len,
723 const guint8 * cea608_2, guint cea608_2_len)
724 {
725 GST_DEBUG_OBJECT (self, "holding data of len ccp:%u, cea608 1:%u, "
726 "cea608 2:%u until next input buffer", ccp_data_len, cea608_1_len,
727 cea608_2_len);
728
729 if (ccp_data && ccp_data_len > 0) {
730 memcpy (self->scratch_ccp, ccp_data, ccp_data_len);
731 self->scratch_ccp_len = ccp_data_len;
732 } else {
733 self->scratch_ccp_len = 0;
734 }
735 g_assert_cmpint (self->scratch_ccp_len, <, sizeof (self->scratch_ccp));
736
737 if (cea608_1 && cea608_1_len > 0) {
738 memcpy (self->scratch_cea608_1, cea608_1, cea608_1_len);
739 self->scratch_cea608_1_len = cea608_1_len;
740 } else {
741 self->scratch_cea608_1_len = 0;
742 }
743 g_assert_cmpint (self->scratch_cea608_1_len, <,
744 sizeof (self->scratch_cea608_1));
745
746 if (cea608_2 && cea608_2_len > 0) {
747 memcpy (self->scratch_cea608_2, cea608_2, cea608_2_len);
748 self->scratch_cea608_2_len = cea608_2_len;
749 } else {
750 self->scratch_cea608_2_len = 0;
751 }
752 g_assert_cmpint (self->scratch_cea608_2_len, <,
753 sizeof (self->scratch_cea608_2));
754 }
755
756 static gboolean
combine_cc_data(GstCCConverter * self,gboolean pad_cea608,const struct cdp_fps_entry * out_fps_entry,const guint8 * ccp_data,guint ccp_data_len,const guint8 * cea608_1,guint cea608_1_len,const guint8 * cea608_2,guint cea608_2_len,guint8 * out,guint * out_size)757 combine_cc_data (GstCCConverter * self, gboolean pad_cea608,
758 const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data,
759 guint ccp_data_len, const guint8 * cea608_1, guint cea608_1_len,
760 const guint8 * cea608_2, guint cea608_2_len, guint8 * out, guint * out_size)
761 {
762 guint i = 0, out_i = 0, max_size = 0, cea608_1_i = 0, cea608_2_i = 0;
763 guint cea608_output_count;
764 guint total_cea608_1_count, total_cea608_2_count;
765
766 g_assert (out);
767 g_assert (out_size);
768 g_assert (!ccp_data || ccp_data_len % 3 == 0);
769 g_assert (!cea608_1 || cea608_1_len % 2 == 0);
770 g_assert (!cea608_2 || cea608_2_len % 2 == 0);
771 cea608_1_len /= 2;
772 cea608_2_len /= 2;
773 #if 0
774 /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated,
775 * However that is not possible for 60fps (where only one cea608 field fits)
776 * without adding previous output buffer tracking */
777 g_assert_cmpint (cea608_1_len >= cea608_2_len);
778 #endif
779 g_assert_cmpint (cea608_1_len + cea608_2_len, <=,
780 out_fps_entry->max_cea608_count);
781
782 total_cea608_1_count = cea608_1_len;
783 total_cea608_2_count = cea608_2_len;
784
785 #if 0
786 /* FIXME: if cea608 field 2 is generated, field 1 needs to be generated. */
787 if (cea608_1_len < cea608_2_len)
788 total_cea608_1_count += cea608_2_len - cea608_1_len;
789 #endif
790
791 max_size = ccp_data_len + (total_cea608_1_count + total_cea608_2_count) * 3;
792 if (*out_size < max_size) {
793 GST_WARNING_OBJECT (self, "Output data too small (%u < %u)", *out_size,
794 max_size);
795 return FALSE;
796 }
797
798 /* FIXME: interlacing, tff, rff, ensuring cea608 field1 is generated if
799 * field2 exists even across packets */
800
801 cea608_output_count = cea608_1_len + cea608_2_len;
802 if (pad_cea608) {
803 for (i = total_cea608_1_count + total_cea608_2_count;
804 i < out_fps_entry->max_cea608_count; i++) {
805 /* try to pad evenly */
806 if (i > cea608_1_len / 2)
807 total_cea608_1_count++;
808 else
809 total_cea608_2_count++;
810 cea608_output_count++;
811 }
812 }
813
814 GST_LOG ("writing %u cea608-1 fields and %u cea608-2 fields",
815 total_cea608_1_count, total_cea608_2_count);
816 g_assert_cmpint (total_cea608_1_count + total_cea608_2_count, <=,
817 out_fps_entry->max_cea608_count);
818
819 while (cea608_1_i + cea608_2_i < cea608_output_count) {
820 if (cea608_1_i < cea608_1_len) {
821 out[out_i++] = 0xfc;
822 out[out_i++] = cea608_1[cea608_1_i * 2];
823 out[out_i++] = cea608_1[cea608_1_i * 2 + 1];
824 cea608_1_i++;
825 i++;
826 } else if (cea608_1_i < total_cea608_1_count) {
827 out[out_i++] = 0xf8;
828 out[out_i++] = 0x80;
829 out[out_i++] = 0x80;
830 cea608_1_i++;
831 i++;
832 }
833
834 if (cea608_2_i < cea608_2_len) {
835 out[out_i++] = 0xfd;
836 out[out_i++] = cea608_2[cea608_2_i * 2];
837 out[out_i++] = cea608_2[cea608_2_i * 2 + 1];
838 cea608_2_i++;
839 i++;
840 } else if (cea608_2_i < total_cea608_2_count) {
841 out[out_i++] = 0xf9;
842 out[out_i++] = 0x80;
843 out[out_i++] = 0x80;
844 cea608_2_i++;
845 i++;
846 }
847 }
848
849 g_assert_cmpint (out_i / 3, <=, out_fps_entry->max_cea608_count);
850
851 *out_size = out_i;
852
853 if (ccp_data) {
854 memcpy (&out[out_i], ccp_data, ccp_data_len);
855 *out_size += ccp_data_len;
856 }
857
858 g_assert_cmpint (*out_size, <, MAX_CDP_PACKET_LEN);
859
860 return TRUE;
861 }
862
863 /* takes cc_data cea608_1, cea608_2 and attempts to fit it into a hypothetical
864 * output packet. Any leftover data is stored for later addition. Returns
865 * whether any output can be generated. @ccp_data_len, @cea608_1_len,
866 * @cea608_2_len are also updated to reflect the size of that data to add to
867 * the output packet */
868 static gboolean
fit_and_scale_cc_data(GstCCConverter * self,const struct cdp_fps_entry * in_fps_entry,const struct cdp_fps_entry * out_fps_entry,const guint8 * ccp_data,guint * ccp_data_len,const guint8 * cea608_1,guint * cea608_1_len,const guint8 * cea608_2,guint * cea608_2_len,const GstVideoTimeCode * tc)869 fit_and_scale_cc_data (GstCCConverter * self,
870 const struct cdp_fps_entry *in_fps_entry,
871 const struct cdp_fps_entry *out_fps_entry, const guint8 * ccp_data,
872 guint * ccp_data_len, const guint8 * cea608_1, guint * cea608_1_len,
873 const guint8 * cea608_2, guint * cea608_2_len, const GstVideoTimeCode * tc)
874 {
875 if (!in_fps_entry || in_fps_entry->fps_n == 0) {
876 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
877 if (!in_fps_entry || in_fps_entry->fps_n == 0)
878 g_assert_not_reached ();
879 }
880
881 /* This is slightly looser than checking for the exact framerate as the cdp
882 * spec allow for 0.1% difference between framerates to be considered equal */
883 if (in_fps_entry->max_cc_count == out_fps_entry->max_cc_count) {
884 if (tc && tc->config.fps_n != 0)
885 interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
886 out_fps_entry->fps_d, 1, 1, &self->current_output_timecode);
887
888 self->scratch_ccp_len = 0;
889 self->scratch_cea608_1_len = 0;
890 self->scratch_cea608_2_len = 0;
891 self->input_frames = 0;
892 self->output_frames = 0;
893 } else {
894 int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
895 int output_time_cmp, scale_n, scale_d, rate_cmp;
896
897 /* TODO: handle input discont */
898
899 /* compute the relative frame count for each */
900 if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
901 self->input_frames, 1, &input_frame_n, &input_frame_d))
902 /* we should never overflow */
903 g_assert_not_reached ();
904
905 if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
906 self->output_frames, 1, &output_frame_n, &output_frame_d))
907 /* we should never overflow */
908 g_assert_not_reached ();
909
910 output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
911 output_frame_n, output_frame_d);
912
913 /* compute the relative rates of the two framerates */
914 get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
915
916 rate_cmp = gst_util_fraction_compare (scale_n, scale_d, 1, 1);
917
918 GST_TRACE_OBJECT (self, "performing framerate conversion at scale %d/%d "
919 "of cc data of with sizes, ccp:%u, cea608-1:%u, cea608-2:%u", scale_n,
920 scale_d, VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
921 VAL_OR_0 (cea608_2_len));
922
923 if (rate_cmp == 0) {
924 /* we are not scaling. Should never happen with current conditions
925 * above */
926 g_assert_not_reached ();
927 } else if (output_time_cmp < 0) {
928 /* we can't generate an output yet */
929 guint cd_len = ccp_data_len ? *ccp_data_len : 0;
930 guint c1_len = cea608_1_len ? *cea608_1_len : 0;
931 guint c2_len = cea608_2_len ? *cea608_2_len : 0;
932
933 store_cc_data (self, ccp_data, cd_len, cea608_1, c1_len, cea608_2,
934 c2_len);
935 if (ccp_data_len)
936 *ccp_data_len = 0;
937 if (cea608_1_len)
938 *cea608_1_len = 0;
939 if (cea608_2_len)
940 *cea608_2_len = 0;
941 return FALSE;
942 } else if (rate_cmp != 0) {
943 /* we are changing the framerate and may overflow the max output packet
944 * size. Split them where necessary. */
945 gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0;
946 gint ccp_off = 0, cea608_1_off = 0, cea608_2_off = 0;
947
948 if (output_time_cmp == 0) {
949 /* we have completed a cycle and can reset our counters to avoid
950 * overflow. Anything that fits into the output packet will be written */
951 GST_LOG_OBJECT (self, "cycle completed, resetting frame counters");
952 self->scratch_ccp_len = 0;
953 self->scratch_cea608_1_len = 0;
954 self->scratch_cea608_2_len = 0;
955 self->input_frames = 0;
956 self->output_frames = 0;
957 }
958
959 if (ccp_data_len) {
960 extra_ccp = *ccp_data_len - 3 * out_fps_entry->max_ccp_count;
961 extra_ccp = MAX (0, extra_ccp);
962 ccp_off = *ccp_data_len - extra_ccp;
963 }
964
965 if (cea608_1_len) {
966 extra_cea608_1 = *cea608_1_len - 2 * out_fps_entry->max_cea608_count;
967 extra_cea608_1 = MAX (0, extra_cea608_1);
968 cea608_1_off = *cea608_1_len - extra_cea608_1;
969 }
970
971 if (cea608_2_len) {
972 /* this prefers using field1 data first. This may not be quite correct */
973 if (extra_cea608_1 > 0) {
974 /* all the cea608 space is for field 1 */
975 extra_cea608_2 = *cea608_2_len;
976 cea608_2_off = 0;
977 } else if (cea608_1_len) {
978 /* cea608 space is shared between field 1 and field 2 */
979 extra_cea608_2 =
980 *cea608_1_len + *cea608_2_len -
981 2 * out_fps_entry->max_cea608_count;
982 extra_cea608_2 = MAX (0, extra_cea608_2);
983 cea608_2_off = *cea608_2_len - extra_cea608_2;
984 } else {
985 /* all of the cea608 space is for field 2 */
986 extra_cea608_2 = *cea608_2_len - 2 * out_fps_entry->max_cea608_count;
987 extra_cea608_2 = MAX (0, extra_cea608_2);
988 cea608_2_off = *cea608_2_len - extra_cea608_2;
989 }
990 }
991
992 if (extra_ccp > 0 || extra_cea608_1 > 0 || extra_cea608_2 > 0) {
993 /* packet would overflow, push extra bytes into the next packet */
994 GST_DEBUG_OBJECT (self, "buffer would overflow by %u ccp bytes, "
995 "%u cea608 field 1 bytes, or %u cea608 field 2 bytes", extra_ccp,
996 extra_cea608_1, extra_cea608_2);
997 store_cc_data (self, &ccp_data[ccp_off], extra_ccp,
998 &cea608_1[cea608_1_off], extra_cea608_1, &cea608_2[cea608_2_off],
999 extra_cea608_2);
1000 if (ccp_data_len)
1001 *ccp_data_len = MIN (*ccp_data_len, ccp_off);
1002 if (cea608_1_len)
1003 *cea608_1_len = MIN (*cea608_1_len, cea608_1_off);
1004 if (cea608_2_len)
1005 *cea608_2_len = MIN (*cea608_2_len, cea608_2_off);
1006 } else {
1007 GST_DEBUG_OBJECT (self, "section sizes of %u ccp bytes, "
1008 "%u cea608 field 1 bytes, and %u cea608 field 2 bytes fit within "
1009 "output packet", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
1010 VAL_OR_0 (cea608_2_len));
1011 self->scratch_ccp_len = 0;
1012 self->scratch_cea608_1_len = 0;
1013 self->scratch_cea608_2_len = 0;
1014 }
1015 } else {
1016 g_assert_not_reached ();
1017 }
1018
1019 if (tc && tc->config.fps_n != 0)
1020 interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
1021 out_fps_entry->fps_d, scale_n, scale_d,
1022 &self->current_output_timecode);
1023 }
1024
1025 g_assert_cmpint (VAL_OR_0 (ccp_data_len) + (VAL_OR_0 (cea608_1_len) +
1026 VAL_OR_0 (cea608_2_len)) / 2 * 3, <=,
1027 3 * out_fps_entry->max_cc_count);
1028
1029 GST_DEBUG_OBJECT (self, "write out packet with lengths ccp:%u, cea608-1:%u, "
1030 "cea608-2:%u", VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
1031 VAL_OR_0 (cea608_2_len));
1032
1033 return TRUE;
1034 }
1035
1036 /* Converts raw CEA708 cc_data and an optional timecode into CDP */
1037 static guint
convert_cea708_cc_data_cea708_cdp_internal(GstCCConverter * self,const guint8 * cc_data,guint cc_data_len,guint8 * cdp,guint cdp_len,const GstVideoTimeCode * tc,const struct cdp_fps_entry * fps_entry)1038 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
1039 const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
1040 const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
1041 {
1042 GstByteWriter bw;
1043 guint8 flags, checksum;
1044 guint i, len;
1045
1046 GST_DEBUG_OBJECT (self, "writing out cdp packet from cc_data with length %u",
1047 cc_data_len);
1048
1049 gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
1050 gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
1051 /* Write a length of 0 for now */
1052 gst_byte_writer_put_uint8_unchecked (&bw, 0);
1053
1054 gst_byte_writer_put_uint8_unchecked (&bw, fps_entry->fps_idx);
1055
1056 if (cc_data_len / 3 > fps_entry->max_cc_count) {
1057 GST_WARNING_OBJECT (self, "Too many cc_data triplets for framerate: %u. "
1058 "Truncating to %u", cc_data_len / 3, fps_entry->max_cc_count);
1059 cc_data_len = 3 * fps_entry->max_cc_count;
1060 }
1061
1062 /* caption_service_active */
1063 flags = 0x02;
1064
1065 /* ccdata_present */
1066 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA))
1067 flags |= 0x40;
1068
1069 /* time_code_present */
1070 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
1071 && tc->config.fps_n > 0)
1072 flags |= 0x80;
1073
1074 /* reserved */
1075 flags |= 0x01;
1076
1077 gst_byte_writer_put_uint8_unchecked (&bw, flags);
1078
1079 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
1080
1081 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
1082 && tc->config.fps_n > 0) {
1083 guint8 u8;
1084
1085 gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
1086 /* reserved 11 - 2 bits */
1087 u8 = 0xc0;
1088 /* tens of hours - 2 bits */
1089 u8 |= ((tc->hours / 10) & 0x3) << 4;
1090 /* units of hours - 4 bits */
1091 u8 |= (tc->hours % 10) & 0xf;
1092 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1093
1094 /* reserved 1 - 1 bit */
1095 u8 = 0x80;
1096 /* tens of minutes - 3 bits */
1097 u8 |= ((tc->minutes / 10) & 0x7) << 4;
1098 /* units of minutes - 4 bits */
1099 u8 |= (tc->minutes % 10) & 0xf;
1100 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1101
1102 /* field flag - 1 bit */
1103 u8 = tc->field_count < 2 ? 0x00 : 0x80;
1104 /* tens of seconds - 3 bits */
1105 u8 |= ((tc->seconds / 10) & 0x7) << 4;
1106 /* units of seconds - 4 bits */
1107 u8 |= (tc->seconds % 10) & 0xf;
1108 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1109
1110 /* drop frame flag - 1 bit */
1111 u8 = (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
1112 0x00;
1113 /* reserved0 - 1 bit */
1114 /* tens of frames - 2 bits */
1115 u8 |= ((tc->frames / 10) & 0x3) << 4;
1116 /* units of frames 4 bits */
1117 u8 |= (tc->frames % 10) & 0xf;
1118 gst_byte_writer_put_uint8_unchecked (&bw, u8);
1119 }
1120
1121 if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA)) {
1122 gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
1123 gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | fps_entry->max_cc_count);
1124 gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
1125 while (fps_entry->max_cc_count > cc_data_len / 3) {
1126 gst_byte_writer_put_uint8_unchecked (&bw, 0xfa);
1127 gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
1128 gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
1129 cc_data_len += 3;
1130 }
1131 }
1132
1133 gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
1134 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
1135 self->cdp_hdr_sequence_cntr++;
1136 /* We calculate the checksum afterwards */
1137 gst_byte_writer_put_uint8_unchecked (&bw, 0);
1138
1139 len = gst_byte_writer_get_pos (&bw);
1140 gst_byte_writer_set_pos (&bw, 2);
1141 gst_byte_writer_put_uint8_unchecked (&bw, len);
1142
1143 checksum = 0;
1144 for (i = 0; i < len; i++) {
1145 checksum += cdp[i];
1146 }
1147 checksum &= 0xff;
1148 checksum = 256 - checksum;
1149 cdp[len - 1] = checksum;
1150
1151 return len;
1152 }
1153
1154 /* Converts CDP into raw CEA708 cc_data */
1155 static guint
convert_cea708_cdp_cea708_cc_data_internal(GstCCConverter * self,const guint8 * cdp,guint cdp_len,guint8 cc_data[MAX_CDP_PACKET_LEN],GstVideoTimeCode * tc,const struct cdp_fps_entry ** out_fps_entry)1156 convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
1157 const guint8 * cdp, guint cdp_len, guint8 cc_data[MAX_CDP_PACKET_LEN],
1158 GstVideoTimeCode * tc, const struct cdp_fps_entry **out_fps_entry)
1159 {
1160 GstByteReader br;
1161 guint16 u16;
1162 guint8 u8;
1163 guint8 flags;
1164 guint len = 0;
1165 const struct cdp_fps_entry *fps_entry;
1166
1167 *out_fps_entry = &null_fps_entry;
1168 memset (tc, 0, sizeof (*tc));
1169
1170 /* Header + footer length */
1171 if (cdp_len < 11) {
1172 GST_WARNING_OBJECT (self, "cdp packet too short (%u). expected at "
1173 "least %u", cdp_len, 11);
1174 return 0;
1175 }
1176
1177 gst_byte_reader_init (&br, cdp, cdp_len);
1178 u16 = gst_byte_reader_get_uint16_be_unchecked (&br);
1179 if (u16 != 0x9669) {
1180 GST_WARNING_OBJECT (self, "cdp packet does not have initial magic bytes "
1181 "of 0x9669");
1182 return 0;
1183 }
1184
1185 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1186 if (u8 != cdp_len) {
1187 GST_WARNING_OBJECT (self, "cdp packet length (%u) does not match passed "
1188 "in value (%u)", u8, cdp_len);
1189 return 0;
1190 }
1191
1192 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1193 fps_entry = cdp_fps_entry_from_id (u8);
1194 if (!fps_entry || fps_entry->fps_n == 0) {
1195 GST_WARNING_OBJECT (self, "cdp packet does not have a valid framerate "
1196 "id (0x%02x", u8);
1197 return 0;
1198 }
1199
1200 flags = gst_byte_reader_get_uint8_unchecked (&br);
1201 /* No cc_data? */
1202 if ((flags & 0x40) == 0) {
1203 GST_DEBUG_OBJECT (self, "cdp packet does have any cc_data");
1204 return 0;
1205 }
1206
1207 /* cdp_hdr_sequence_cntr */
1208 gst_byte_reader_skip_unchecked (&br, 2);
1209
1210 /* time_code_present */
1211 if (flags & 0x80) {
1212 guint8 hours, minutes, seconds, frames, fields;
1213 gboolean drop_frame;
1214
1215 if (gst_byte_reader_get_remaining (&br) < 5) {
1216 GST_WARNING_OBJECT (self, "cdp packet does not have enough data to "
1217 "contain a timecode (%u). Need at least 5 bytes",
1218 gst_byte_reader_get_remaining (&br));
1219 return 0;
1220 }
1221 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1222 if (u8 != 0x71) {
1223 GST_WARNING_OBJECT (self, "cdp packet does not have timecode start byte "
1224 "of 0x71, found 0x%02x", u8);
1225 return 0;
1226 }
1227
1228 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1229 if ((u8 & 0xc0) != 0xc0) {
1230 GST_WARNING_OBJECT (self, "reserved bits are not 0xc0, found 0x%02x", u8);
1231 return 0;
1232 }
1233
1234 hours = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
1235
1236 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1237 if ((u8 & 0x80) != 0x80) {
1238 GST_WARNING_OBJECT (self, "reserved bit is not 0x80, found 0x%02x", u8);
1239 return 0;
1240 }
1241 minutes = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
1242
1243 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1244 if (u8 & 0x80)
1245 fields = 2;
1246 else
1247 fields = 1;
1248 seconds = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
1249
1250 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1251 if (u8 & 0x40) {
1252 GST_WARNING_OBJECT (self, "reserved bit is not 0x0, found 0x%02x", u8);
1253 return 0;
1254 }
1255
1256 drop_frame = ! !(u8 & 0x80);
1257 frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
1258
1259 gst_video_time_code_init (tc, fps_entry->fps_n, fps_entry->fps_d, NULL,
1260 drop_frame ? GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME :
1261 GST_VIDEO_TIME_CODE_FLAGS_NONE, hours, minutes, seconds, frames,
1262 fields);
1263 }
1264
1265 /* ccdata_present */
1266 if (flags & 0x40) {
1267 guint8 cc_count;
1268
1269 if (gst_byte_reader_get_remaining (&br) < 2) {
1270 GST_WARNING_OBJECT (self, "not enough data to contain valid cc_data");
1271 return 0;
1272 }
1273 u8 = gst_byte_reader_get_uint8_unchecked (&br);
1274 if (u8 != 0x72) {
1275 GST_WARNING_OBJECT (self, "missing cc_data start code of 0x72, "
1276 "found 0x%02x", u8);
1277 return 0;
1278 }
1279
1280 cc_count = gst_byte_reader_get_uint8_unchecked (&br);
1281 if ((cc_count & 0xe0) != 0xe0) {
1282 GST_WARNING_OBJECT (self, "reserved bits are not 0xe0, found 0x%02x", u8);
1283 return 0;
1284 }
1285 cc_count &= 0x1f;
1286
1287 len = 3 * cc_count;
1288 if (gst_byte_reader_get_remaining (&br) < len)
1289 return 0;
1290
1291 memcpy (cc_data, gst_byte_reader_get_data_unchecked (&br, len), len);
1292 }
1293
1294 *out_fps_entry = fps_entry;
1295
1296 /* skip everything else we don't care about */
1297 return len;
1298 }
1299
1300 static gboolean
copy_from_stored_data(GstCCConverter * self,guint8 * out_ccp,guint * ccp_size,guint8 * cea608_1,guint * cea608_1_len,guint8 * cea608_2,guint * cea608_2_len)1301 copy_from_stored_data (GstCCConverter * self, guint8 * out_ccp,
1302 guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len,
1303 guint8 * cea608_2, guint * cea608_2_len)
1304 {
1305 guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0;
1306
1307 g_assert ((out_ccp && ccp_size) || (!out_ccp && !ccp_size));
1308 g_assert ((cea608_1 && cea608_1_len) || (!cea608_1 && !cea608_1_len));
1309 g_assert ((cea608_2 && cea608_2_len) || (!cea608_2 && !cea608_2_len));
1310
1311 if (ccp_size) {
1312 ccp_in_size = *ccp_size;
1313 *ccp_size = 0;
1314 }
1315 if (cea608_1_len) {
1316 cea608_1_in_size = *cea608_1_len;
1317 *cea608_1_len = 0;
1318 }
1319 if (cea608_2_len) {
1320 cea608_2_in_size = *cea608_2_len;
1321 *cea608_2_len = 0;
1322 }
1323
1324 if (out_ccp && self->scratch_ccp_len > 0) {
1325 GST_DEBUG_OBJECT (self, "copying from previous scratch ccp buffer of "
1326 "%u bytes", self->scratch_ccp_len);
1327 if (ccp_in_size < *ccp_size + self->scratch_ccp_len) {
1328 GST_WARNING_OBJECT (self, "output buffer too small %u < %u", ccp_in_size,
1329 *ccp_size + self->scratch_ccp_len);
1330 goto fail;
1331 }
1332 memcpy (&out_ccp[*ccp_size], self->scratch_ccp, self->scratch_ccp_len);
1333 *ccp_size += self->scratch_ccp_len;
1334 }
1335
1336 if (cea608_1 && self->scratch_cea608_1_len > 0) {
1337 GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 1 "
1338 "buffer of %u bytes", self->scratch_cea608_1_len);
1339 if (cea608_1_in_size < *cea608_1_len + self->scratch_cea608_1_len) {
1340 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1341 cea608_1_in_size, *cea608_1_len + self->scratch_cea608_1_len);
1342 goto fail;
1343 }
1344 memcpy (&cea608_1[*cea608_1_len], self->scratch_cea608_1,
1345 self->scratch_cea608_1_len);
1346 *cea608_1_len += self->scratch_cea608_1_len;
1347 }
1348
1349 if (cea608_2 && self->scratch_cea608_2_len > 0) {
1350 GST_DEBUG_OBJECT (self, "copying from previous scratch cea608 field 2 "
1351 "buffer of %u bytes", self->scratch_cea608_2_len);
1352 if (cea608_2_in_size < *cea608_2_len + self->scratch_cea608_2_len) {
1353 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1354 cea608_2_in_size, *cea608_2_len + self->scratch_cea608_2_len);
1355 goto fail;
1356 }
1357 memcpy (&cea608_2[*cea608_2_len], self->scratch_cea608_2,
1358 self->scratch_cea608_2_len);
1359 *cea608_2_len += self->scratch_cea608_2_len;
1360 }
1361
1362 return TRUE;
1363
1364 fail:
1365 if (ccp_size)
1366 *ccp_size = 0;
1367 if (cea608_1_len)
1368 *cea608_1_len = 0;
1369 if (cea608_2_len)
1370 *cea608_2_len = 0;
1371 return FALSE;
1372 }
1373
1374 static gboolean
cc_data_to_cea608_ccp(GstCCConverter * self,guint8 * cc_data,guint cc_data_len,guint8 * out_ccp,guint * ccp_size,guint8 * cea608_1,guint * cea608_1_len,guint8 * cea608_2,guint * cea608_2_len,const struct cdp_fps_entry * in_fps_entry)1375 cc_data_to_cea608_ccp (GstCCConverter * self, guint8 * cc_data,
1376 guint cc_data_len, guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1,
1377 guint * cea608_1_len, guint8 * cea608_2, guint * cea608_2_len,
1378 const struct cdp_fps_entry *in_fps_entry)
1379 {
1380 guint ccp_in_size = 0, cea608_1_in_size = 0, cea608_2_in_size = 0;
1381
1382 g_assert (cc_data || cc_data_len == 0);
1383
1384 if (ccp_size)
1385 ccp_in_size = *ccp_size;
1386 if (cea608_1_len)
1387 cea608_1_in_size = *cea608_1_len;
1388 if (cea608_2_len)
1389 cea608_2_in_size = *cea608_2_len;
1390
1391 if (!copy_from_stored_data (self, out_ccp, ccp_size, cea608_1, cea608_1_len,
1392 cea608_2, cea608_2_len))
1393 goto fail;
1394
1395 if (cc_data) {
1396 gint ccp_offset = 0;
1397 guint new_cea608_1_len = 0, new_cea608_2_len = 0;
1398 guint8 *new_cea608_1 = cea608_1, *new_cea608_2 = cea608_2;
1399
1400 if (cea608_1_len) {
1401 new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
1402 new_cea608_1 = &cea608_1[*cea608_1_len];
1403 }
1404 if (cea608_2_len) {
1405 new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
1406 new_cea608_2 = &cea608_2[*cea608_2_len];
1407 }
1408
1409 cc_data_len = compact_cc_data (cc_data, cc_data_len);
1410
1411 if (cc_data_len / 3 > in_fps_entry->max_cc_count) {
1412 GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u. "
1413 "Truncating to %u", cc_data_len / 3, in_fps_entry->max_cc_count);
1414 cc_data_len = 3 * in_fps_entry->max_cc_count;
1415 }
1416
1417 ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
1418 &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
1419 if (ccp_offset < 0) {
1420 GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
1421 goto fail;
1422 }
1423
1424 if ((new_cea608_1_len + new_cea608_2_len) / 2 >
1425 in_fps_entry->max_cea608_count) {
1426 GST_WARNING_OBJECT (self, "Too many cea608 triples in CDP packet %u. "
1427 "Truncating to %u", (new_cea608_1_len + new_cea608_2_len) / 2,
1428 in_fps_entry->max_cea608_count);
1429 if ((new_cea608_1_len + new_cea608_2_len) / 2 >
1430 in_fps_entry->max_cea608_count) {
1431 new_cea608_1_len = 2 * in_fps_entry->max_cea608_count;
1432 new_cea608_2_len = 0;
1433 } else {
1434 new_cea608_2_len =
1435 2 * in_fps_entry->max_cea608_count - new_cea608_1_len;
1436 }
1437 }
1438
1439 if (cea608_1_len)
1440 *cea608_1_len += new_cea608_1_len;
1441 if (cea608_2_len)
1442 *cea608_2_len += new_cea608_2_len;
1443
1444 if (out_ccp) {
1445 if (ccp_in_size < *ccp_size + cc_data_len - ccp_offset) {
1446 GST_WARNING_OBJECT (self, "output buffer too small %u < %u",
1447 ccp_in_size, *ccp_size + cc_data_len - ccp_offset);
1448 goto fail;
1449 }
1450 memcpy (&out_ccp[*ccp_size], &cc_data[ccp_offset],
1451 cc_data_len - ccp_offset);
1452 *ccp_size += cc_data_len - ccp_offset;
1453 }
1454 }
1455
1456 return TRUE;
1457
1458 fail:
1459 if (ccp_size)
1460 *ccp_size = 0;
1461 if (cea608_1_len)
1462 *cea608_1_len = 0;
1463 if (cea608_2_len)
1464 *cea608_2_len = 0;
1465 return FALSE;
1466 }
1467
1468 static gboolean
cdp_to_cea608_cc_data(GstCCConverter * self,GstBuffer * inbuf,guint8 * out_ccp,guint * ccp_size,guint8 * cea608_1,guint * cea608_1_len,guint8 * cea608_2,guint * cea608_2_len,GstVideoTimeCode * out_tc,const struct cdp_fps_entry ** in_fps_entry)1469 cdp_to_cea608_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1470 guint8 * out_ccp, guint * ccp_size, guint8 * cea608_1, guint * cea608_1_len,
1471 guint8 * cea608_2, guint * cea608_2_len, GstVideoTimeCode * out_tc,
1472 const struct cdp_fps_entry **in_fps_entry)
1473 {
1474 guint8 cc_data[MAX_CDP_PACKET_LEN];
1475 guint cc_data_len = 0;
1476 GstMapInfo in;
1477
1478 if (inbuf) {
1479 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1480
1481 cc_data_len =
1482 convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1483 cc_data, out_tc, in_fps_entry);
1484
1485 gst_buffer_unmap (inbuf, &in);
1486 self->input_frames++;
1487 }
1488
1489 return cc_data_to_cea608_ccp (self, inbuf ? cc_data : NULL, cc_data_len,
1490 out_ccp, ccp_size, cea608_1, cea608_1_len, cea608_2, cea608_2_len,
1491 inbuf ? *in_fps_entry : NULL);
1492 }
1493
1494 static GstFlowReturn
convert_cea608_raw_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1495 convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1496 GstBuffer * outbuf)
1497 {
1498 GstMapInfo in, out;
1499 guint i, n;
1500
1501 n = gst_buffer_get_size (inbuf);
1502 if (n & 1) {
1503 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1504 gst_buffer_set_size (outbuf, 0);
1505 return GST_FLOW_OK;
1506 }
1507
1508 n /= 2;
1509
1510 if (n > 3) {
1511 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
1512 3);
1513 n = 3;
1514 }
1515
1516 gst_buffer_set_size (outbuf, 3 * n);
1517
1518 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1519 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1520
1521 /* We have to assume that each value is from the first field and
1522 * don't know from which line offset it originally is */
1523 for (i = 0; i < n; i++) {
1524 out.data[i * 3] = 0x80;
1525 out.data[i * 3 + 1] = in.data[i * 2];
1526 out.data[i * 3 + 2] = in.data[i * 2 + 1];
1527 }
1528
1529 gst_buffer_unmap (inbuf, &in);
1530 gst_buffer_unmap (outbuf, &out);
1531
1532 return GST_FLOW_OK;
1533 }
1534
1535 static GstFlowReturn
convert_cea608_raw_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1536 convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1537 GstBuffer * outbuf)
1538 {
1539 GstMapInfo in, out;
1540 guint i, n;
1541
1542 n = gst_buffer_get_size (inbuf);
1543 if (n & 1) {
1544 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1545 gst_buffer_set_size (outbuf, 0);
1546 return GST_FLOW_OK;
1547 }
1548
1549 n /= 2;
1550
1551 if (n > 3) {
1552 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
1553 3);
1554 n = 3;
1555 }
1556
1557 gst_buffer_set_size (outbuf, 3 * n);
1558
1559 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1560 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1561
1562 /* We have to assume that each value is from the first field and
1563 * don't know from which line offset it originally is */
1564 for (i = 0; i < n; i++) {
1565 out.data[i * 3] = 0xfc;
1566 out.data[i * 3 + 1] = in.data[i * 2];
1567 out.data[i * 3 + 2] = in.data[i * 2 + 1];
1568 }
1569
1570 gst_buffer_unmap (inbuf, &in);
1571 gst_buffer_unmap (outbuf, &out);
1572
1573 return GST_FLOW_OK;
1574 }
1575
1576 static GstFlowReturn
convert_cea608_raw_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)1577 convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1578 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1579 {
1580 GstMapInfo in, out;
1581 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1582 guint cc_data_len = MAX_CDP_PACKET_LEN;
1583 guint cea608_1_len = MAX_CDP_PACKET_LEN;
1584 guint8 cc_data[MAX_CDP_PACKET_LEN], cea608_1[MAX_CEA608_LEN];
1585
1586 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1587 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1588 g_assert_not_reached ();
1589
1590 if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len, NULL, 0))
1591 goto drop;
1592
1593 if (inbuf) {
1594 guint n = 0;
1595
1596 n = gst_buffer_get_size (inbuf);
1597 if (n & 1) {
1598 GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
1599 gst_buffer_set_size (outbuf, 0);
1600 return GST_FLOW_OK;
1601 }
1602
1603 n /= 2;
1604
1605 if (n > in_fps_entry->max_cea608_count) {
1606 GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u",
1607 n, in_fps_entry->max_cea608_count);
1608 n = in_fps_entry->max_cea608_count;
1609 }
1610
1611 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1612 memcpy (&cea608_1[cea608_1_len], in.data, n * 2);
1613 gst_buffer_unmap (inbuf, &in);
1614 cea608_1_len += n * 2;
1615 self->input_frames++;
1616 }
1617
1618 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1619 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1620 g_assert_not_reached ();
1621
1622 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1623 cea608_1, &cea608_1_len, NULL, 0, tc_meta ? &tc_meta->tc : NULL))
1624 goto drop;
1625
1626 if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1,
1627 cea608_1_len, NULL, 0, cc_data, &cc_data_len))
1628 goto drop;
1629
1630 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1631 cc_data_len =
1632 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1633 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1634 self->output_frames++;
1635 gst_buffer_unmap (outbuf, &out);
1636
1637 out:
1638 gst_buffer_set_size (outbuf, cc_data_len);
1639
1640 return GST_FLOW_OK;
1641
1642 drop:
1643 cc_data_len = 0;
1644 goto out;
1645 }
1646
1647 static GstFlowReturn
convert_cea608_s334_1a_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1648 convert_cea608_s334_1a_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1649 GstBuffer * outbuf)
1650 {
1651 GstMapInfo in, out;
1652 guint i, n;
1653 guint cea608 = 0;
1654
1655 n = gst_buffer_get_size (inbuf);
1656 if (n % 3 != 0) {
1657 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1658 n = n - (n % 3);
1659 }
1660
1661 n /= 3;
1662
1663 if (n > 3) {
1664 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1665 n = 3;
1666 }
1667
1668 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1669 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1670
1671 for (i = 0; i < n; i++) {
1672 if (in.data[i * 3] & 0x80) {
1673 out.data[i * 2] = in.data[i * 3 + 1];
1674 out.data[i * 2 + 1] = in.data[i * 3 + 2];
1675 cea608++;
1676 }
1677 }
1678
1679 gst_buffer_unmap (inbuf, &in);
1680 gst_buffer_unmap (outbuf, &out);
1681
1682 gst_buffer_set_size (outbuf, 2 * cea608);
1683
1684 return GST_FLOW_OK;
1685 }
1686
1687 static GstFlowReturn
convert_cea608_s334_1a_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1688 convert_cea608_s334_1a_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1689 GstBuffer * outbuf)
1690 {
1691 GstMapInfo in, out;
1692 guint i, n;
1693
1694 n = gst_buffer_get_size (inbuf);
1695 if (n % 3 != 0) {
1696 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1697 n = n - (n % 3);
1698 }
1699
1700 n /= 3;
1701
1702 if (n > 3) {
1703 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1704 n = 3;
1705 }
1706
1707 gst_buffer_set_size (outbuf, 3 * n);
1708
1709 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1710 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1711
1712 for (i = 0; i < n; i++) {
1713 out.data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
1714 out.data[i * 3 + 1] = in.data[i * 3 + 1];
1715 out.data[i * 3 + 2] = in.data[i * 3 + 2];
1716 }
1717
1718 gst_buffer_unmap (inbuf, &in);
1719 gst_buffer_unmap (outbuf, &out);
1720
1721 return GST_FLOW_OK;
1722 }
1723
1724 static GstFlowReturn
convert_cea608_s334_1a_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)1725 convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1726 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1727 {
1728 GstMapInfo in, out;
1729 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1730 guint cc_data_len = MAX_CDP_PACKET_LEN;
1731 guint cea608_1_len = MAX_CDP_PACKET_LEN, cea608_2_len = MAX_CDP_PACKET_LEN;
1732 guint8 cc_data[MAX_CDP_PACKET_LEN];
1733 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
1734 guint i, n;
1735
1736 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1737 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1738 g_assert_not_reached ();
1739
1740 if (!copy_from_stored_data (self, NULL, 0, cea608_1, &cea608_1_len,
1741 cea608_2, &cea608_2_len))
1742 goto drop;
1743
1744 if (inbuf) {
1745 n = gst_buffer_get_size (inbuf);
1746 if (n % 3 != 0) {
1747 GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
1748 n = n - (n % 3);
1749 }
1750
1751 n /= 3;
1752
1753 if (n > in_fps_entry->max_cea608_count) {
1754 GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
1755 n = in_fps_entry->max_cea608_count;
1756 }
1757
1758 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1759
1760 for (i = 0; i < n; i++) {
1761 if (in.data[i * 3] & 0x80) {
1762 cea608_1[cea608_1_len++] = in.data[i * 3 + 1];
1763 cea608_1[cea608_1_len++] = in.data[i * 3 + 2];
1764 } else {
1765 cea608_2[cea608_2_len++] = in.data[i * 3 + 1];
1766 cea608_2[cea608_2_len++] = in.data[i * 3 + 2];
1767 }
1768 }
1769 gst_buffer_unmap (inbuf, &in);
1770 self->input_frames++;
1771 }
1772
1773 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1774 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1775 g_assert_not_reached ();
1776
1777 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1778 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1779 tc_meta ? &tc_meta->tc : NULL)) {
1780 goto drop;
1781 }
1782
1783 if (!combine_cc_data (self, TRUE, out_fps_entry, NULL, 0, cea608_1,
1784 cea608_1_len, cea608_2, cea608_2_len, cc_data, &cc_data_len)) {
1785 goto drop;
1786 }
1787
1788 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1789 cc_data_len =
1790 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1791 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1792 self->output_frames++;
1793 gst_buffer_unmap (outbuf, &out);
1794
1795 out:
1796 gst_buffer_set_size (outbuf, cc_data_len);
1797
1798 return GST_FLOW_OK;
1799
1800 drop:
1801 cc_data_len = 0;
1802 goto out;
1803 }
1804
1805 static GstFlowReturn
convert_cea708_cc_data_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1806 convert_cea708_cc_data_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1807 GstBuffer * outbuf)
1808 {
1809 GstMapInfo in, out;
1810 guint i, n;
1811 guint cea608 = 0;
1812
1813 n = gst_buffer_get_size (inbuf);
1814 if (n % 3 != 0) {
1815 GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1816 n = n - (n % 3);
1817 }
1818
1819 n /= 3;
1820
1821 if (n > 25) {
1822 GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1823 n = 25;
1824 }
1825
1826 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1827 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1828
1829 for (i = 0; i < n; i++) {
1830 /* We can only really copy the first field here as there can't be any
1831 * signalling in raw CEA608 and we must not mix the streams of different
1832 * fields
1833 */
1834 if (in.data[i * 3] == 0xfc) {
1835 out.data[cea608 * 2] = in.data[i * 3 + 1];
1836 out.data[cea608 * 2 + 1] = in.data[i * 3 + 2];
1837 cea608++;
1838 }
1839 }
1840
1841 gst_buffer_unmap (inbuf, &in);
1842 gst_buffer_unmap (outbuf, &out);
1843
1844 gst_buffer_set_size (outbuf, 2 * cea608);
1845
1846 return GST_FLOW_OK;
1847 }
1848
1849 static GstFlowReturn
convert_cea708_cc_data_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1850 convert_cea708_cc_data_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1851 GstBuffer * outbuf)
1852 {
1853 GstMapInfo in, out;
1854 guint i, n;
1855 guint cea608 = 0;
1856
1857 n = gst_buffer_get_size (inbuf);
1858 if (n % 3 != 0) {
1859 GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1860 n = n - (n % 3);
1861 }
1862
1863 n /= 3;
1864
1865 if (n > 25) {
1866 GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1867 n = 25;
1868 }
1869
1870 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1871 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1872
1873 for (i = 0; i < n; i++) {
1874 if (in.data[i * 3] == 0xfc || in.data[i * 3] == 0xfd) {
1875 /* We have to assume a line offset of 0 */
1876 out.data[cea608 * 3] = in.data[i * 3] == 0xfc ? 0x80 : 0x00;
1877 out.data[cea608 * 3 + 1] = in.data[i * 3 + 1];
1878 out.data[cea608 * 3 + 2] = in.data[i * 3 + 2];
1879 cea608++;
1880 }
1881 }
1882
1883 gst_buffer_unmap (inbuf, &in);
1884 gst_buffer_unmap (outbuf, &out);
1885
1886 gst_buffer_set_size (outbuf, 3 * cea608);
1887
1888 return GST_FLOW_OK;
1889 }
1890
1891 static GstFlowReturn
convert_cea708_cc_data_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)1892 convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1893 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1894 {
1895 GstMapInfo in, out;
1896 const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1897 guint in_cc_data_len;
1898 guint cc_data_len = MAX_CDP_PACKET_LEN, ccp_data_len = MAX_CDP_PACKET_LEN;
1899 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
1900 guint8 cc_data[MAX_CDP_PACKET_LEN], ccp_data[MAX_CDP_PACKET_LEN];
1901 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
1902 guint8 *in_cc_data;
1903
1904 if (inbuf) {
1905 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1906 in_cc_data = in.data;
1907 in_cc_data_len = in.size;
1908 self->input_frames++;
1909 } else {
1910 in_cc_data = NULL;
1911 in_cc_data_len = 0;
1912 }
1913
1914 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1915 if (!in_fps_entry || in_fps_entry->fps_n == 0)
1916 g_assert_not_reached ();
1917
1918 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1919 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1920 g_assert_not_reached ();
1921
1922 if (!cc_data_to_cea608_ccp (self, in_cc_data, in_cc_data_len, ccp_data,
1923 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1924 in_fps_entry)) {
1925 if (inbuf)
1926 gst_buffer_unmap (inbuf, &in);
1927 goto drop;
1928 }
1929
1930 if (inbuf)
1931 gst_buffer_unmap (inbuf, &in);
1932
1933 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
1934 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len,
1935 tc_meta ? &tc_meta->tc : NULL))
1936 goto drop;
1937
1938 if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len,
1939 cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data,
1940 &cc_data_len))
1941 goto drop;
1942
1943 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1944 cc_data_len =
1945 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1946 out.data, out.size, &self->current_output_timecode, out_fps_entry);
1947 self->output_frames++;
1948 gst_buffer_unmap (outbuf, &out);
1949
1950 out:
1951 gst_buffer_set_size (outbuf, cc_data_len);
1952
1953 return GST_FLOW_OK;
1954
1955 drop:
1956 cc_data_len = 0;
1957 goto out;
1958 }
1959
1960 static GstFlowReturn
convert_cea708_cdp_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)1961 convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1962 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1963 {
1964 GstMapInfo out;
1965 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1966 guint cea608_1_len;
1967 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1968
1969 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1970 cea608_1_len = (guint) out.size;
1971 if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, out.data, &cea608_1_len,
1972 NULL, NULL, &tc, &in_fps_entry)) {
1973 gst_buffer_set_size (outbuf, 0);
1974 return GST_FLOW_OK;
1975 }
1976
1977 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1978 if (!out_fps_entry || out_fps_entry->fps_n == 0)
1979 out_fps_entry = in_fps_entry;
1980
1981 if (fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
1982 out.data, &cea608_1_len, NULL, NULL, &tc)) {
1983 self->output_frames++;
1984 } else {
1985 cea608_1_len = 0;
1986 }
1987 gst_buffer_unmap (outbuf, &out);
1988
1989 gst_buffer_set_size (outbuf, cea608_1_len);
1990
1991 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
1992 gst_buffer_add_video_time_code_meta (outbuf,
1993 &self->current_output_timecode);
1994 gst_video_time_code_increment_frame (&self->current_output_timecode);
1995 }
1996
1997 return GST_FLOW_OK;
1998 }
1999
2000 static GstFlowReturn
convert_cea708_cdp_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)2001 convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
2002 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
2003 {
2004 GstMapInfo out;
2005 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
2006 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
2007 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
2008 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
2009 guint i, cc_data_len;
2010
2011 if (!cdp_to_cea608_cc_data (self, inbuf, NULL, NULL, cea608_1, &cea608_1_len,
2012 cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2013 goto drop;
2014
2015 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2016 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2017 out_fps_entry = in_fps_entry;
2018
2019 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, NULL, 0,
2020 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2021 goto drop;
2022
2023 cc_data_len = gst_buffer_get_sizes (outbuf, NULL, NULL);
2024
2025 gst_buffer_map (outbuf, &out, GST_MAP_READWRITE);
2026 if (!combine_cc_data (self, FALSE, out_fps_entry, NULL, 0, cea608_1,
2027 cea608_1_len, cea608_2, cea608_2_len, out.data, &cc_data_len)) {
2028 gst_buffer_unmap (outbuf, &out);
2029 goto drop;
2030 }
2031
2032 for (i = 0; i < cc_data_len / 3; i++)
2033 /* We have to assume a line offset of 0 */
2034 out.data[i * 3] = out.data[i * 3] == 0xfc ? 0x80 : 0x00;
2035
2036 gst_buffer_unmap (outbuf, &out);
2037 self->output_frames++;
2038
2039 gst_buffer_set_size (outbuf, cc_data_len);
2040
2041 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
2042 gst_buffer_add_video_time_code_meta (outbuf,
2043 &self->current_output_timecode);
2044 gst_video_time_code_increment_frame (&self->current_output_timecode);
2045 }
2046
2047 return GST_FLOW_OK;
2048
2049 drop:
2050 gst_buffer_set_size (outbuf, 0);
2051 return GST_FLOW_OK;
2052 }
2053
2054 static GstFlowReturn
convert_cea708_cdp_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf,const GstVideoTimeCodeMeta * tc_meta)2055 convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
2056 GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
2057 {
2058 GstMapInfo out;
2059 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
2060 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
2061 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
2062 guint8 ccp_data[MAX_CDP_PACKET_LEN];
2063 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
2064 guint ccp_data_len = MAX_CDP_PACKET_LEN;
2065 guint out_len = 0;
2066
2067 if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len,
2068 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2069 goto out;
2070
2071 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2072 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2073 out_fps_entry = in_fps_entry;
2074
2075 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
2076 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2077 goto out;
2078
2079 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
2080 out_len = (guint) out.size;
2081 if (!combine_cc_data (self, FALSE, out_fps_entry, ccp_data, ccp_data_len,
2082 cea608_1, cea608_1_len, cea608_2, cea608_2_len, out.data, &out_len)) {
2083 gst_buffer_unmap (outbuf, &out);
2084 out_len = 0;
2085 goto out;
2086 }
2087
2088 gst_buffer_unmap (outbuf, &out);
2089 self->output_frames++;
2090
2091 if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
2092 gst_buffer_add_video_time_code_meta (outbuf,
2093 &self->current_output_timecode);
2094 gst_video_time_code_increment_frame (&self->current_output_timecode);
2095 }
2096
2097 out:
2098 gst_buffer_set_size (outbuf, out_len);
2099
2100 return GST_FLOW_OK;
2101 }
2102
2103 static GstFlowReturn
convert_cea708_cdp_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)2104 convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
2105 GstBuffer * outbuf)
2106 {
2107 GstMapInfo out;
2108 GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
2109 const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
2110 guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
2111 guint8 ccp_data[MAX_CDP_PACKET_LEN], cc_data[MAX_CDP_PACKET_LEN];
2112 guint cea608_1_len = MAX_CEA608_LEN, cea608_2_len = MAX_CEA608_LEN;
2113 guint ccp_data_len = MAX_CDP_PACKET_LEN, cc_data_len = MAX_CDP_PACKET_LEN;
2114 guint out_len = 0;
2115
2116 if (!cdp_to_cea608_cc_data (self, inbuf, ccp_data, &ccp_data_len,
2117 cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc, &in_fps_entry))
2118 goto out;
2119
2120 out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
2121 if (!out_fps_entry || out_fps_entry->fps_n == 0)
2122 out_fps_entry = in_fps_entry;
2123
2124 if (!fit_and_scale_cc_data (self, in_fps_entry, out_fps_entry, ccp_data,
2125 &ccp_data_len, cea608_1, &cea608_1_len, cea608_2, &cea608_2_len, &tc))
2126 goto out;
2127
2128 if (!combine_cc_data (self, TRUE, out_fps_entry, ccp_data, ccp_data_len,
2129 cea608_1, cea608_1_len, cea608_2, cea608_2_len, cc_data,
2130 &cc_data_len)) {
2131 goto out;
2132 }
2133
2134 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
2135 out_len =
2136 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
2137 out.data, out.size, &self->current_output_timecode, out_fps_entry);
2138
2139 gst_buffer_unmap (outbuf, &out);
2140 self->output_frames++;
2141
2142 out:
2143 gst_buffer_set_size (outbuf, out_len);
2144
2145 return GST_FLOW_OK;
2146 }
2147
2148 static GstFlowReturn
gst_cc_converter_transform(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)2149 gst_cc_converter_transform (GstCCConverter * self, GstBuffer * inbuf,
2150 GstBuffer * outbuf)
2151 {
2152 GstVideoTimeCodeMeta *tc_meta = NULL;
2153 GstFlowReturn ret = GST_FLOW_OK;
2154
2155 GST_DEBUG_OBJECT (self, "Converting %" GST_PTR_FORMAT " from %u to %u", inbuf,
2156 self->input_caption_type, self->output_caption_type);
2157
2158 if (inbuf)
2159 tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
2160
2161 if (tc_meta) {
2162 if (self->current_output_timecode.config.fps_n <= 0) {
2163 /* XXX: this assumes the input time codes are well-formed and increase
2164 * at the rate of one frame for each input buffer */
2165 const struct cdp_fps_entry *in_fps_entry;
2166 gint scale_n, scale_d;
2167
2168 in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
2169 if (!in_fps_entry || in_fps_entry->fps_n == 0)
2170 scale_n = scale_d = 1;
2171 else
2172 get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
2173
2174 interpolate_time_code_with_framerate (self, &tc_meta->tc,
2175 self->out_fps_n, self->out_fps_d, scale_n, scale_d,
2176 &self->current_output_timecode);
2177 }
2178 }
2179
2180 switch (self->input_caption_type) {
2181 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2182
2183 switch (self->output_caption_type) {
2184 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2185 ret = convert_cea608_raw_cea608_s334_1a (self, inbuf, outbuf);
2186 break;
2187 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2188 ret = convert_cea608_raw_cea708_cc_data (self, inbuf, outbuf);
2189 break;
2190 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2191 ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf, tc_meta);
2192 break;
2193 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2194 default:
2195 g_assert_not_reached ();
2196 break;
2197 }
2198
2199 break;
2200 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2201
2202 switch (self->output_caption_type) {
2203 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2204 ret = convert_cea608_s334_1a_cea608_raw (self, inbuf, outbuf);
2205 break;
2206 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2207 ret = convert_cea608_s334_1a_cea708_cc_data (self, inbuf, outbuf);
2208 break;
2209 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2210 ret =
2211 convert_cea608_s334_1a_cea708_cdp (self, inbuf, outbuf, tc_meta);
2212 break;
2213 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2214 default:
2215 g_assert_not_reached ();
2216 break;
2217 }
2218
2219 break;
2220 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2221
2222 switch (self->output_caption_type) {
2223 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2224 ret = convert_cea708_cc_data_cea608_raw (self, inbuf, outbuf);
2225 break;
2226 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2227 ret = convert_cea708_cc_data_cea608_s334_1a (self, inbuf, outbuf);
2228 break;
2229 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2230 ret =
2231 convert_cea708_cc_data_cea708_cdp (self, inbuf, outbuf, tc_meta);
2232 break;
2233 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2234 default:
2235 g_assert_not_reached ();
2236 break;
2237 }
2238
2239 break;
2240 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2241
2242 switch (self->output_caption_type) {
2243 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
2244 ret = convert_cea708_cdp_cea608_raw (self, inbuf, outbuf, tc_meta);
2245 break;
2246 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
2247 ret =
2248 convert_cea708_cdp_cea608_s334_1a (self, inbuf, outbuf, tc_meta);
2249 break;
2250 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
2251 ret =
2252 convert_cea708_cdp_cea708_cc_data (self, inbuf, outbuf, tc_meta);
2253 break;
2254 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
2255 ret = convert_cea708_cdp_cea708_cdp (self, inbuf, outbuf);
2256 break;
2257 default:
2258 g_assert_not_reached ();
2259 break;
2260 }
2261
2262 break;
2263 default:
2264 g_assert_not_reached ();
2265 break;
2266 }
2267
2268 if (ret != GST_FLOW_OK) {
2269 GST_DEBUG_OBJECT (self, "returning %s", gst_flow_get_name (ret));
2270 return ret;
2271 }
2272
2273 GST_DEBUG_OBJECT (self, "Converted to %" GST_PTR_FORMAT, outbuf);
2274
2275 if (gst_buffer_get_size (outbuf) > 0) {
2276 if (self->current_output_timecode.config.fps_n > 0) {
2277 gst_buffer_add_video_time_code_meta (outbuf,
2278 &self->current_output_timecode);
2279 gst_video_time_code_increment_frame (&self->current_output_timecode);
2280 }
2281
2282 return GST_FLOW_OK;
2283 } else {
2284 return GST_FLOW_OK;
2285 }
2286 }
2287
2288 static gboolean
gst_cc_converter_transform_meta(GstBaseTransform * base,GstBuffer * outbuf,GstMeta * meta,GstBuffer * inbuf)2289 gst_cc_converter_transform_meta (GstBaseTransform * base, GstBuffer * outbuf,
2290 GstMeta * meta, GstBuffer * inbuf)
2291 {
2292 const GstMetaInfo *info = meta->info;
2293
2294 /* we do this manually for framerate scaling */
2295 if (info->api == GST_VIDEO_TIME_CODE_META_API_TYPE)
2296 return FALSE;
2297
2298 return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (base, outbuf,
2299 meta, inbuf);
2300 }
2301
2302 static gboolean
can_generate_output(GstCCConverter * self)2303 can_generate_output (GstCCConverter * self)
2304 {
2305 int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
2306 int output_time_cmp;
2307
2308 if (self->in_fps_n == 0 || self->out_fps_n == 0)
2309 return FALSE;
2310
2311 /* compute the relative frame count for each */
2312 if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
2313 self->input_frames, 1, &input_frame_n, &input_frame_d))
2314 /* we should never overflow */
2315 g_assert_not_reached ();
2316
2317 if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
2318 self->output_frames, 1, &output_frame_n, &output_frame_d))
2319 /* we should never overflow */
2320 g_assert_not_reached ();
2321
2322 output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
2323 output_frame_n, output_frame_d);
2324
2325 /* if the next output frame is at or before the current input frame */
2326 if (output_time_cmp >= 0)
2327 return TRUE;
2328
2329 return FALSE;
2330 }
2331
2332 static void
reset_counters(GstCCConverter * self)2333 reset_counters (GstCCConverter * self)
2334 {
2335 self->scratch_ccp_len = 0;
2336 self->scratch_cea608_1_len = 0;
2337 self->scratch_cea608_2_len = 0;
2338 self->input_frames = 0;
2339 self->output_frames = 1;
2340 gst_video_time_code_clear (&self->current_output_timecode);
2341 gst_clear_buffer (&self->previous_buffer);
2342 }
2343
2344 static GstFlowReturn
drain_input(GstCCConverter * self)2345 drain_input (GstCCConverter * self)
2346 {
2347 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (self);
2348 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
2349 GstFlowReturn ret = GST_FLOW_OK;
2350
2351 while (self->scratch_ccp_len > 0 || self->scratch_cea608_1_len > 0
2352 || self->scratch_cea608_2_len > 0 || can_generate_output (self)) {
2353 GstBuffer *outbuf;
2354
2355 if (!self->previous_buffer) {
2356 GST_WARNING_OBJECT (self, "Attempt to draining without a previous "
2357 "buffer. Aborting");
2358 return GST_FLOW_OK;
2359 }
2360
2361 outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
2362
2363 if (bclass->copy_metadata) {
2364 if (!bclass->copy_metadata (trans, self->previous_buffer, outbuf)) {
2365 /* something failed, post a warning */
2366 GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
2367 ("could not copy metadata"), (NULL));
2368 }
2369 }
2370
2371 ret = gst_cc_converter_transform (self, NULL, outbuf);
2372 if (gst_buffer_get_size (outbuf) <= 0) {
2373 /* try to move the output along */
2374 self->input_frames++;
2375 gst_buffer_unref (outbuf);
2376 continue;
2377 } else if (ret != GST_FLOW_OK) {
2378 gst_buffer_unref (outbuf);
2379 return ret;
2380 }
2381
2382 ret = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (trans), outbuf);
2383 if (ret != GST_FLOW_OK) {
2384 return ret;
2385 }
2386 }
2387
2388 return ret;
2389 }
2390
2391 static GstFlowReturn
gst_cc_converter_generate_output(GstBaseTransform * base,GstBuffer ** outbuf)2392 gst_cc_converter_generate_output (GstBaseTransform * base, GstBuffer ** outbuf)
2393 {
2394 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (base);
2395 GstCCConverter *self = GST_CCCONVERTER (base);
2396 GstBuffer *inbuf = base->queued_buf;
2397 GstFlowReturn ret;
2398
2399 *outbuf = NULL;
2400 base->queued_buf = NULL;
2401 if (!inbuf && !can_generate_output (self)) {
2402 return GST_FLOW_OK;
2403 }
2404
2405 if (gst_base_transform_is_passthrough (base)) {
2406 *outbuf = inbuf;
2407 ret = GST_FLOW_OK;
2408 } else {
2409 if (inbuf && GST_BUFFER_IS_DISCONT (inbuf)) {
2410 ret = drain_input (self);
2411 reset_counters (self);
2412 if (ret != GST_FLOW_OK)
2413 return ret;
2414 }
2415
2416 *outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
2417 if (*outbuf == NULL)
2418 goto no_buffer;
2419
2420 if (inbuf)
2421 gst_buffer_replace (&self->previous_buffer, inbuf);
2422
2423 if (bclass->copy_metadata) {
2424 if (!bclass->copy_metadata (base, self->previous_buffer, *outbuf)) {
2425 /* something failed, post a warning */
2426 GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
2427 ("could not copy metadata"), (NULL));
2428 }
2429 }
2430
2431 ret = gst_cc_converter_transform (self, inbuf, *outbuf);
2432 if (gst_buffer_get_size (*outbuf) <= 0) {
2433 gst_buffer_unref (*outbuf);
2434 *outbuf = NULL;
2435 ret = GST_FLOW_OK;
2436 }
2437
2438 if (inbuf)
2439 gst_buffer_unref (inbuf);
2440 }
2441
2442 return ret;
2443
2444 no_buffer:
2445 {
2446 if (inbuf)
2447 gst_buffer_unref (inbuf);
2448 *outbuf = NULL;
2449 GST_WARNING_OBJECT (self, "could not allocate buffer");
2450 return GST_FLOW_ERROR;
2451 }
2452 }
2453
2454 static gboolean
gst_cc_converter_sink_event(GstBaseTransform * trans,GstEvent * event)2455 gst_cc_converter_sink_event (GstBaseTransform * trans, GstEvent * event)
2456 {
2457 GstCCConverter *self = GST_CCCONVERTER (trans);
2458
2459 switch (GST_EVENT_TYPE (event)) {
2460 case GST_EVENT_EOS:
2461 GST_DEBUG_OBJECT (self, "received EOS");
2462
2463 drain_input (self);
2464
2465 /* fallthrough */
2466 case GST_EVENT_FLUSH_START:
2467 reset_counters (self);
2468 break;
2469 default:
2470 break;
2471 }
2472
2473 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2474 }
2475
2476 static gboolean
gst_cc_converter_start(GstBaseTransform * base)2477 gst_cc_converter_start (GstBaseTransform * base)
2478 {
2479 GstCCConverter *self = GST_CCCONVERTER (base);
2480
2481 /* Resetting this is not really needed but makes debugging easier */
2482 self->cdp_hdr_sequence_cntr = 0;
2483 self->current_output_timecode = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
2484 self->input_frames = 0;
2485 self->output_frames = 1;
2486 self->scratch_ccp_len = 0;
2487 self->scratch_cea608_1_len = 0;
2488 self->scratch_cea608_2_len = 0;
2489
2490 return TRUE;
2491 }
2492
2493 static gboolean
gst_cc_converter_stop(GstBaseTransform * base)2494 gst_cc_converter_stop (GstBaseTransform * base)
2495 {
2496 GstCCConverter *self = GST_CCCONVERTER (base);
2497
2498 gst_video_time_code_clear (&self->current_output_timecode);
2499 gst_clear_buffer (&self->previous_buffer);
2500
2501 return TRUE;
2502 }
2503
2504 static void
gst_cc_converter_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2505 gst_cc_converter_set_property (GObject * object, guint prop_id,
2506 const GValue * value, GParamSpec * pspec)
2507 {
2508 GstCCConverter *filter = GST_CCCONVERTER (object);
2509
2510 switch (prop_id) {
2511 case PROP_CDP_MODE:
2512 filter->cdp_mode = g_value_get_flags (value);
2513 break;
2514 default:
2515 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2516 break;
2517 }
2518 }
2519
2520 static void
gst_cc_converter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2521 gst_cc_converter_get_property (GObject * object, guint prop_id, GValue * value,
2522 GParamSpec * pspec)
2523 {
2524 GstCCConverter *filter = GST_CCCONVERTER (object);
2525
2526 switch (prop_id) {
2527 case PROP_CDP_MODE:
2528 g_value_set_flags (value, filter->cdp_mode);
2529 break;
2530 default:
2531 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2532 break;
2533 }
2534 }
2535
2536 static void
gst_cc_converter_class_init(GstCCConverterClass * klass)2537 gst_cc_converter_class_init (GstCCConverterClass * klass)
2538 {
2539 GObjectClass *gobject_class;
2540 GstElementClass *gstelement_class;
2541 GstBaseTransformClass *basetransform_class;
2542
2543 gobject_class = (GObjectClass *) klass;
2544 gstelement_class = (GstElementClass *) klass;
2545 basetransform_class = (GstBaseTransformClass *) klass;
2546
2547 gobject_class->set_property = gst_cc_converter_set_property;
2548 gobject_class->get_property = gst_cc_converter_get_property;
2549
2550 /**
2551 * GstCCConverter:cdp-mode
2552 *
2553 * Only insert the selection sections into CEA 708 CDP packets.
2554 *
2555 * Various software does not handle any other information than CC data
2556 * contained in CDP packets and might fail parsing the packets otherwise.
2557 *
2558 * Since: 1.20
2559 */
2560 g_object_class_install_property (G_OBJECT_CLASS (klass),
2561 PROP_CDP_MODE, g_param_spec_flags ("cdp-mode",
2562 "CDP Mode",
2563 "Select which CDP sections to store in CDP packets",
2564 GST_TYPE_CC_CONVERTER_CDP_MODE, DEFAULT_CDP_MODE,
2565 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2566
2567 gst_element_class_set_static_metadata (gstelement_class,
2568 "Closed Caption Converter",
2569 "Filter/ClosedCaption",
2570 "Converts Closed Captions between different formats",
2571 "Sebastian Dröge <sebastian@centricular.com>");
2572
2573 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
2574 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
2575
2576 basetransform_class->start = GST_DEBUG_FUNCPTR (gst_cc_converter_start);
2577 basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_cc_converter_stop);
2578 basetransform_class->sink_event =
2579 GST_DEBUG_FUNCPTR (gst_cc_converter_sink_event);
2580 basetransform_class->transform_size =
2581 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_size);
2582 basetransform_class->transform_caps =
2583 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_caps);
2584 basetransform_class->fixate_caps =
2585 GST_DEBUG_FUNCPTR (gst_cc_converter_fixate_caps);
2586 basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_cc_converter_set_caps);
2587 basetransform_class->transform_meta =
2588 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_meta);
2589 basetransform_class->generate_output =
2590 GST_DEBUG_FUNCPTR (gst_cc_converter_generate_output);
2591 basetransform_class->passthrough_on_same_caps = TRUE;
2592
2593 GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
2594 0, "Closed Caption converter");
2595
2596 gst_type_mark_as_plugin_api (GST_TYPE_CC_CONVERTER_CDP_MODE, 0);
2597 }
2598
2599 static void
gst_cc_converter_init(GstCCConverter * self)2600 gst_cc_converter_init (GstCCConverter * self)
2601 {
2602 self->cdp_mode = DEFAULT_CDP_MODE;
2603 }
2604