ccconverter: introduce define for max cdp packet length
[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 MAX_CDP_PACKET_LEN bytes large */
68
69   *othersize = MAX_CDP_PACKET_LEN;
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->in_fps_n,
424           &self->in_fps_d))
425     self->in_fps_n = self->in_fps_d = 0;
426
427   s = gst_caps_get_structure (outcaps, 0);
428   if (!gst_structure_get_fraction (s, "framerate", &self->out_fps_n,
429           &self->out_fps_d))
430     self->out_fps_n = self->out_fps_d = 0;
431
432   /* Caps can be different but we can passthrough as long as they can
433    * intersect, i.e. have same caps name and format */
434   passthrough = gst_caps_can_intersect (incaps, outcaps);
435   gst_base_transform_set_passthrough (base, passthrough);
436
437   GST_DEBUG_OBJECT (self,
438       "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)",
439       incaps, outcaps, passthrough);
440
441   return TRUE;
442
443 invalid_caps:
444   {
445     GST_ERROR_OBJECT (self,
446         "Invalid caps: in %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, incaps,
447         outcaps);
448     return FALSE;
449   }
450 }
451
452 struct cdp_fps_entry
453 {
454   guint8 fps_idx;
455   guint fps_n, fps_d;
456   guint max_cc_count;
457 };
458
459 static const struct cdp_fps_entry cdp_fps_table[] = {
460   {0x1f, 24000, 1001, 25},
461   {0x2f, 24, 1, 25},
462   {0x3f, 25, 1, 24},
463   {0x4f, 30000, 1001, 20},
464   {0x5f, 30, 1, 20},
465   {0x6f, 50, 1, 12},
466   {0x7f, 60000, 1001, 10},
467   {0x8f, 60, 1, 10},
468 };
469 static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
470
471 static const struct cdp_fps_entry *
472 cdp_fps_entry_from_id (guint8 id)
473 {
474   int i;
475   for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
476     if (cdp_fps_table[i].fps_idx == id)
477       return &cdp_fps_table[i];
478   }
479   return &null_fps_entry;
480 }
481
482 static const struct cdp_fps_entry *
483 cdp_fps_entry_from_fps (guint fps_n, guint fps_d)
484 {
485   int i;
486   for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
487     if (cdp_fps_table[i].fps_n == fps_n && cdp_fps_table[i].fps_d == fps_d)
488       return &cdp_fps_table[i];
489   }
490   return &null_fps_entry;
491 }
492
493 /* Converts raw CEA708 cc_data and an optional timecode into CDP */
494 static guint
495 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
496     const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
497     const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
498 {
499   GstByteWriter bw;
500   guint8 flags, checksum;
501   guint i, len;
502
503   gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
504   gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
505   /* Write a length of 0 for now */
506   gst_byte_writer_put_uint8_unchecked (&bw, 0);
507
508   gst_byte_writer_put_uint8_unchecked (&bw, fps_entry->fps_idx);
509
510   if (cc_data_len / 3 > fps_entry->max_cc_count) {
511     GST_WARNING_OBJECT (self, "Too many cc_data triplet for framerate: %u > %u",
512         cc_data_len / 3, fps_entry->max_cc_count);
513     cc_data_len = 3 * fps_entry->max_cc_count;
514   }
515
516   /* ccdata_present | caption_service_active */
517   flags = 0x42;
518
519   /* time_code_present */
520   if (tc)
521     flags |= 0x80;
522
523   /* reserved */
524   flags |= 0x01;
525
526   gst_byte_writer_put_uint8_unchecked (&bw, flags);
527
528   gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
529
530   if (tc) {
531     gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
532     gst_byte_writer_put_uint8_unchecked (&bw, 0xc0 |
533         (((tc->hours % 10) & 0x3) << 4) |
534         ((tc->hours - (tc->hours % 10)) & 0xf));
535
536     gst_byte_writer_put_uint8_unchecked (&bw, 0x80 |
537         (((tc->minutes % 10) & 0x7) << 4) |
538         ((tc->minutes - (tc->minutes % 10)) & 0xf));
539
540     gst_byte_writer_put_uint8_unchecked (&bw,
541         (tc->field_count <
542             2 ? 0x00 : 0x80) | (((tc->seconds %
543                     10) & 0x7) << 4) | ((tc->seconds -
544                 (tc->seconds % 10)) & 0xf));
545
546     gst_byte_writer_put_uint8_unchecked (&bw,
547         ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
548             0x00) | (((tc->frames % 10) & 0x3) << 4) | ((tc->frames -
549                 (tc->frames % 10)) & 0xf));
550   }
551
552   gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
553   gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | fps_entry->max_cc_count);
554   gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
555   while (fps_entry->max_cc_count > cc_data_len / 3) {
556     gst_byte_writer_put_uint8_unchecked (&bw, 0xf8);
557     gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
558     gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
559     cc_data_len += 3;
560   }
561
562   gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
563   gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
564   self->cdp_hdr_sequence_cntr++;
565   /* We calculate the checksum afterwards */
566   gst_byte_writer_put_uint8_unchecked (&bw, 0);
567
568   len = gst_byte_writer_get_pos (&bw);
569   gst_byte_writer_set_pos (&bw, 2);
570   gst_byte_writer_put_uint8_unchecked (&bw, len);
571
572   checksum = 0;
573   for (i = 0; i < len; i++) {
574     checksum += cdp[i];
575   }
576   checksum &= 0xff;
577   checksum = 256 - checksum;
578   cdp[len - 1] = checksum;
579
580   return len;
581 }
582
583 /* Converts CDP into raw CEA708 cc_data */
584 static guint
585 convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
586     const guint8 * cdp, guint cdp_len, guint8 cc_data[MAX_CDP_PACKET_LEN],
587     GstVideoTimeCode * tc, const struct cdp_fps_entry ** out_fps_entry)
588 {
589   GstByteReader br;
590   guint16 u16;
591   guint8 u8;
592   guint8 flags;
593   guint len = 0;
594   const struct cdp_fps_entry *fps_entry;
595
596   *out_fps_entry = &null_fps_entry;
597   memset (tc, 0, sizeof (*tc));
598
599   /* Header + footer length */
600   if (cdp_len < 11)
601     return 0;
602
603   gst_byte_reader_init (&br, cdp, cdp_len);
604   u16 = gst_byte_reader_get_uint16_be_unchecked (&br);
605   if (u16 != 0x9669)
606     return 0;
607
608   u8 = gst_byte_reader_get_uint8_unchecked (&br);
609   if (u8 != cdp_len)
610     return 0;
611
612   u8 = gst_byte_reader_get_uint8_unchecked (&br);
613   fps_entry = cdp_fps_entry_from_id (u8);
614   if (!fps_entry || fps_entry->fps_n == 0)
615     return 0;
616
617   flags = gst_byte_reader_get_uint8_unchecked (&br);
618   /* No cc_data? */
619   if ((flags & 0x40) == 0)
620     return 0;
621
622   /* cdp_hdr_sequence_cntr */
623   gst_byte_reader_skip_unchecked (&br, 2);
624
625   /* time_code_present */
626   if (flags & 0x80) {
627     guint8 hours, minutes, seconds, frames, fields;
628     gboolean drop_frame;
629
630     if (gst_byte_reader_get_remaining (&br) < 5)
631       return 0;
632     if (gst_byte_reader_get_uint8_unchecked (&br) != 0x71)
633       return 0;
634
635     u8 = gst_byte_reader_get_uint8_unchecked (&br);
636     if ((u8 & 0xc) != 0xc)
637       return 0;
638
639     hours = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
640
641     u8 = gst_byte_reader_get_uint8_unchecked (&br);
642     if ((u8 & 0x80) != 0x80)
643       return 0;
644     minutes = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
645
646     u8 = gst_byte_reader_get_uint8_unchecked (&br);
647     if (u8 & 0x80)
648       fields = 2;
649     else
650       fields = 1;
651     seconds = ((u8 >> 4) & 0x7) * 10 + (u8 & 0xf);
652
653     u8 = gst_byte_reader_get_uint8_unchecked (&br);
654     if (u8 & 0x40)
655       return 0;
656
657     drop_frame = ! !(u8 & 0x80);
658     frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
659
660     gst_video_time_code_init (tc, fps_entry->fps_n, fps_entry->fps_d, NULL,
661         drop_frame ? GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME :
662         GST_VIDEO_TIME_CODE_FLAGS_NONE, hours, minutes, seconds, frames,
663         fields);
664   }
665
666   /* ccdata_present */
667   if (flags & 0x40) {
668     guint8 cc_count;
669
670     if (gst_byte_reader_get_remaining (&br) < 2)
671       return 0;
672     if (gst_byte_reader_get_uint8_unchecked (&br) != 0x72)
673       return 0;
674
675     cc_count = gst_byte_reader_get_uint8_unchecked (&br);
676     if ((cc_count & 0xe0) != 0xe0)
677       return 0;
678     cc_count &= 0x1f;
679
680     len = 3 * cc_count;
681     if (gst_byte_reader_get_remaining (&br) < len)
682       return 0;
683
684     memcpy (cc_data, gst_byte_reader_get_data_unchecked (&br, len), len);
685   }
686
687   *out_fps_entry = fps_entry;
688
689   /* skip everything else we don't care about */
690   return len;
691 }
692
693
694 static GstFlowReturn
695 convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
696     GstBuffer * outbuf)
697 {
698   GstMapInfo in, out;
699   guint i, n;
700
701   n = gst_buffer_get_size (inbuf);
702   if (n & 1) {
703     GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
704     gst_buffer_set_size (outbuf, 0);
705     return GST_FLOW_OK;
706   }
707
708   n /= 2;
709
710   if (n > 3) {
711     GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u", n);
712     n = 3;
713   }
714
715   gst_buffer_set_size (outbuf, 3 * n);
716
717   gst_buffer_map (inbuf, &in, GST_MAP_READ);
718   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
719
720   /* We have to assume that each value is from the first field and
721    * don't know from which line offset it originally is */
722   for (i = 0; i < n; i++) {
723     out.data[i * 3] = 0x80;
724     out.data[i * 3 + 1] = in.data[i * 2];
725     out.data[i * 3 + 2] = in.data[i * 2 + 1];
726   }
727
728   gst_buffer_unmap (inbuf, &in);
729   gst_buffer_unmap (outbuf, &out);
730
731   return GST_FLOW_OK;
732 }
733
734 static GstFlowReturn
735 convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
736     GstBuffer * outbuf)
737 {
738   GstMapInfo in, out;
739   guint i, n;
740
741   n = gst_buffer_get_size (inbuf);
742   if (n & 1) {
743     GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
744     gst_buffer_set_size (outbuf, 0);
745     return GST_FLOW_OK;
746   }
747
748   n /= 2;
749
750   if (n > 3) {
751     GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u", n);
752     n = 3;
753   }
754
755   gst_buffer_set_size (outbuf, 3 * n);
756
757   gst_buffer_map (inbuf, &in, GST_MAP_READ);
758   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
759
760   /* We have to assume that each value is from the first field and
761    * don't know from which line offset it originally is */
762   for (i = 0; i < n; i++) {
763     out.data[i * 3] = 0xfc;
764     out.data[i * 3 + 1] = in.data[i * 2];
765     out.data[i * 3 + 2] = in.data[i * 2 + 1];
766   }
767
768   gst_buffer_unmap (inbuf, &in);
769   gst_buffer_unmap (outbuf, &out);
770
771   return GST_FLOW_OK;
772 }
773
774 static GstFlowReturn
775 convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
776     GstBuffer * outbuf)
777 {
778   GstMapInfo in, out;
779   guint i, n, len;
780   guint8 cc_data[MAX_CDP_PACKET_LEN];
781   const GstVideoTimeCodeMeta *tc_meta;
782   const struct cdp_fps_entry *fps_entry;
783
784   n = gst_buffer_get_size (inbuf);
785   if (n & 1) {
786     GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
787     gst_buffer_set_size (outbuf, 0);
788     return GST_FLOW_OK;
789   }
790
791   n /= 2;
792
793   if (n > 3) {
794     GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u", n);
795     n = 3;
796   }
797
798   gst_buffer_map (inbuf, &in, GST_MAP_READ);
799   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
800
801   for (i = 0; i < n; i++) {
802     cc_data[i * 3] = 0xfc;
803     cc_data[i * 3 + 1] = in.data[i * 2];
804     cc_data[i * 3 + 2] = in.data[i * 2 + 1];
805   }
806
807   fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
808   if (!fps_entry || fps_entry->fps_n == 0)
809     g_assert_not_reached ();
810
811   tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
812   len =
813       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, n * 3,
814       out.data, out.size, tc_meta ? &tc_meta->tc : NULL, fps_entry);
815
816   gst_buffer_unmap (inbuf, &in);
817   gst_buffer_unmap (outbuf, &out);
818
819   gst_buffer_set_size (outbuf, len);
820
821   return GST_FLOW_OK;
822 }
823
824 static GstFlowReturn
825 convert_cea608_s334_1a_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
826     GstBuffer * outbuf)
827 {
828   GstMapInfo in, out;
829   guint i, n;
830   guint cea608 = 0;
831
832   n = gst_buffer_get_size (inbuf);
833   if (n % 3 != 0) {
834     GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
835     n = n - (n % 3);
836   }
837
838   n /= 3;
839
840   if (n > 3) {
841     GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
842     n = 3;
843   }
844
845   gst_buffer_map (inbuf, &in, GST_MAP_READ);
846   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
847
848   for (i = 0; i < n; i++) {
849     if (in.data[i * 3] & 0x80) {
850       out.data[i * 2] = in.data[i * 3 + 1];
851       out.data[i * 2 + 1] = in.data[i * 3 + 2];
852       cea608++;
853     }
854   }
855
856   gst_buffer_unmap (inbuf, &in);
857   gst_buffer_unmap (outbuf, &out);
858
859   gst_buffer_set_size (outbuf, 2 * cea608);
860
861   return GST_FLOW_OK;
862 }
863
864 static GstFlowReturn
865 convert_cea608_s334_1a_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
866     GstBuffer * outbuf)
867 {
868   GstMapInfo in, out;
869   guint i, n;
870
871   n = gst_buffer_get_size (inbuf);
872   if (n % 3 != 0) {
873     GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
874     n = n - (n % 3);
875   }
876
877   n /= 3;
878
879   if (n > 3) {
880     GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
881     n = 3;
882   }
883
884   gst_buffer_set_size (outbuf, 3 * n);
885
886   gst_buffer_map (inbuf, &in, GST_MAP_READ);
887   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
888
889   for (i = 0; i < n; i++) {
890     out.data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
891     out.data[i * 3 + 1] = in.data[i * 3 + 1];
892     out.data[i * 3 + 2] = in.data[i * 3 + 2];
893   }
894
895   gst_buffer_unmap (inbuf, &in);
896   gst_buffer_unmap (outbuf, &out);
897
898   return GST_FLOW_OK;
899 }
900
901 static GstFlowReturn
902 convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
903     GstBuffer * outbuf)
904 {
905   GstMapInfo in, out;
906   guint i, n, len;
907   guint8 cc_data[MAX_CDP_PACKET_LEN];
908   const GstVideoTimeCodeMeta *tc_meta;
909   const struct cdp_fps_entry *fps_entry;
910
911   n = gst_buffer_get_size (inbuf);
912   if (n % 3 != 0) {
913     GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
914     n = n - (n % 3);
915   }
916
917   n /= 3;
918
919   if (n > 3) {
920     GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
921     n = 3;
922   }
923
924   gst_buffer_map (inbuf, &in, GST_MAP_READ);
925   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
926
927   for (i = 0; i < n; i++) {
928     cc_data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
929     cc_data[i * 3 + 1] = in.data[i * 3 + 1];
930     cc_data[i * 3 + 2] = in.data[i * 3 + 2];
931   }
932
933   fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
934   if (!fps_entry || fps_entry->fps_n == 0)
935     g_assert_not_reached ();
936
937   tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
938   len =
939       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, n * 3,
940       out.data, out.size, tc_meta ? &tc_meta->tc : NULL, fps_entry);
941
942   gst_buffer_unmap (inbuf, &in);
943   gst_buffer_unmap (outbuf, &out);
944
945   gst_buffer_set_size (outbuf, len);
946
947   return GST_FLOW_OK;
948 }
949
950 static GstFlowReturn
951 convert_cea708_cc_data_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
952     GstBuffer * outbuf)
953 {
954   GstMapInfo in, out;
955   guint i, n;
956   guint cea608 = 0;
957
958   n = gst_buffer_get_size (inbuf);
959   if (n % 3 != 0) {
960     GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
961     n = n - (n % 3);
962   }
963
964   n /= 3;
965
966   if (n > 25) {
967     GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
968     n = 25;
969   }
970
971   gst_buffer_map (inbuf, &in, GST_MAP_READ);
972   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
973
974   for (i = 0; i < n; i++) {
975     /* We can only really copy the first field here as there can't be any
976      * signalling in raw CEA608 and we must not mix the streams of different
977      * fields
978      */
979     if (in.data[i * 3] == 0xfc) {
980       out.data[cea608 * 2] = in.data[i * 3 + 1];
981       out.data[cea608 * 2 + 1] = in.data[i * 3 + 2];
982       cea608++;
983     }
984   }
985
986   gst_buffer_unmap (inbuf, &in);
987   gst_buffer_unmap (outbuf, &out);
988
989   gst_buffer_set_size (outbuf, 2 * cea608);
990
991   return GST_FLOW_OK;
992 }
993
994 static GstFlowReturn
995 convert_cea708_cc_data_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
996     GstBuffer * outbuf)
997 {
998   GstMapInfo in, out;
999   guint i, n;
1000   guint cea608 = 0;
1001
1002   n = gst_buffer_get_size (inbuf);
1003   if (n % 3 != 0) {
1004     GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1005     n = n - (n % 3);
1006   }
1007
1008   n /= 3;
1009
1010   if (n > 25) {
1011     GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1012     n = 25;
1013   }
1014
1015   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1016   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1017
1018   for (i = 0; i < n; i++) {
1019     if (in.data[i * 3] == 0xfc || in.data[i * 3] == 0xfd) {
1020       /* We have to assume a line offset of 0 */
1021       out.data[cea608 * 3] = in.data[i * 3] == 0xfc ? 0x80 : 0x00;
1022       out.data[cea608 * 3 + 1] = in.data[i * 3 + 1];
1023       out.data[cea608 * 3 + 2] = in.data[i * 3 + 2];
1024       cea608++;
1025     }
1026   }
1027
1028   gst_buffer_unmap (inbuf, &in);
1029   gst_buffer_unmap (outbuf, &out);
1030
1031   gst_buffer_set_size (outbuf, 3 * cea608);
1032
1033   return GST_FLOW_OK;
1034 }
1035
1036 static GstFlowReturn
1037 convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1038     GstBuffer * outbuf)
1039 {
1040   GstMapInfo in, out;
1041   guint n;
1042   guint len;
1043   const GstVideoTimeCodeMeta *tc_meta;
1044   const struct cdp_fps_entry *fps_entry;
1045
1046   n = gst_buffer_get_size (inbuf);
1047   if (n % 3 != 0) {
1048     GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1049     n = n - (n % 3);
1050   }
1051
1052   n /= 3;
1053
1054   if (n > 25) {
1055     GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1056     n = 25;
1057   }
1058
1059   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1060   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1061
1062   fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1063   if (!fps_entry || fps_entry->fps_n == 0)
1064     g_assert_not_reached ();
1065
1066   tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
1067   len =
1068       convert_cea708_cc_data_cea708_cdp_internal (self, in.data, in.size,
1069       out.data, out.size, tc_meta ? &tc_meta->tc : NULL, fps_entry);
1070
1071   gst_buffer_unmap (inbuf, &in);
1072   gst_buffer_unmap (outbuf, &out);
1073
1074   gst_buffer_set_size (outbuf, len);
1075
1076   return GST_FLOW_OK;
1077 }
1078
1079 static GstFlowReturn
1080 convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1081     GstBuffer * outbuf)
1082 {
1083   GstMapInfo in, out;
1084   guint i;
1085   GstVideoTimeCode tc;
1086   guint8 cc_data[MAX_CDP_PACKET_LEN];
1087   guint len, cea608 = 0;
1088   const struct cdp_fps_entry *fps_entry;
1089
1090   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1091   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1092
1093   len =
1094       convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1095       cc_data, &tc, &fps_entry);
1096   len /= 3;
1097
1098   if (len > fps_entry->max_cc_count) {
1099     GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u", len);
1100     len = fps_entry->max_cc_count;
1101   }
1102
1103   for (i = 0; i < len; i++) {
1104     /* We can only really copy the first field here as there can't be any
1105      * signalling in raw CEA608 and we must not mix the streams of different
1106      * fields
1107      */
1108     if (cc_data[i * 3] == 0xfc) {
1109       out.data[cea608 * 2] = cc_data[i * 3 + 1];
1110       out.data[cea608 * 2 + 1] = cc_data[i * 3 + 2];
1111       cea608++;
1112     }
1113   }
1114
1115   gst_buffer_unmap (inbuf, &in);
1116   gst_buffer_unmap (outbuf, &out);
1117
1118   gst_buffer_set_size (outbuf, 2 * cea608);
1119
1120   if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1121     gst_buffer_add_video_time_code_meta (outbuf, &tc);
1122
1123   return GST_FLOW_OK;
1124 }
1125
1126 static GstFlowReturn
1127 convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1128     GstBuffer * outbuf)
1129 {
1130   GstMapInfo in, out;
1131   guint i;
1132   GstVideoTimeCode tc;
1133   guint8 cc_data[MAX_CDP_PACKET_LEN];
1134   guint len, cea608 = 0;
1135   const struct cdp_fps_entry *fps_entry;
1136
1137   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1138   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1139
1140   len =
1141       convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1142       cc_data, &tc, &fps_entry);
1143   len /= 3;
1144
1145   if (len > fps_entry->max_cc_count) {
1146     GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u", len);
1147     len = fps_entry->max_cc_count;
1148   }
1149
1150   for (i = 0; i < len; i++) {
1151     if (cc_data[i * 3] == 0xfc || cc_data[i * 3] == 0xfd) {
1152       /* We have to assume a line offset of 0 */
1153       out.data[cea608 * 3] = cc_data[i * 3] == 0xfc ? 0x80 : 0x00;
1154       out.data[cea608 * 3 + 1] = cc_data[i * 3 + 1];
1155       out.data[cea608 * 3 + 2] = cc_data[i * 3 + 2];
1156       cea608++;
1157     }
1158   }
1159
1160   gst_buffer_unmap (inbuf, &in);
1161   gst_buffer_unmap (outbuf, &out);
1162
1163   gst_buffer_set_size (outbuf, 3 * cea608);
1164
1165   if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1166     gst_buffer_add_video_time_code_meta (outbuf, &tc);
1167
1168   return GST_FLOW_OK;
1169 }
1170
1171 static GstFlowReturn
1172 convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1173     GstBuffer * outbuf)
1174 {
1175   GstMapInfo in, out;
1176   GstVideoTimeCode tc;
1177   guint len;
1178   const struct cdp_fps_entry *fps_entry;
1179
1180   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1181   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1182
1183   len =
1184       convert_cea708_cdp_cea708_cc_data_internal (self, in.data, in.size,
1185       out.data, &tc, &fps_entry);
1186
1187   gst_buffer_unmap (inbuf, &in);
1188   gst_buffer_unmap (outbuf, &out);
1189
1190   if (len / 3 > fps_entry->max_cc_count) {
1191     GST_WARNING_OBJECT (self, "Too many cc_data triples in CDP packet %u",
1192         len / 3);
1193     len = 3 * fps_entry->max_cc_count;
1194   }
1195
1196   gst_buffer_set_size (outbuf, len);
1197
1198   if (tc.config.fps_n != 0 && !gst_buffer_get_video_time_code_meta (inbuf))
1199     gst_buffer_add_video_time_code_meta (outbuf, &tc);
1200
1201   return GST_FLOW_OK;
1202 }
1203
1204 static GstFlowReturn
1205 gst_cc_converter_transform (GstCCConverter * self, GstBuffer * inbuf,
1206     GstBuffer * outbuf)
1207 {
1208   GstVideoTimeCodeMeta *tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
1209   GstFlowReturn ret = GST_FLOW_OK;
1210
1211   GST_DEBUG_OBJECT (self, "Converting %" GST_PTR_FORMAT " from %u to %u", inbuf,
1212       self->input_caption_type, self->output_caption_type);
1213
1214   switch (self->input_caption_type) {
1215     case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1216
1217       switch (self->output_caption_type) {
1218         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1219           ret = convert_cea608_raw_cea608_s334_1a (self, inbuf, outbuf);
1220           break;
1221         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1222           ret = convert_cea608_raw_cea708_cc_data (self, inbuf, outbuf);
1223           break;
1224         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1225           ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf);
1226           break;
1227         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1228         default:
1229           g_assert_not_reached ();
1230           break;
1231       }
1232
1233       break;
1234     case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1235
1236       switch (self->output_caption_type) {
1237         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1238           ret = convert_cea608_s334_1a_cea608_raw (self, inbuf, outbuf);
1239           break;
1240         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1241           ret = convert_cea608_s334_1a_cea708_cc_data (self, inbuf, outbuf);
1242           break;
1243         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1244           ret = convert_cea608_s334_1a_cea708_cdp (self, inbuf, outbuf);
1245           break;
1246         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1247         default:
1248           g_assert_not_reached ();
1249           break;
1250       }
1251
1252       break;
1253     case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1254
1255       switch (self->output_caption_type) {
1256         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1257           ret = convert_cea708_cc_data_cea608_raw (self, inbuf, outbuf);
1258           break;
1259         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1260           ret = convert_cea708_cc_data_cea608_s334_1a (self, inbuf, outbuf);
1261           break;
1262         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1263           ret = convert_cea708_cc_data_cea708_cdp (self, inbuf, outbuf);
1264           break;
1265         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1266         default:
1267           g_assert_not_reached ();
1268           break;
1269       }
1270
1271       break;
1272     case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1273
1274       switch (self->output_caption_type) {
1275         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1276           ret = convert_cea708_cdp_cea608_raw (self, inbuf, outbuf);
1277           break;
1278         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1279           ret = convert_cea708_cdp_cea608_s334_1a (self, inbuf, outbuf);
1280           break;
1281         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1282           ret = convert_cea708_cdp_cea708_cc_data (self, inbuf, outbuf);
1283           break;
1284         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1285         default:
1286           g_assert_not_reached ();
1287           break;
1288       }
1289
1290       break;
1291     default:
1292       g_assert_not_reached ();
1293       break;
1294   }
1295
1296   if (ret != GST_FLOW_OK)
1297     return ret;
1298
1299   if (tc_meta)
1300     gst_buffer_add_video_time_code_meta (outbuf, &tc_meta->tc);
1301
1302   GST_DEBUG_OBJECT (self, "Converted to %" GST_PTR_FORMAT, outbuf);
1303
1304   return gst_buffer_get_size (outbuf) >
1305       0 ? GST_FLOW_OK : GST_BASE_TRANSFORM_FLOW_DROPPED;
1306 }
1307
1308 static GstFlowReturn
1309 gst_cc_converter_generate_output (GstBaseTransform * base, GstBuffer ** outbuf)
1310 {
1311   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (base);
1312   GstCCConverter *self = GST_CCCONVERTER (base);
1313   GstBuffer *inbuf = base->queued_buf;
1314   GstFlowReturn ret;
1315
1316   *outbuf = NULL;
1317   base->queued_buf = NULL;
1318   if (!inbuf) {
1319     return GST_FLOW_OK;
1320   }
1321
1322   if (gst_base_transform_is_passthrough (base)) {
1323     *outbuf = inbuf;
1324     ret = GST_FLOW_OK;
1325   } else {
1326     ret = bclass->prepare_output_buffer (base, inbuf, outbuf);
1327
1328     if (ret != GST_FLOW_OK || *outbuf == NULL)
1329       goto no_buffer;
1330
1331     ret = gst_cc_converter_transform (self, inbuf, *outbuf);
1332     gst_buffer_unref (inbuf);
1333   }
1334
1335   return ret;
1336
1337 no_buffer:
1338   {
1339     gst_buffer_unref (inbuf);
1340     *outbuf = NULL;
1341     GST_WARNING_OBJECT (self, "could not get buffer from pool: %s",
1342         gst_flow_get_name (ret));
1343     return ret;
1344   }
1345 }
1346
1347 static gboolean
1348 gst_cc_converter_start (GstBaseTransform * base)
1349 {
1350   GstCCConverter *self = GST_CCCONVERTER (base);
1351
1352   /* Resetting this is not really needed but makes debugging easier */
1353   self->cdp_hdr_sequence_cntr = 0;
1354
1355   return TRUE;
1356 }
1357
1358 static void
1359 gst_cc_converter_class_init (GstCCConverterClass * klass)
1360 {
1361   GstElementClass *gstelement_class;
1362   GstBaseTransformClass *basetransform_class;
1363
1364   gstelement_class = (GstElementClass *) klass;
1365   basetransform_class = (GstBaseTransformClass *) klass;
1366
1367   gst_element_class_set_static_metadata (gstelement_class,
1368       "Closed Caption Converter",
1369       "Filter/ClosedCaption",
1370       "Converts Closed Captions between different formats",
1371       "Sebastian Dröge <sebastian@centricular.com>");
1372
1373   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
1374   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
1375
1376   basetransform_class->start = GST_DEBUG_FUNCPTR (gst_cc_converter_start);
1377   basetransform_class->transform_size =
1378       GST_DEBUG_FUNCPTR (gst_cc_converter_transform_size);
1379   basetransform_class->transform_caps =
1380       GST_DEBUG_FUNCPTR (gst_cc_converter_transform_caps);
1381   basetransform_class->fixate_caps =
1382       GST_DEBUG_FUNCPTR (gst_cc_converter_fixate_caps);
1383   basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_cc_converter_set_caps);
1384   basetransform_class->generate_output =
1385       GST_DEBUG_FUNCPTR (gst_cc_converter_generate_output);
1386   basetransform_class->passthrough_on_same_caps = TRUE;
1387
1388   GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
1389       0, "Closed Caption converter");
1390 }
1391
1392 static void
1393 gst_cc_converter_init (GstCCConverter * self)
1394 {
1395 }