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