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