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 /* Ordered by the amount of information they can contain */
37 #define CC_CAPS \
38 "closedcaption/x-cea-708,format=(string) cdp; " \
39 "closedcaption/x-cea-708,format=(string) cc_data; " \
40 "closedcaption/x-cea-608,format=(string) s334-1a; " \
41 "closedcaption/x-cea-608,format=(string) raw"
42
43 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
44 GST_PAD_SINK,
45 GST_PAD_ALWAYS,
46 GST_STATIC_CAPS (CC_CAPS));
47
48 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
49 GST_PAD_SRC,
50 GST_PAD_ALWAYS,
51 GST_STATIC_CAPS (CC_CAPS));
52
53 G_DEFINE_TYPE (GstCCConverter, gst_cc_converter, GST_TYPE_BASE_TRANSFORM);
54 #define parent_class gst_cc_converter_parent_class
55
56 static gboolean
gst_cc_converter_transform_size(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)57 gst_cc_converter_transform_size (GstBaseTransform * base,
58 GstPadDirection direction,
59 GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
60 {
61 /* We can't really convert from an output size to an input size */
62 if (direction != GST_PAD_SINK)
63 return FALSE;
64
65 /* Assume worst-case here and over-allocate, and in ::transform() we then
66 * downsize the buffer as needed. The worst-case is one CDP packet, which
67 * can be up to 256 bytes large */
68
69 *othersize = 256;
70
71 return TRUE;
72 }
73
74 static GstCaps *
gst_cc_converter_transform_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * caps,GstCaps * filter)75 gst_cc_converter_transform_caps (GstBaseTransform * base,
76 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
77 {
78 static GstStaticCaps non_cdp_caps =
79 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cc_data; "
80 "closedcaption/x-cea-608,format=(string) s334-1a; "
81 "closedcaption/x-cea-608,format=(string) raw");
82 static GstStaticCaps cdp_caps =
83 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp");
84 static GstStaticCaps cdp_caps_framerate =
85 GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp, "
86 "framerate=(fraction){60/1, 60000/1001, 50/1, 30/1, 30000/1001, 25/1, 24/1, 24000/1001}");
87
88 GstCCConverter *self = GST_CCCONVERTER (base);
89 guint i, n;
90 GstCaps *res, *templ;
91
92 templ = gst_pad_get_pad_template_caps (base->srcpad);
93
94 res = gst_caps_new_empty ();
95 n = gst_caps_get_size (caps);
96 for (i = 0; i < n; i++) {
97 const GstStructure *s = gst_caps_get_structure (caps, i);
98
99 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
100 const GValue *framerate;
101
102 framerate = gst_structure_get_value (s, "framerate");
103
104 if (direction == GST_PAD_SRC) {
105 /* SRC direction: We produce upstream caps
106 *
107 * Downstream wanted CEA608 caps. If it had a framerate, we
108 * also need upstream to provide exactly that same framerate
109 * and otherwise we don't care.
110 *
111 * We can convert everything to CEA608.
112 */
113 if (framerate) {
114 GstCaps *tmp;
115
116 tmp =
117 gst_caps_merge (gst_static_caps_get (&cdp_caps),
118 gst_static_caps_get (&non_cdp_caps));
119 tmp = gst_caps_make_writable (tmp);
120 gst_caps_set_value (tmp, "framerate", framerate);
121 res = gst_caps_merge (res, tmp);
122 } else {
123 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps));
124 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
125 }
126 } else {
127 /* SINK: We produce downstream caps
128 *
129 * Upstream provided CEA608 caps. We can convert that to CDP if
130 * also a CDP compatible framerate was provided, and we can convert
131 * it to anything else regardless.
132 *
133 * If upstream provided a framerate we can pass that through, possibly
134 * filtered for the CDP case.
135 */
136 if (framerate) {
137 GstCaps *tmp;
138 GstStructure *t, *u;
139
140 /* Create caps that contain the intersection of all framerates with
141 * the CDP allowed framerates */
142 tmp =
143 gst_caps_make_writable (gst_static_caps_get
144 (&cdp_caps_framerate));
145 t = gst_caps_get_structure (tmp, 0);
146 gst_structure_set_name (t, "closedcaption/x-cea-608");
147 gst_structure_remove_field (t, "format");
148 u = gst_structure_intersect (s, t);
149 gst_caps_unref (tmp);
150
151 if (u) {
152 const GValue *cdp_framerate;
153
154 /* There's an intersection between the framerates so we can convert
155 * into CDP with exactly those framerates */
156 cdp_framerate = gst_structure_get_value (u, "framerate");
157 tmp = gst_caps_make_writable (gst_static_caps_get (&cdp_caps));
158 gst_caps_set_value (tmp, "framerate", cdp_framerate);
159 gst_structure_free (u);
160
161 res = gst_caps_merge (res, tmp);
162 }
163
164 /* And we can convert to everything else with the given framerate */
165 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
166 gst_caps_set_value (tmp, "framerate", framerate);
167 res = gst_caps_merge (res, tmp);
168 } else {
169 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
170 }
171 }
172 } else if (gst_structure_has_name (s, "closedcaption/x-cea-708")) {
173 const GValue *framerate;
174
175 framerate = gst_structure_get_value (s, "framerate");
176
177 if (direction == GST_PAD_SRC) {
178 /* SRC direction: We produce upstream caps
179 *
180 * Downstream wanted CEA708 caps. If downstream wants *only* CDP we
181 * either need CDP from upstream, or anything else with a CDP
182 * framerate.
183 * If downstream also wants non-CDP we can accept anything.
184 *
185 * We pass through any framerate as-is, except for filtering
186 * for CDP framerates if downstream wants only CDP.
187 */
188
189 if (g_strcmp0 (gst_structure_get_string (s, "format"), "cdp") == 0) {
190 /* Downstream wants only CDP */
191
192 /* We need CDP from upstream in that case */
193 if (framerate) {
194 GstCaps *tmp;
195
196 tmp = gst_caps_make_writable (gst_static_caps_get (&cdp_caps));
197 gst_caps_set_value (tmp, "framerate", framerate);
198 res = gst_caps_merge (res, tmp);
199 } else {
200 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps));
201 }
202
203 /* Or anything else with a CDP framerate */
204 if (framerate) {
205 GstCaps *tmp;
206 GstStructure *t, *u;
207
208 /* Create caps that contain the intersection of all framerates with
209 * the CDP allowed framerates */
210 tmp =
211 gst_caps_make_writable (gst_static_caps_get
212 (&cdp_caps_framerate));
213 t = gst_caps_get_structure (tmp, 0);
214 gst_structure_set_name (t, "closedcaption/x-cea-708");
215 gst_structure_remove_field (t, "format");
216 u = gst_structure_intersect (s, t);
217 gst_caps_unref (tmp);
218
219 if (u) {
220 const GValue *cdp_framerate;
221
222 /* There's an intersection between the framerates so we can convert
223 * into CDP with exactly those framerates from anything else */
224 cdp_framerate = gst_structure_get_value (u, "framerate");
225
226 tmp =
227 gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
228 gst_caps_set_value (tmp, "framerate", cdp_framerate);
229 res = gst_caps_merge (res, tmp);
230 }
231 } else {
232 GstCaps *tmp, *cdp_caps;
233 const GValue *cdp_framerate;
234
235 /* Get all CDP framerates, we can accept anything that has those
236 * framerates */
237 cdp_caps = gst_static_caps_get (&cdp_caps_framerate);
238 cdp_framerate =
239 gst_structure_get_value (gst_caps_get_structure (cdp_caps, 0),
240 "framerate");
241
242 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
243 gst_caps_set_value (tmp, "framerate", cdp_framerate);
244 gst_caps_unref (cdp_caps);
245
246 res = gst_caps_merge (res, tmp);
247 }
248 } else {
249 /* Downstream wants not only CDP, we can do everything */
250
251 if (framerate) {
252 GstCaps *tmp;
253
254 tmp =
255 gst_caps_merge (gst_static_caps_get (&cdp_caps),
256 gst_static_caps_get (&non_cdp_caps));
257 tmp = gst_caps_make_writable (tmp);
258 gst_caps_set_value (tmp, "framerate", framerate);
259 res = gst_caps_merge (res, tmp);
260 } else {
261 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps));
262 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
263 }
264 }
265 } else {
266 GstCaps *tmp;
267
268 /* SINK: We produce downstream caps
269 *
270 * Upstream provided CEA708 caps. If upstream provided CDP we can
271 * output CDP, no matter what (-> passthrough). If upstream did not
272 * provide CDP, we can output CDP only if the framerate fits.
273 * We can always produce everything else apart from CDP.
274 *
275 * If upstream provided a framerate we pass that through for non-CDP
276 * output, and pass it through filtered for CDP output.
277 */
278
279 if (gst_structure_can_intersect (s,
280 gst_caps_get_structure (gst_static_caps_get (&cdp_caps), 0))) {
281 /* Upstream provided CDP caps, we can do everything independent of
282 * framerate */
283 if (framerate) {
284 tmp = gst_caps_make_writable (gst_static_caps_get (&cdp_caps));
285 gst_caps_set_value (tmp, "framerate", framerate);
286 res = gst_caps_merge (res, tmp);
287 } else {
288 res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps));
289 }
290 } else if (framerate) {
291 GstStructure *t, *u;
292
293 /* Upstream did not provide CDP. We can only do CDP if upstream
294 * happened to have a CDP framerate */
295
296 /* Create caps that contain the intersection of all framerates with
297 * the CDP allowed framerates */
298 tmp =
299 gst_caps_make_writable (gst_static_caps_get
300 (&cdp_caps_framerate));
301 t = gst_caps_get_structure (tmp, 0);
302 gst_structure_set_name (t, "closedcaption/x-cea-708");
303 gst_structure_remove_field (t, "format");
304 u = gst_structure_intersect (s, t);
305 gst_caps_unref (tmp);
306
307 if (u) {
308 const GValue *cdp_framerate;
309
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 (u, "framerate");
313 tmp = gst_caps_make_writable (gst_static_caps_get (&cdp_caps));
314 gst_caps_set_value (tmp, "framerate", cdp_framerate);
315 gst_structure_free (u);
316
317 res = gst_caps_merge (res, tmp);
318 }
319 }
320
321 /* We can always convert CEA708 to all non-CDP formats */
322 if (framerate) {
323 tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
324 gst_caps_set_value (tmp, "framerate", framerate);
325 res = gst_caps_merge (res, tmp);
326 } else {
327 res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
328 }
329 }
330 } else {
331 g_assert_not_reached ();
332 }
333 }
334
335 /* We can convert anything into anything but it might involve loss of
336 * information so always filter according to the order in our template caps
337 * in the end */
338 if (filter) {
339 GstCaps *tmp;
340 filter = gst_caps_intersect_full (templ, filter, GST_CAPS_INTERSECT_FIRST);
341
342 tmp = gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
343 gst_caps_unref (res);
344 gst_caps_unref (filter);
345 res = tmp;
346 }
347
348 gst_caps_unref (templ);
349
350 GST_DEBUG_OBJECT (self,
351 "Transformed in direction %s caps %" GST_PTR_FORMAT " to %"
352 GST_PTR_FORMAT, direction == GST_PAD_SRC ? "src" : "sink", caps, res);
353
354 return res;
355 }
356
357 static GstCaps *
gst_cc_converter_fixate_caps(GstBaseTransform * base,GstPadDirection direction,GstCaps * incaps,GstCaps * outcaps)358 gst_cc_converter_fixate_caps (GstBaseTransform * base,
359 GstPadDirection direction, GstCaps * incaps, GstCaps * outcaps)
360 {
361 GstCCConverter *self = GST_CCCONVERTER (base);
362 const GstStructure *s;
363 GstStructure *t;
364 const GValue *framerate;
365 GstCaps *intersection, *templ;
366
367 /* Prefer passthrough if we can */
368 if (gst_caps_is_subset (incaps, outcaps)) {
369 gst_caps_unref (outcaps);
370 return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base,
371 direction, incaps, gst_caps_ref (incaps));
372 }
373
374 /* Otherwise prefer caps in the order of our template caps */
375 templ = gst_pad_get_pad_template_caps (base->srcpad);
376 intersection =
377 gst_caps_intersect_full (templ, outcaps, GST_CAPS_INTERSECT_FIRST);
378 gst_caps_unref (outcaps);
379 outcaps = intersection;
380
381 outcaps =
382 GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base, direction,
383 incaps, outcaps);
384
385 if (direction == GST_PAD_SRC)
386 return outcaps;
387
388 /* if we generate caps for the source pad, pass through any framerate
389 * upstream might've given us and remove any framerate that might've
390 * been added by basetransform due to intersecting with downstream */
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);
395 if (framerate) {
396 gst_structure_set_value (t, "framerate", framerate);
397 } else {
398 gst_structure_remove_field (t, "framerate");
399 }
400
401 GST_DEBUG_OBJECT (self,
402 "Fixated caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, incaps, outcaps);
403
404 return outcaps;
405 }
406
407 static gboolean
gst_cc_converter_set_caps(GstBaseTransform * base,GstCaps * incaps,GstCaps * outcaps)408 gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps,
409 GstCaps * outcaps)
410 {
411 GstCCConverter *self = GST_CCCONVERTER (base);
412 const GstStructure *s;
413 gboolean passthrough;
414
415 self->input_caption_type = gst_video_caption_type_from_caps (incaps);
416 self->output_caption_type = gst_video_caption_type_from_caps (outcaps);
417
418 if (self->input_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN ||
419 self->output_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN)
420 goto invalid_caps;
421
422 s = gst_caps_get_structure (incaps, 0);
423 if (!gst_structure_get_fraction (s, "framerate", &self->fps_n, &self->fps_d))
424 self->fps_n = self->fps_d = 0;
425
426 /* Caps can be different but we can passthrough as long as they can
427 * intersect, i.e. have same caps name and format */
428 passthrough = gst_caps_can_intersect (incaps, outcaps);
429 gst_base_transform_set_passthrough (base, passthrough);
430
431 GST_DEBUG_OBJECT (self,
432 "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)",
433 incaps, outcaps, passthrough);
434
435 return TRUE;
436
437 invalid_caps:
438 {
439 GST_ERROR_OBJECT (self,
440 "Invalid caps: in %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, incaps,
441 outcaps);
442 return FALSE;
443 }
444 }
445
446 /* Converts raw CEA708 cc_data and an optional timecode into CDP */
447 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 GstVideoTimeCodeMeta * tc_meta)448 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
449 const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
450 const GstVideoTimeCodeMeta * tc_meta)
451 {
452 GstByteWriter bw;
453 guint8 flags, checksum;
454 guint i, len;
455 guint cc_count;
456
457 gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
458 gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
459 /* Write a length of 0 for now */
460 gst_byte_writer_put_uint8_unchecked (&bw, 0);
461 if (self->fps_n == 24000 && self->fps_d == 1001) {
462 gst_byte_writer_put_uint8_unchecked (&bw, 0x1f);
463 cc_count = 25;
464 } else if (self->fps_n == 24 && self->fps_d == 1) {
465 gst_byte_writer_put_uint8_unchecked (&bw, 0x2f);
466 cc_count = 25;
467 } else if (self->fps_n == 25 && self->fps_d == 1) {
468 gst_byte_writer_put_uint8_unchecked (&bw, 0x3f);
469 cc_count = 24;
470 } else if (self->fps_n == 30000 && self->fps_d == 1001) {
471 gst_byte_writer_put_uint8_unchecked (&bw, 0x4f);
472 cc_count = 20;
473 } else if (self->fps_n == 30 && self->fps_d == 1) {
474 gst_byte_writer_put_uint8_unchecked (&bw, 0x5f);
475 cc_count = 20;
476 } else if (self->fps_n == 50 && self->fps_d == 1) {
477 gst_byte_writer_put_uint8_unchecked (&bw, 0x6f);
478 cc_count = 12;
479 } else if (self->fps_n == 60000 && self->fps_d == 1001) {
480 gst_byte_writer_put_uint8_unchecked (&bw, 0x7f);
481 cc_count = 10;
482 } else if (self->fps_n == 60 && self->fps_d == 1) {
483 gst_byte_writer_put_uint8_unchecked (&bw, 0x8f);
484 cc_count = 10;
485 } else {
486 g_assert_not_reached ();
487 }
488
489 if (cc_data_len / 3 > cc_count) {
490 GST_ERROR_OBJECT (self, "Too many cc_data triplet for framerate: %u > %u",
491 cc_data_len / 3, cc_count);
492 return -1;
493 }
494
495 /* ccdata_present | caption_service_active */
496 flags = 0x42;
497
498 /* time_code_present */
499 if (tc_meta)
500 flags |= 0x80;
501
502 /* reserved */
503 flags |= 0x01;
504
505 gst_byte_writer_put_uint8_unchecked (&bw, flags);
506
507 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
508
509 if (tc_meta) {
510 const GstVideoTimeCode *tc = &tc_meta->tc;
511
512 gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
513 gst_byte_writer_put_uint8_unchecked (&bw, 0xc0 |
514 (((tc->hours % 10) & 0x3) << 4) |
515 ((tc->hours - (tc->hours % 10)) & 0xf));
516
517 gst_byte_writer_put_uint8_unchecked (&bw, 0x80 |
518 (((tc->minutes % 10) & 0x7) << 4) |
519 ((tc->minutes - (tc->minutes % 10)) & 0xf));
520
521 gst_byte_writer_put_uint8_unchecked (&bw,
522 (tc->field_count <
523 2 ? 0x00 : 0x80) | (((tc->seconds %
524 10) & 0x7) << 4) | ((tc->seconds -
525 (tc->seconds % 10)) & 0xf));
526
527 gst_byte_writer_put_uint8_unchecked (&bw,
528 ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
529 0x00) | (((tc->frames % 10) & 0x3) << 4) | ((tc->frames -
530 (tc->frames % 10)) & 0xf));
531 }
532
533 gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
534 gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | cc_count);
535 gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
536 if (cc_count > cc_data_len / 3) {
537 gst_byte_writer_fill (&bw, 0, 3 * cc_count - cc_data_len);
538 }
539
540 gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
541 gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
542 self->cdp_hdr_sequence_cntr++;
543 /* We calculate the checksum afterwards */
544 gst_byte_writer_put_uint8_unchecked (&bw, 0);
545
546 len = gst_byte_writer_get_pos (&bw);
547 gst_byte_writer_set_pos (&bw, 2);
548 gst_byte_writer_put_uint8_unchecked (&bw, len);
549
550 checksum = 0;
551 for (i = 0; i < len; i++) {
552 checksum += cdp[i];
553 }
554 checksum &= 0xff;
555 checksum = 256 - checksum;
556 cdp[len - 1] = checksum;
557
558 return len;
559 }
560
561 /* Converts CDP into raw CEA708 cc_data */
562 static guint
convert_cea708_cdp_cea708_cc_data_internal(GstCCConverter * self,const guint8 * cdp,guint cdp_len,guint8 cc_data[256],GstVideoTimeCode * tc)563 convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
564 const guint8 * cdp, guint cdp_len, guint8 cc_data[256],
565 GstVideoTimeCode * tc)
566 {
567 GstByteReader br;
568 guint16 u16;
569 guint8 u8;
570 guint8 flags;
571 gint fps_n, fps_d;
572 guint len = 0;
573
574 memset (tc, 0, sizeof (*tc));
575
576 /* Header + footer length */
577 if (cdp_len < 11)
578 return 0;
579
580 gst_byte_reader_init (&br, cdp, cdp_len);
581 u16 = gst_byte_reader_get_uint16_be_unchecked (&br);
582 if (u16 != 0x9669)
583 return 0;
584
585 u8 = gst_byte_reader_get_uint8_unchecked (&br);
586 if (u8 != cdp_len)
587 return 0;
588
589 u8 = gst_byte_reader_get_uint8_unchecked (&br);
590 switch (u8) {
591 case 0x1f:
592 fps_n = 24000;
593 fps_d = 1001;
594 break;
595 case 0x2f:
596 fps_n = 24;
597 fps_d = 1;
598 break;
599 case 0x3f:
600 fps_n = 25;
601 fps_d = 1;
602 break;
603 case 0x4f:
604 fps_n = 30000;
605 fps_d = 1001;
606 break;
607 case 0x5f:
608 fps_n = 30;
609 fps_d = 1;
610 break;
611 case 0x6f:
612 fps_n = 50;
613 fps_d = 1;
614 break;
615 case 0x7f:
616 fps_n = 60000;
617 fps_d = 1001;
618 break;
619 case 0x8f:
620 fps_n = 60;
621 fps_d = 1;
622 break;
623 default:
624 return 0;
625 }
626
627 flags = gst_byte_reader_get_uint8_unchecked (&br);
628 /* No cc_data? */
629 if ((flags & 0x40) == 0)
630 return 0;
631
632 /* cdp_hdr_sequence_cntr */
633 gst_byte_reader_skip_unchecked (&br, 2);
634
635 /* time_code_present */
636 if (flags & 0x80) {
637 guint8 hours, minutes, seconds, frames, fields;
638 gboolean drop_frame;
639
640 if (gst_byte_reader_get_remaining (&br) < 5)
641 return 0;
642 if (gst_byte_reader_get_uint8_unchecked (&br) != 0x71)
643 return 0;
644
645 u8 = gst_byte_reader_get_uint8_unchecked (&br);
646 if ((u8 & 0xc) != 0xc)
647 return 0;
648
649 hours = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
650
651 u8 = gst_byte_reader_get_uint8_unchecked (&br);
652 if ((u8 & 0x80) != 0x80)
653 return 0;
654 minutes = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
655
656 u8 = gst_byte_reader_get_uint8_unchecked (&br);
657 if (u8 & 0x80)
658 fields = 2;
659 else
660 fields = 1;
661 seconds = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
662
663 u8 = gst_byte_reader_get_uint8_unchecked (&br);
664 if (u8 & 0x40)
665 return 0;
666
667 drop_frame = ! !(u8 & 0x80);
668 frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
669
670 gst_video_time_code_init (tc, fps_n, fps_d, NULL,
671 drop_frame ? GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME :
672 GST_VIDEO_TIME_CODE_FLAGS_NONE, hours, minutes, seconds, frames,
673 fields);
674 }
675
676 /* ccdata_present */
677 if (flags & 0x40) {
678 guint8 cc_count;
679
680 if (gst_byte_reader_get_remaining (&br) < 2)
681 return 0;
682 if (gst_byte_reader_get_uint8_unchecked (&br) != 0x72)
683 return 0;
684
685 cc_count = gst_byte_reader_get_uint8_unchecked (&br);
686 if ((cc_count & 0xe0) != 0xe0)
687 return 0;
688 cc_count &= 0x1f;
689
690 len = 3 * cc_count;
691 if (gst_byte_reader_get_remaining (&br) < len)
692 return 0;
693
694 memcpy (cc_data, gst_byte_reader_get_data_unchecked (&br, len), len);
695 }
696
697 /* skip everything else we don't care about */
698 return len;
699 }
700
701
702 static GstFlowReturn
convert_cea608_raw_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)703 convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
704 GstBuffer * outbuf)
705 {
706 GstMapInfo in, out;
707 guint i, n;
708
709 n = gst_buffer_get_size (inbuf);
710 if (n & 1) {
711 GST_ERROR_OBJECT (self, "Invalid raw CEA608 buffer size");
712 return GST_FLOW_ERROR;
713 }
714
715 n /= 2;
716
717 if (n > 3) {
718 GST_ERROR_OBJECT (self, "Too many CEA608 pairs %u", n);
719 return GST_FLOW_ERROR;
720 }
721
722 gst_buffer_set_size (outbuf, 3 * n);
723
724 gst_buffer_map (inbuf, &in, GST_MAP_READ);
725 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
726
727 /* We have to assume that each value is from the first field and
728 * don't know from which line offset it originally is */
729 for (i = 0; i < n; i++) {
730 out.data[i * 3] = 0x80;
731 out.data[i * 3 + 1] = in.data[i * 2];
732 out.data[i * 3 + 2] = in.data[i * 2 + 1];
733 }
734
735 gst_buffer_unmap (inbuf, &in);
736 gst_buffer_unmap (outbuf, &out);
737
738 return GST_FLOW_OK;
739 }
740
741 static GstFlowReturn
convert_cea608_raw_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)742 convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
743 GstBuffer * outbuf)
744 {
745 GstMapInfo in, out;
746 guint i, n;
747
748 n = gst_buffer_get_size (inbuf);
749 if (n & 1) {
750 GST_ERROR_OBJECT (self, "Invalid raw CEA608 buffer size");
751 return GST_FLOW_ERROR;
752 }
753
754 n /= 2;
755
756 if (n > 3) {
757 GST_ERROR_OBJECT (self, "Too many CEA608 pairs %u", n);
758 return GST_FLOW_ERROR;
759 }
760
761 gst_buffer_set_size (outbuf, 3 * n);
762
763 gst_buffer_map (inbuf, &in, GST_MAP_READ);
764 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
765
766 /* We have to assume that each value is from the first field and
767 * don't know from which line offset it originally is */
768 for (i = 0; i < n; i++) {
769 out.data[i * 3] = 0xfc;
770 out.data[i * 3 + 1] = in.data[i * 2];
771 out.data[i * 3 + 2] = in.data[i * 2 + 1];
772 }
773
774 gst_buffer_unmap (inbuf, &in);
775 gst_buffer_unmap (outbuf, &out);
776
777 return GST_FLOW_OK;
778 }
779
780 static GstFlowReturn
convert_cea608_raw_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)781 convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
782 GstBuffer * outbuf)
783 {
784 GstMapInfo in, out;
785 guint i, n, len;
786 guint8 cc_data[256];
787
788 n = gst_buffer_get_size (inbuf);
789 if (n & 1) {
790 GST_ERROR_OBJECT (self, "Invalid raw CEA608 buffer size");
791 return GST_FLOW_ERROR;
792 }
793
794 n /= 2;
795
796 if (n > 3) {
797 GST_ERROR_OBJECT (self, "Too many CEA608 pairs %u", n);
798 return GST_FLOW_ERROR;
799 }
800
801 gst_buffer_map (inbuf, &in, GST_MAP_READ);
802 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
803
804 for (i = 0; i < n; i++) {
805 cc_data[i * 3] = 0xfc;
806 cc_data[i * 3 + 1] = in.data[i * 2];
807 cc_data[i * 3 + 2] = in.data[i * 2 + 1];
808 }
809
810 len =
811 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, n * 3,
812 out.data, out.size, gst_buffer_get_video_time_code_meta (inbuf));
813
814 gst_buffer_unmap (inbuf, &in);
815 gst_buffer_unmap (outbuf, &out);
816
817 if (len == -1)
818 return GST_FLOW_ERROR;
819
820 gst_buffer_set_size (outbuf, len);
821
822 return GST_FLOW_OK;
823 }
824
825 static GstFlowReturn
convert_cea608_s334_1a_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)826 convert_cea608_s334_1a_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
827 GstBuffer * outbuf)
828 {
829 GstMapInfo in, out;
830 guint i, n;
831 guint cea608 = 0;
832
833 n = gst_buffer_get_size (inbuf);
834 if (n % 3 != 0) {
835 GST_ERROR_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
836 return GST_FLOW_ERROR;
837 }
838
839 n /= 3;
840
841 if (n > 3) {
842 GST_ERROR_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
843 return GST_FLOW_ERROR;
844 }
845
846 gst_buffer_map (inbuf, &in, GST_MAP_READ);
847 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
848
849 for (i = 0; i < n; i++) {
850 if (in.data[i * 3] & 0x80) {
851 out.data[i * 2] = in.data[i * 3 + 1];
852 out.data[i * 2 + 1] = in.data[i * 3 + 2];
853 cea608++;
854 }
855 }
856
857 gst_buffer_unmap (inbuf, &in);
858 gst_buffer_unmap (outbuf, &out);
859
860 gst_buffer_set_size (outbuf, 2 * cea608);
861
862 return GST_FLOW_OK;
863 }
864
865 static GstFlowReturn
convert_cea608_s334_1a_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)866 convert_cea608_s334_1a_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
867 GstBuffer * outbuf)
868 {
869 GstMapInfo in, out;
870 guint i, n;
871
872 n = gst_buffer_get_size (inbuf);
873 if (n % 3 != 0) {
874 GST_ERROR_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
875 return GST_FLOW_ERROR;
876 }
877
878 n /= 3;
879
880 if (n > 3) {
881 GST_ERROR_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
882 return GST_FLOW_ERROR;
883 }
884
885 gst_buffer_set_size (outbuf, 3 * n);
886
887 gst_buffer_map (inbuf, &in, GST_MAP_READ);
888 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
889
890 for (i = 0; i < n; i++) {
891 out.data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
892 out.data[i * 3 + 1] = in.data[i * 3 + 1];
893 out.data[i * 3 + 2] = in.data[i * 3 + 2];
894 }
895
896 gst_buffer_unmap (inbuf, &in);
897 gst_buffer_unmap (outbuf, &out);
898
899 return GST_FLOW_OK;
900 }
901
902 static GstFlowReturn
convert_cea608_s334_1a_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)903 convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
904 GstBuffer * outbuf)
905 {
906 GstMapInfo in, out;
907 guint i, n, len;
908 guint8 cc_data[256];
909
910 n = gst_buffer_get_size (inbuf);
911 if (n % 3 != 0) {
912 GST_ERROR_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
913 return GST_FLOW_ERROR;
914 }
915
916 n /= 3;
917
918 if (n > 3) {
919 GST_ERROR_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
920 return GST_FLOW_ERROR;
921 }
922
923 gst_buffer_map (inbuf, &in, GST_MAP_READ);
924 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
925
926 for (i = 0; i < n; i++) {
927 cc_data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
928 cc_data[i * 3 + 1] = in.data[i * 3 + 1];
929 cc_data[i * 3 + 2] = in.data[i * 3 + 2];
930 }
931
932 len =
933 convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, n * 3,
934 out.data, out.size, gst_buffer_get_video_time_code_meta (inbuf));
935
936 gst_buffer_unmap (inbuf, &in);
937 gst_buffer_unmap (outbuf, &out);
938
939 if (len == -1)
940 return GST_FLOW_ERROR;
941
942 gst_buffer_set_size (outbuf, len);
943
944 return GST_FLOW_OK;
945 }
946
947 static GstFlowReturn
convert_cea708_cc_data_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)948 convert_cea708_cc_data_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
949 GstBuffer * outbuf)
950 {
951 GstMapInfo in, out;
952 guint i, n;
953 guint cea608 = 0;
954
955 n = gst_buffer_get_size (inbuf);
956 if (n % 3 != 0) {
957 GST_ERROR_OBJECT (self, "Invalid raw CEA708 buffer size");
958 return GST_FLOW_ERROR;
959 }
960
961 n /= 3;
962
963 if (n > 25) {
964 GST_ERROR_OBJECT (self, "Too many CEA708 triplets %u", n);
965 return GST_FLOW_ERROR;
966 }
967
968 gst_buffer_map (inbuf, &in, GST_MAP_READ);
969 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
970
971 for (i = 0; i < n; i++) {
972 /* We can only really copy the first field here as there can't be any
973 * signalling in raw CEA608 and we must not mix the streams of different
974 * fields
975 */
976 if (in.data[i * 3] == 0xfc) {
977 out.data[cea608 * 2] = in.data[i * 3 + 1];
978 out.data[cea608 * 2 + 1] = in.data[i * 3 + 2];
979 cea608++;
980 }
981 }
982
983 gst_buffer_unmap (inbuf, &in);
984 gst_buffer_unmap (outbuf, &out);
985
986 gst_buffer_set_size (outbuf, 2 * cea608);
987
988 return GST_FLOW_OK;
989 }
990
991 static GstFlowReturn
convert_cea708_cc_data_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)992 convert_cea708_cc_data_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
993 GstBuffer * outbuf)
994 {
995 GstMapInfo in, out;
996 guint i, n;
997 guint cea608 = 0;
998
999 n = gst_buffer_get_size (inbuf);
1000 if (n % 3 != 0) {
1001 GST_ERROR_OBJECT (self, "Invalid raw CEA708 buffer size");
1002 return GST_FLOW_ERROR;
1003 }
1004
1005 n /= 3;
1006
1007 if (n > 25) {
1008 GST_ERROR_OBJECT (self, "Too many CEA708 triplets %u", n);
1009 return GST_FLOW_ERROR;
1010 }
1011
1012 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1013 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1014
1015 for (i = 0; i < n; i++) {
1016 if (in.data[i * 3] == 0xfc || in.data[i * 3] == 0xfd) {
1017 /* We have to assume a line offset of 0 */
1018 out.data[cea608 * 3] = in.data[i * 3] == 0xfc ? 0x80 : 0x00;
1019 out.data[cea608 * 3 + 1] = in.data[i * 3 + 1];
1020 out.data[cea608 * 3 + 2] = in.data[i * 3 + 2];
1021 cea608++;
1022 }
1023 }
1024
1025 gst_buffer_unmap (inbuf, &in);
1026 gst_buffer_unmap (outbuf, &out);
1027
1028 gst_buffer_set_size (outbuf, 3 * cea608);
1029
1030 return GST_FLOW_OK;
1031 }
1032
1033 static GstFlowReturn
convert_cea708_cc_data_cea708_cdp(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1034 convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1035 GstBuffer * outbuf)
1036 {
1037 GstMapInfo in, out;
1038 guint n;
1039 guint len;
1040
1041 n = gst_buffer_get_size (inbuf);
1042 if (n % 3 != 0) {
1043 GST_ERROR_OBJECT (self, "Invalid raw CEA708 buffer size");
1044 return GST_FLOW_ERROR;
1045 }
1046
1047 n /= 3;
1048
1049 if (n > 25) {
1050 GST_ERROR_OBJECT (self, "Too many CEA708 triplets %u", n);
1051 return GST_FLOW_ERROR;
1052 }
1053
1054 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1055 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1056
1057 len =
1058 convert_cea708_cc_data_cea708_cdp_internal (self, in.data, in.size,
1059 out.data, out.size, gst_buffer_get_video_time_code_meta (inbuf));
1060
1061 gst_buffer_unmap (inbuf, &in);
1062 gst_buffer_unmap (outbuf, &out);
1063
1064 if (len == -1)
1065 return GST_FLOW_ERROR;
1066
1067 gst_buffer_set_size (outbuf, len);
1068
1069 return GST_FLOW_OK;
1070 }
1071
1072 static GstFlowReturn
convert_cea708_cdp_cea608_raw(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1073 convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1074 GstBuffer * outbuf)
1075 {
1076 GstMapInfo in, out;
1077 guint i;
1078 GstVideoTimeCode tc;
1079 guint8 cc_data[256];
1080 guint len, cea608 = 0;
1081
1082 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1083 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1084
1085 len =
1086 convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1087 cc_data, &tc);
1088 len /= 3;
1089
1090 if (len > 25) {
1091 GST_ERROR_OBJECT (self, "Too many cc_data triples in CDP packet %u", len);
1092 return GST_FLOW_ERROR;
1093 }
1094
1095 for (i = 0; i < len; i++) {
1096 /* We can only really copy the first field here as there can't be any
1097 * signalling in raw CEA608 and we must not mix the streams of different
1098 * fields
1099 */
1100 if (cc_data[i * 3] == 0xfc) {
1101 out.data[cea608 * 2] = cc_data[i * 3 + 1];
1102 out.data[cea608 * 2 + 1] = cc_data[i * 3 + 2];
1103 cea608++;
1104 }
1105 }
1106
1107 gst_buffer_unmap (inbuf, &in);
1108 gst_buffer_unmap (outbuf, &out);
1109
1110 gst_buffer_set_size (outbuf, 2 * cea608);
1111
1112 if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1113 gst_buffer_add_video_time_code_meta (outbuf, &tc);
1114
1115 return GST_FLOW_OK;
1116 }
1117
1118 static GstFlowReturn
convert_cea708_cdp_cea608_s334_1a(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1119 convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1120 GstBuffer * outbuf)
1121 {
1122 GstMapInfo in, out;
1123 guint i;
1124 GstVideoTimeCode tc;
1125 guint8 cc_data[256];
1126 guint len, cea608 = 0;
1127
1128 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1129 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1130
1131 len =
1132 convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1133 cc_data, &tc);
1134 len /= 3;
1135
1136 if (len > 25) {
1137 GST_ERROR_OBJECT (self, "Too many cc_data triples in CDP packet %u", len);
1138 return GST_FLOW_ERROR;
1139 }
1140
1141 for (i = 0; i < len; i++) {
1142 if (cc_data[i * 3] == 0xfc || cc_data[i * 3] == 0xfd) {
1143 /* We have to assume a line offset of 0 */
1144 out.data[cea608 * 3] = cc_data[i * 3] == 0xfc ? 0x80 : 0x00;
1145 out.data[cea608 * 3 + 1] = cc_data[i * 3 + 1];
1146 out.data[cea608 * 3 + 2] = cc_data[i * 3 + 2];
1147 cea608++;
1148 }
1149 }
1150
1151 gst_buffer_unmap (inbuf, &in);
1152 gst_buffer_unmap (outbuf, &out);
1153
1154 gst_buffer_set_size (outbuf, 3 * cea608);
1155
1156 if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1157 gst_buffer_add_video_time_code_meta (outbuf, &tc);
1158
1159 return GST_FLOW_OK;
1160 }
1161
1162 static GstFlowReturn
convert_cea708_cdp_cea708_cc_data(GstCCConverter * self,GstBuffer * inbuf,GstBuffer * outbuf)1163 convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1164 GstBuffer * outbuf)
1165 {
1166 GstMapInfo in, out;
1167 GstVideoTimeCode tc;
1168 guint len;
1169
1170 gst_buffer_map (inbuf, &in, GST_MAP_READ);
1171 gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1172
1173 len =
1174 convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1175 out.data, &tc);
1176
1177 gst_buffer_unmap (inbuf, &in);
1178 gst_buffer_unmap (outbuf, &out);
1179
1180 if (len / 3 > 25) {
1181 GST_ERROR_OBJECT (self, "Too many cc_data triples in CDP packet %u",
1182 len / 3);
1183 return GST_FLOW_ERROR;
1184 }
1185
1186 gst_buffer_set_size (outbuf, len);
1187
1188 if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1189 gst_buffer_add_video_time_code_meta (outbuf, &tc);
1190
1191 return GST_FLOW_OK;
1192 }
1193
1194 static GstFlowReturn
gst_cc_converter_transform(GstBaseTransform * base,GstBuffer * inbuf,GstBuffer * outbuf)1195 gst_cc_converter_transform (GstBaseTransform * base, GstBuffer * inbuf,
1196 GstBuffer * outbuf)
1197 {
1198 GstCCConverter *self = GST_CCCONVERTER (base);
1199 GstVideoTimeCodeMeta *tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
1200 GstFlowReturn ret = GST_FLOW_OK;
1201
1202 GST_DEBUG_OBJECT (base, "Converting %" GST_PTR_FORMAT " from %u to %u", inbuf,
1203 self->input_caption_type, self->output_caption_type);
1204
1205 switch (self->input_caption_type) {
1206 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1207
1208 switch (self->output_caption_type) {
1209 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1210 ret = convert_cea608_raw_cea608_s334_1a (self, inbuf, outbuf);
1211 break;
1212 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1213 ret = convert_cea608_raw_cea708_cc_data (self, inbuf, outbuf);
1214 break;
1215 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1216 ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf);
1217 break;
1218 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1219 default:
1220 g_assert_not_reached ();
1221 break;
1222 }
1223
1224 break;
1225 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1226
1227 switch (self->output_caption_type) {
1228 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1229 ret = convert_cea608_s334_1a_cea608_raw (self, inbuf, outbuf);
1230 break;
1231 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1232 ret = convert_cea608_s334_1a_cea708_cc_data (self, inbuf, outbuf);
1233 break;
1234 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1235 ret = convert_cea608_s334_1a_cea708_cdp (self, inbuf, outbuf);
1236 break;
1237 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1238 default:
1239 g_assert_not_reached ();
1240 break;
1241 }
1242
1243 break;
1244 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1245
1246 switch (self->output_caption_type) {
1247 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1248 ret = convert_cea708_cc_data_cea608_raw (self, inbuf, outbuf);
1249 break;
1250 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1251 ret = convert_cea708_cc_data_cea608_s334_1a (self, inbuf, outbuf);
1252 break;
1253 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1254 ret = convert_cea708_cc_data_cea708_cdp (self, inbuf, outbuf);
1255 break;
1256 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1257 default:
1258 g_assert_not_reached ();
1259 break;
1260 }
1261
1262 break;
1263 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1264
1265 switch (self->output_caption_type) {
1266 case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1267 ret = convert_cea708_cdp_cea608_raw (self, inbuf, outbuf);
1268 break;
1269 case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1270 ret = convert_cea708_cdp_cea608_s334_1a (self, inbuf, outbuf);
1271 break;
1272 case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1273 ret = convert_cea708_cdp_cea708_cc_data (self, inbuf, outbuf);
1274 break;
1275 case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1276 default:
1277 g_assert_not_reached ();
1278 break;
1279 }
1280
1281 break;
1282 default:
1283 g_assert_not_reached ();
1284 break;
1285 }
1286
1287 if (ret != GST_FLOW_OK)
1288 return ret;
1289
1290 if (tc_meta)
1291 gst_buffer_add_video_time_code_meta (outbuf, &tc_meta->tc);
1292
1293 GST_DEBUG_OBJECT (self, "Converted to %" GST_PTR_FORMAT, outbuf);
1294
1295 return gst_buffer_get_size (outbuf) >
1296 0 ? GST_FLOW_OK : GST_BASE_TRANSFORM_FLOW_DROPPED;
1297 }
1298
1299 static gboolean
gst_cc_converter_start(GstBaseTransform * base)1300 gst_cc_converter_start (GstBaseTransform * base)
1301 {
1302 GstCCConverter *self = GST_CCCONVERTER (base);
1303
1304 /* Resetting this is not really needed but makes debugging easier */
1305 self->cdp_hdr_sequence_cntr = 0;
1306
1307 return TRUE;
1308 }
1309
1310 static void
gst_cc_converter_class_init(GstCCConverterClass * klass)1311 gst_cc_converter_class_init (GstCCConverterClass * klass)
1312 {
1313 GstElementClass *gstelement_class;
1314 GstBaseTransformClass *basetransform_class;
1315
1316 gstelement_class = (GstElementClass *) klass;
1317 basetransform_class = (GstBaseTransformClass *) klass;
1318
1319 gst_element_class_set_static_metadata (gstelement_class,
1320 "Closed Caption Converter",
1321 "Filter/ClosedCaption",
1322 "Converts Closed Captions between different formats",
1323 "Sebastian Dröge <sebastian@centricular.com>");
1324
1325 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
1326 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
1327
1328 basetransform_class->start = GST_DEBUG_FUNCPTR (gst_cc_converter_start);
1329 basetransform_class->transform_size =
1330 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_size);
1331 basetransform_class->transform_caps =
1332 GST_DEBUG_FUNCPTR (gst_cc_converter_transform_caps);
1333 basetransform_class->fixate_caps =
1334 GST_DEBUG_FUNCPTR (gst_cc_converter_fixate_caps);
1335 basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_cc_converter_set_caps);
1336 basetransform_class->transform =
1337 GST_DEBUG_FUNCPTR (gst_cc_converter_transform);
1338 basetransform_class->passthrough_on_same_caps = TRUE;
1339
1340 GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
1341 0, "Closed Caption converter");
1342 }
1343
1344 static void
gst_cc_converter_init(GstCCConverter * self)1345 gst_cc_converter_init (GstCCConverter * self)
1346 {
1347 }
1348