hlsdemux: Enable support for external subtitles
[platform/upstream/gstreamer.git] / ext / closedcaption / gstccconverter.c
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1345 gst_cc_converter_init (GstCCConverter * self)
1346 {
1347 }