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