rtsp-server:wfd: Fix build error for gcc upgrade
[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 "ccutils.h"
32 #include "gstccconverter.h"
33
34 GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
35 #define GST_CAT_DEFAULT gst_cc_converter_debug
36
37 /**
38  * GstCCConverterCDPMode:
39  * @GST_CC_CONVERTER_CDP_MODE_TIME_CODE: Store time code information in CDP packets
40  * @GST_CC_CONVERTER_CDP_MODE_CC_DATA: Store CC data in CDP packets
41  * @GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO: Store CC service information in CDP packets
42  *
43  * Since: 1.20
44  */
45
46 enum
47 {
48   PROP_0,
49   PROP_CDP_MODE,
50 };
51
52 #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)
53
54 /* Ordered by the amount of information they can contain */
55 #define CC_CAPS \
56         "closedcaption/x-cea-708,format=(string) cdp; " \
57         "closedcaption/x-cea-708,format=(string) cc_data; " \
58         "closedcaption/x-cea-608,format=(string) s334-1a; " \
59         "closedcaption/x-cea-608,format=(string) raw"
60
61 #define VAL_OR_0(v) ((v) ? (*(v)) : 0)
62
63 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS (CC_CAPS));
67
68 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
69     GST_PAD_SRC,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS (CC_CAPS));
72
73 #define parent_class gst_cc_converter_parent_class
74 G_DEFINE_TYPE (GstCCConverter, gst_cc_converter, GST_TYPE_BASE_TRANSFORM);
75 GST_ELEMENT_REGISTER_DEFINE (ccconverter, "ccconverter",
76     GST_RANK_NONE, GST_TYPE_CCCONVERTER);
77
78 #define GST_TYPE_CC_CONVERTER_CDP_MODE (gst_cc_converter_cdp_mode_get_type())
79 static GType
80 gst_cc_converter_cdp_mode_get_type (void)
81 {
82   static const GFlagsValue values[] = {
83     {GST_CC_CDP_MODE_TIME_CODE,
84         "Store time code information in CDP packets", "time-code"},
85     {GST_CC_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
86         "cc-data"},
87     {GST_CC_CDP_MODE_CC_SVC_INFO,
88         "Store CC service information in CDP packets", "cc-svc-info"},
89     {0, NULL, NULL}
90   };
91   static GType id = 0;
92
93   if (g_once_init_enter ((gsize *) & id)) {
94     GType _id;
95
96     _id = g_flags_register_static ("GstCCConverterCDPMode", values);
97
98     g_once_init_leave ((gsize *) & id, _id);
99   }
100
101   return id;
102 }
103
104 static gboolean
105 gst_cc_converter_transform_size (GstBaseTransform * base,
106     GstPadDirection direction,
107     GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
108 {
109   /* We can't really convert from an output size to an input size */
110   if (direction != GST_PAD_SINK)
111     return FALSE;
112
113   /* Assume worst-case here and over-allocate, and in ::transform() we then
114    * downsize the buffer as needed. The worst-case is one CDP packet, which
115    * can be up to MAX_CDP_PACKET_LEN bytes large */
116
117   *othersize = MAX_CDP_PACKET_LEN;
118
119   return TRUE;
120 }
121
122 static GstCaps *
123 gst_cc_converter_transform_caps (GstBaseTransform * base,
124     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
125 {
126   static GstStaticCaps non_cdp_caps =
127       GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cc_data; "
128       "closedcaption/x-cea-608,format=(string) s334-1a; "
129       "closedcaption/x-cea-608,format=(string) raw");
130   static GstStaticCaps cdp_caps =
131       GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp");
132   static GstStaticCaps cdp_caps_framerate =
133       GST_STATIC_CAPS ("closedcaption/x-cea-708, format=(string)cdp, "
134       "framerate=(fraction){60/1, 60000/1001, 50/1, 30/1, 30000/1001, 25/1, 24/1, 24000/1001}");
135
136   GstCCConverter *self = GST_CCCONVERTER (base);
137   guint i, n;
138   GstCaps *res, *templ;
139
140   templ = gst_pad_get_pad_template_caps (base->srcpad);
141
142   GST_DEBUG_OBJECT (self, "direction %s from caps %" GST_PTR_FORMAT,
143       direction == GST_PAD_SRC ? "src" : "sink", caps);
144
145   res = gst_caps_new_empty ();
146   n = gst_caps_get_size (caps);
147   for (i = 0; i < n; i++) {
148     const GstStructure *s = gst_caps_get_structure (caps, i);
149     const GValue *framerate = gst_structure_get_value (s, "framerate");
150
151     if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
152
153       if (direction == GST_PAD_SRC) {
154         /* SRC direction: We produce upstream caps
155          *
156          * Downstream wanted CEA608 caps. If it had a framerate, we
157          * also need upstream to provide exactly that same framerate
158          * and otherwise we don't care.
159          *
160          * We can convert everything to CEA608.
161          */
162         res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
163         if (framerate) {
164           /* we can only keep the same framerate for non-cdp */
165           GstCaps *tmp;
166
167           tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
168           gst_caps_set_value (tmp, "framerate", framerate);
169           res = gst_caps_merge (res, tmp);
170         } else {
171           res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
172         }
173       } else {
174         /* SINK: We produce downstream caps
175          *
176          * Upstream provided CEA608 caps. We can convert that to CDP if
177          * also a CDP compatible framerate was provided, and we can convert
178          * it to anything else regardless.
179          *
180          * If upstream provided a framerate we can pass that through, possibly
181          * filtered for the CDP case.
182          */
183         if (framerate) {
184           GstCaps *tmp;
185           GstStructure *t;
186
187           /* Create caps that contain the intersection of all framerates with
188            * the CDP allowed framerates */
189           tmp =
190               gst_caps_make_writable (gst_static_caps_get
191               (&cdp_caps_framerate));
192           t = gst_caps_get_structure (tmp, 0);
193           gst_structure_set_name (t, "closedcaption/x-cea-608");
194           gst_structure_remove_field (t, "format");
195           if (gst_structure_can_intersect (s, t)) {
196             gst_caps_unref (tmp);
197
198             tmp =
199                 gst_caps_make_writable (gst_static_caps_get
200                 (&cdp_caps_framerate));
201
202             res = gst_caps_merge (res, tmp);
203           } else {
204             gst_caps_unref (tmp);
205           }
206           /* And we can convert to everything else with the given framerate */
207           tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
208           gst_caps_set_value (tmp, "framerate", framerate);
209           res = gst_caps_merge (res, tmp);
210         } else {
211           res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
212         }
213       }
214     } else if (gst_structure_has_name (s, "closedcaption/x-cea-708")) {
215       if (direction == GST_PAD_SRC) {
216         /* SRC direction: We produce upstream caps
217          *
218          * Downstream wanted CEA708 caps. If downstream wants *only* CDP we
219          * either need CDP from upstream, or anything else with a CDP
220          * framerate.
221          * If downstream also wants non-CDP we can accept anything.
222          *
223          * We pass through any framerate as-is, except for filtering
224          * for CDP framerates if downstream wants only CDP.
225          */
226
227         if (g_strcmp0 (gst_structure_get_string (s, "format"), "cdp") == 0) {
228           /* Downstream wants only CDP */
229
230           /* We need CDP from upstream in that case */
231           res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
232
233           /* Or anything else with a CDP framerate */
234           if (framerate) {
235             GstCaps *tmp;
236             GstStructure *t;
237             const GValue *cdp_framerate;
238
239             /* Create caps that contain the intersection of all framerates with
240              * the CDP allowed framerates */
241             tmp =
242                 gst_caps_make_writable (gst_static_caps_get
243                 (&cdp_caps_framerate));
244             t = gst_caps_get_structure (tmp, 0);
245
246             /* There's an intersection between the framerates so we can convert
247              * into CDP with exactly those framerates from anything else */
248             cdp_framerate = gst_structure_get_value (t, "framerate");
249             tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
250             gst_caps_set_value (tmp, "framerate", cdp_framerate);
251             res = gst_caps_merge (res, tmp);
252           } else {
253             GstCaps *tmp, *cdp_caps;
254             const GValue *cdp_framerate;
255
256             /* Get all CDP framerates, we can accept anything that has those
257              * framerates */
258             cdp_caps = gst_static_caps_get (&cdp_caps_framerate);
259             cdp_framerate =
260                 gst_structure_get_value (gst_caps_get_structure (cdp_caps, 0),
261                 "framerate");
262
263             tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
264             gst_caps_set_value (tmp, "framerate", cdp_framerate);
265             gst_caps_unref (cdp_caps);
266
267             res = gst_caps_merge (res, tmp);
268           }
269         } else {
270           /* Downstream wants not only CDP, we can do everything */
271           res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
272           if (framerate) {
273             /* we can only keep the same framerate for non-cdp */
274             GstCaps *tmp;
275
276             tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
277             gst_caps_set_value (tmp, "framerate", framerate);
278             res = gst_caps_merge (res, tmp);
279           } else {
280             res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
281           }
282         }
283       } else {
284         GstCaps *tmp;
285
286         /* SINK: We produce downstream caps
287          *
288          * Upstream provided CEA708 caps. If upstream provided CDP we can
289          * output CDP, no matter what (-> passthrough). If upstream did not
290          * provide CDP, we can output CDP only if the framerate fits.
291          * We can always produce everything else apart from CDP.
292          *
293          * If upstream provided a framerate we pass that through for non-CDP
294          * output, and pass it through filtered for CDP output.
295          */
296
297         if (gst_structure_can_intersect (s,
298                 gst_caps_get_structure (gst_static_caps_get (&cdp_caps), 0))) {
299           /* Upstream provided CDP caps, we can do everything independent of
300            * framerate */
301           res = gst_caps_merge (res, gst_static_caps_get (&cdp_caps_framerate));
302         } else if (framerate) {
303           const GValue *cdp_framerate;
304           GstStructure *t;
305
306           /* Upstream did not provide CDP. We can only do CDP if upstream
307            * happened to have a CDP framerate */
308
309           /* Create caps that contain the intersection of all framerates with
310            * the CDP allowed framerates */
311           tmp =
312               gst_caps_make_writable (gst_static_caps_get
313               (&cdp_caps_framerate));
314           t = gst_caps_get_structure (tmp, 0);
315
316           /* There's an intersection between the framerates so we can convert
317            * into CDP with exactly those framerates */
318           cdp_framerate = gst_structure_get_value (t, "framerate");
319           if (gst_value_intersect (NULL, cdp_framerate, framerate)) {
320             gst_caps_set_value (tmp, "framerate", cdp_framerate);
321
322             res = gst_caps_merge (res, tmp);
323           } else {
324             gst_clear_caps (&tmp);
325           }
326         }
327         /* We can always convert CEA708 to all non-CDP formats */
328         if (framerate) {
329           /* we can only keep the same framerate for non-cdp */
330           GstCaps *tmp;
331
332           tmp = gst_caps_make_writable (gst_static_caps_get (&non_cdp_caps));
333           gst_caps_set_value (tmp, "framerate", framerate);
334           res = gst_caps_merge (res, tmp);
335         } else {
336           res = gst_caps_merge (res, gst_static_caps_get (&non_cdp_caps));
337         }
338       }
339     } else {
340       g_assert_not_reached ();
341     }
342   }
343
344   GST_DEBUG_OBJECT (self, "pre filter caps %" GST_PTR_FORMAT, res);
345
346   /* We can convert anything into anything but it might involve loss of
347    * information so always filter according to the order in our template caps
348    * in the end */
349   if (filter) {
350     GstCaps *tmp;
351     filter = gst_caps_intersect_full (templ, filter, GST_CAPS_INTERSECT_FIRST);
352
353     tmp = gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
354     gst_caps_unref (res);
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   gst_clear_caps (&filter);
366
367   return res;
368 }
369
370 static GstCaps *
371 gst_cc_converter_fixate_caps (GstBaseTransform * base,
372     GstPadDirection direction, GstCaps * incaps, GstCaps * outcaps)
373 {
374   GstCCConverter *self = GST_CCCONVERTER (base);
375   const GstStructure *s;
376   GstStructure *t;
377   const GValue *framerate;
378   GstCaps *intersection, *templ;
379
380   GST_DEBUG_OBJECT (self, "Fixating in direction %s incaps %" GST_PTR_FORMAT,
381       direction == GST_PAD_SRC ? "src" : "sink", incaps);
382   GST_DEBUG_OBJECT (self, "and outcaps %" GST_PTR_FORMAT, outcaps);
383
384   /* Prefer passthrough if we can */
385   if (gst_caps_is_subset (incaps, outcaps)) {
386     gst_caps_unref (outcaps);
387     return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base,
388         direction, incaps, gst_caps_ref (incaps));
389   }
390
391   /* Otherwise prefer caps in the order of our template caps */
392   templ = gst_pad_get_pad_template_caps (base->srcpad);
393   intersection =
394       gst_caps_intersect_full (templ, outcaps, GST_CAPS_INTERSECT_FIRST);
395   gst_caps_unref (outcaps);
396   outcaps = intersection;
397
398   outcaps =
399       GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (base, direction,
400       incaps, outcaps);
401
402   s = gst_caps_get_structure (incaps, 0);
403   framerate = gst_structure_get_value (s, "framerate");
404   outcaps = gst_caps_make_writable (outcaps);
405   t = gst_caps_get_structure (outcaps, 0);
406   if (!framerate) {
407     /* remove any output framerate that might've been added by basetransform
408      * due to intersecting with downstream */
409     gst_structure_remove_field (t, "framerate");
410   } else {
411     /* or passthrough the input framerate if possible */
412     guint n, d;
413
414     n = gst_value_get_fraction_numerator (framerate);
415     d = gst_value_get_fraction_denominator (framerate);
416
417     if (gst_structure_has_field (t, "framerate"))
418       gst_structure_fixate_field_nearest_fraction (t, "framerate", n, d);
419     else
420       gst_structure_set (t, "framerate", GST_TYPE_FRACTION, n, d, NULL);
421   }
422
423   GST_DEBUG_OBJECT (self,
424       "Fixated caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, incaps, outcaps);
425
426   return outcaps;
427 }
428
429 static gboolean
430 gst_cc_converter_set_caps (GstBaseTransform * base, GstCaps * incaps,
431     GstCaps * outcaps)
432 {
433   GstCCConverter *self = GST_CCCONVERTER (base);
434   const GstStructure *s;
435   gboolean passthrough;
436
437   self->input_caption_type = gst_video_caption_type_from_caps (incaps);
438   self->output_caption_type = gst_video_caption_type_from_caps (outcaps);
439
440   if (self->input_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN ||
441       self->output_caption_type == GST_VIDEO_CAPTION_TYPE_UNKNOWN)
442     goto invalid_caps;
443
444   s = gst_caps_get_structure (incaps, 0);
445   if (!gst_structure_get_fraction (s, "framerate", &self->in_fps_n,
446           &self->in_fps_d))
447     self->in_fps_n = self->in_fps_d = 0;
448
449   s = gst_caps_get_structure (outcaps, 0);
450   if (!gst_structure_get_fraction (s, "framerate", &self->out_fps_n,
451           &self->out_fps_d))
452     self->out_fps_n = self->out_fps_d = 0;
453
454   gst_video_time_code_clear (&self->current_output_timecode);
455
456   /* Caps can be different but we can passthrough as long as they can
457    * intersect, i.e. have same caps name and format */
458   passthrough = gst_caps_can_intersect (incaps, outcaps);
459   gst_base_transform_set_passthrough (base, passthrough);
460
461   GST_DEBUG_OBJECT (self,
462       "Got caps %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT " (passthrough %d)",
463       incaps, outcaps, passthrough);
464
465   return TRUE;
466
467 invalid_caps:
468   {
469     GST_ERROR_OBJECT (self,
470         "Invalid caps: in %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, incaps,
471         outcaps);
472     return FALSE;
473   }
474 }
475
476 static void
477 get_framerate_output_scale (GstCCConverter * self,
478     const struct cdp_fps_entry *in_fps_entry, gint * scale_n, gint * scale_d)
479 {
480   if (self->in_fps_n == 0 || self->out_fps_d == 0) {
481     *scale_n = 1;
482     *scale_d = 1;
483     return;
484   }
485
486   /* compute the relative rates of the two framerates */
487   if (!gst_util_fraction_multiply (in_fps_entry->fps_d, in_fps_entry->fps_n,
488           self->out_fps_n, self->out_fps_d, scale_n, scale_d))
489     /* we should never overflow */
490     g_assert_not_reached ();
491 }
492
493 static gboolean
494 interpolate_time_code_with_framerate (GstCCConverter * self,
495     const GstVideoTimeCode * tc, gint out_fps_n, gint out_fps_d,
496     gint scale_n, gint scale_d, GstVideoTimeCode * out)
497 {
498   gchar *tc_str;
499   gint output_n, output_d;
500   guint output_frame;
501   GstVideoTimeCodeFlags flags;
502
503   g_return_val_if_fail (out != NULL, FALSE);
504   /* out_n/d can only be 0 if scale_n/d are 1/1 */
505   g_return_val_if_fail ((scale_n == 1 && scale_d == 1) || (out_fps_n != 0
506           && out_fps_d != 0), FALSE);
507
508   if (!tc || tc->config.fps_n == 0)
509     return FALSE;
510
511   if (!gst_util_fraction_multiply (tc->frames, 1, scale_n, scale_d, &output_n,
512           &output_d))
513     /* we should never overflow */
514     g_assert_not_reached ();
515
516   tc_str = gst_video_time_code_to_string (tc);
517   GST_TRACE_OBJECT (self, "interpolating time code %s with scale %d/%d "
518       "to frame %d/%d", tc_str, scale_n, scale_d, output_n, output_d);
519   g_free (tc_str);
520
521   if (out_fps_n == 0 || out_fps_d == 0) {
522     out_fps_n = tc->config.fps_n;
523     out_fps_d = tc->config.fps_d;
524   }
525
526   flags = tc->config.flags;
527   if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0 && out_fps_d != 1001
528       && out_fps_n != 60000 && out_fps_n != 30000) {
529     flags &= ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
530   } else if ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) == 0
531       && out_fps_d == 1001 && (out_fps_n == 60000 || out_fps_n == 30000)) {
532     /* XXX: theoretically, not quite correct however this is an assumption
533      * we have elsewhere that these framerates are always drop-framed */
534     flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
535   }
536
537   output_frame = output_n / output_d;
538
539   *out = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
540   do {
541     /* here we try to find the next available valid timecode.  The dropped
542      * (when they exist) frames in time codes are that the beginning of each
543      * minute */
544     gst_video_time_code_clear (out);
545     gst_video_time_code_init (out, out_fps_n, out_fps_d,
546         tc->config.latest_daily_jam, flags, tc->hours, tc->minutes,
547         tc->seconds, output_frame, tc->field_count);
548     output_frame++;
549   } while ((flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) != 0
550       && output_frame < 10 && !gst_video_time_code_is_valid (out));
551
552   tc_str = gst_video_time_code_to_string (out);
553   GST_TRACE_OBJECT (self, "interpolated to %s", tc_str);
554   g_free (tc_str);
555
556   return TRUE;
557 }
558
559 static gboolean
560 can_take_buffer (GstCCConverter * self,
561     const struct cdp_fps_entry *in_fps_entry,
562     const struct cdp_fps_entry *out_fps_entry,
563     const GstVideoTimeCode * in_tc, GstVideoTimeCode * out_tc)
564 {
565   int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
566   int output_time_cmp, scale_n, scale_d;
567
568   /* TODO: handle input discont */
569
570   if (self->in_fps_n == 0) {
571     input_frame_n = self->input_frames;
572     input_frame_d = 1;
573   } else {
574     /* compute the relative frame count for each */
575     if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
576             self->input_frames, 1, &input_frame_n, &input_frame_d))
577       /* we should never overflow */
578       g_assert_not_reached ();
579   }
580
581   if (self->in_fps_n == 0) {
582     output_frame_n = self->output_frames;
583     output_frame_d = 1;
584   } else {
585     if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
586             self->output_frames, 1, &output_frame_n, &output_frame_d))
587       /* we should never overflow */
588       g_assert_not_reached ();
589   }
590
591   output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
592       output_frame_n, output_frame_d);
593
594   if (output_time_cmp == 0) {
595     self->output_frames = 0;
596     self->input_frames = 0;
597   }
598
599   in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
600   if (!in_fps_entry || in_fps_entry->fps_n == 0)
601     g_assert_not_reached ();
602
603   /* compute the relative rates of the two framerates */
604   get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
605
606   GST_TRACE_OBJECT (self, "performing conversion at scale %d/%d, "
607       "time comparison %i", scale_n, scale_d, output_time_cmp);
608
609   if (output_time_cmp < 0) {
610     /* we can't generate an output yet */
611     return FALSE;
612   } else {
613     interpolate_time_code_with_framerate (self, in_tc, out_fps_entry->fps_n,
614         out_fps_entry->fps_d, scale_n, scale_d, out_tc);
615     return TRUE;
616   }
617 }
618
619 static guint
620 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
621     const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
622     const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
623 {
624   guint ret;
625
626   ret = convert_cea708_cc_data_to_cdp (GST_OBJECT (self),
627       (GstCCCDPMode) self->cdp_mode, self->cdp_hdr_sequence_cntr, cc_data,
628       cc_data_len, cdp, cdp_len, tc, fps_entry);
629   self->cdp_hdr_sequence_cntr++;
630
631   return ret;
632 }
633
634 static gboolean
635 push_cdp_buffer (GstCCConverter * self, GstBuffer * inbuf,
636     GstVideoTimeCode * out_tc, const struct cdp_fps_entry **in_fps_entry)
637 {
638   guint8 cc_data[MAX_CDP_PACKET_LEN];
639   guint cc_data_len = 0;
640   GstMapInfo in;
641
642   if (inbuf) {
643     gst_buffer_map (inbuf, &in, GST_MAP_READ);
644
645     cc_data_len =
646         convert_cea708_cdp_to_cc_data (GST_OBJECT (self), in.data, in.size,
647         cc_data, out_tc, in_fps_entry);
648
649     cc_buffer_push_cc_data (self->cc_buffer, cc_data, cc_data_len);
650
651     gst_buffer_unmap (inbuf, &in);
652     self->input_frames++;
653   }
654
655   return TRUE;
656 }
657
658 static GstFlowReturn
659 convert_cea608_raw_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
660     GstBuffer * outbuf)
661 {
662   GstMapInfo in, out;
663   guint i, n;
664
665   n = gst_buffer_get_size (inbuf);
666   if (n & 1) {
667     GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
668     gst_buffer_set_size (outbuf, 0);
669     return GST_FLOW_OK;
670   }
671
672   n /= 2;
673
674   if (n > 3) {
675     GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u.  Truncating to %u", n,
676         3);
677     n = 3;
678   }
679
680   gst_buffer_set_size (outbuf, 3 * n);
681
682   gst_buffer_map (inbuf, &in, GST_MAP_READ);
683   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
684
685   /* We have to assume that each value is from the first field and
686    * don't know from which line offset it originally is */
687   for (i = 0; i < n; i++) {
688     out.data[i * 3] = 0x80;
689     out.data[i * 3 + 1] = in.data[i * 2];
690     out.data[i * 3 + 2] = in.data[i * 2 + 1];
691   }
692
693   gst_buffer_unmap (inbuf, &in);
694   gst_buffer_unmap (outbuf, &out);
695
696   return GST_FLOW_OK;
697 }
698
699 static GstFlowReturn
700 convert_cea608_raw_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
701     GstBuffer * outbuf)
702 {
703   GstMapInfo in, out;
704   guint i, n;
705
706   n = gst_buffer_get_size (inbuf);
707   if (n & 1) {
708     GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
709     gst_buffer_set_size (outbuf, 0);
710     return GST_FLOW_OK;
711   }
712
713   n /= 2;
714
715   if (n > 3) {
716     GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u", n,
717         3);
718     n = 3;
719   }
720
721   gst_buffer_set_size (outbuf, 3 * n);
722
723   gst_buffer_map (inbuf, &in, GST_MAP_READ);
724   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
725
726   /* We have to assume that each value is from the first field and
727    * don't know from which line offset it originally is */
728   for (i = 0; i < n; i++) {
729     out.data[i * 3] = 0xfc;
730     out.data[i * 3 + 1] = in.data[i * 2];
731     out.data[i * 3 + 2] = in.data[i * 2 + 1];
732   }
733
734   gst_buffer_unmap (inbuf, &in);
735   gst_buffer_unmap (outbuf, &out);
736
737   return GST_FLOW_OK;
738 }
739
740 static GstFlowReturn
741 convert_cea608_raw_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
742     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
743 {
744   GstMapInfo in, out;
745   const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
746   guint cc_data_len = MAX_CDP_PACKET_LEN;
747   guint8 cc_data[MAX_CDP_PACKET_LEN];
748
749   in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
750   if (!in_fps_entry || in_fps_entry->fps_n == 0)
751     g_assert_not_reached ();
752
753   if (inbuf) {
754     guint n = 0;
755
756     n = gst_buffer_get_size (inbuf);
757     if (n & 1) {
758       GST_WARNING_OBJECT (self, "Invalid raw CEA608 buffer size");
759       gst_buffer_set_size (outbuf, 0);
760       return GST_FLOW_OK;
761     }
762
763     n /= 2;
764
765     if (n > in_fps_entry->max_cea608_count) {
766       GST_WARNING_OBJECT (self, "Too many CEA608 pairs %u. Truncating to %u",
767           n, in_fps_entry->max_cea608_count);
768       n = in_fps_entry->max_cea608_count;
769     }
770
771     gst_buffer_map (inbuf, &in, GST_MAP_READ);
772     cc_buffer_push_separated (self->cc_buffer, in.data, in.size, NULL, 0, NULL,
773         0);
774     gst_buffer_unmap (inbuf, &in);
775     self->input_frames++;
776   }
777
778   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
779   if (!out_fps_entry || out_fps_entry->fps_n == 0)
780     g_assert_not_reached ();
781
782   if (!can_take_buffer (self, in_fps_entry, out_fps_entry,
783           tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode))
784     goto drop;
785
786   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data,
787       &cc_data_len);
788
789   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
790   cc_data_len =
791       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
792       out.data, out.size, &self->current_output_timecode, out_fps_entry);
793   self->output_frames++;
794   gst_buffer_unmap (outbuf, &out);
795
796 out:
797   gst_buffer_set_size (outbuf, cc_data_len);
798
799   return GST_FLOW_OK;
800
801 drop:
802   cc_data_len = 0;
803   goto out;
804 }
805
806 static GstFlowReturn
807 convert_cea608_s334_1a_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
808     GstBuffer * outbuf)
809 {
810   GstMapInfo in, out;
811   guint i, n;
812   guint cea608 = 0;
813
814   n = gst_buffer_get_size (inbuf);
815   if (n % 3 != 0) {
816     GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
817     n = n - (n % 3);
818   }
819
820   n /= 3;
821
822   if (n > 3) {
823     GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
824     n = 3;
825   }
826
827   gst_buffer_map (inbuf, &in, GST_MAP_READ);
828   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
829
830   for (i = 0; i < n; i++) {
831     if (in.data[i * 3] & 0x80) {
832       out.data[i * 2] = in.data[i * 3 + 1];
833       out.data[i * 2 + 1] = in.data[i * 3 + 2];
834       cea608++;
835     }
836   }
837
838   gst_buffer_unmap (inbuf, &in);
839   gst_buffer_unmap (outbuf, &out);
840
841   gst_buffer_set_size (outbuf, 2 * cea608);
842
843   return GST_FLOW_OK;
844 }
845
846 static GstFlowReturn
847 convert_cea608_s334_1a_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
848     GstBuffer * outbuf)
849 {
850   GstMapInfo in, out;
851   guint i, n;
852
853   n = gst_buffer_get_size (inbuf);
854   if (n % 3 != 0) {
855     GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
856     n = n - (n % 3);
857   }
858
859   n /= 3;
860
861   if (n > 3) {
862     GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
863     n = 3;
864   }
865
866   gst_buffer_set_size (outbuf, 3 * n);
867
868   gst_buffer_map (inbuf, &in, GST_MAP_READ);
869   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
870
871   for (i = 0; i < n; i++) {
872     out.data[i * 3] = (in.data[i * 3] & 0x80) ? 0xfc : 0xfd;
873     out.data[i * 3 + 1] = in.data[i * 3 + 1];
874     out.data[i * 3 + 2] = in.data[i * 3 + 2];
875   }
876
877   gst_buffer_unmap (inbuf, &in);
878   gst_buffer_unmap (outbuf, &out);
879
880   return GST_FLOW_OK;
881 }
882
883 static GstFlowReturn
884 convert_cea608_s334_1a_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
885     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
886 {
887   GstMapInfo in, out;
888   const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
889   guint cc_data_len = MAX_CDP_PACKET_LEN;
890   guint cea608_1_len = 0, cea608_2_len = 0;
891   guint8 cc_data[MAX_CDP_PACKET_LEN];
892   guint8 cea608_1[MAX_CEA608_LEN], cea608_2[MAX_CEA608_LEN];
893   guint i, n;
894
895   in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
896   if (!in_fps_entry || in_fps_entry->fps_n == 0)
897     g_assert_not_reached ();
898
899   if (inbuf) {
900     n = gst_buffer_get_size (inbuf);
901     if (n % 3 != 0) {
902       GST_WARNING_OBJECT (self, "Invalid S334-1A CEA608 buffer size");
903       n = n - (n % 3);
904     }
905
906     n /= 3;
907
908     if (n > in_fps_entry->max_cea608_count) {
909       GST_WARNING_OBJECT (self, "Too many S334-1A CEA608 triplets %u", n);
910       n = in_fps_entry->max_cea608_count;
911     }
912
913     gst_buffer_map (inbuf, &in, GST_MAP_READ);
914
915     for (i = 0; i < n; i++) {
916       guint byte1 = in.data[i * 3 + 1];
917       guint byte2 = in.data[i * 3 + 2];
918
919       if (in.data[i * 3] & 0x80) {
920         if (byte1 != 0x80 || byte2 != 0x80) {
921           cea608_1[cea608_1_len++] = byte1;
922           cea608_1[cea608_1_len++] = byte2;
923         }
924       } else {
925         if (byte1 != 0x80 || byte2 != 0x80) {
926           cea608_2[cea608_2_len++] = byte1;
927           cea608_2[cea608_2_len++] = byte2;
928         }
929       }
930     }
931     gst_buffer_unmap (inbuf, &in);
932
933     cc_buffer_push_separated (self->cc_buffer, cea608_1, cea608_1_len,
934         cea608_2, cea608_2_len, NULL, 0);
935     self->input_frames++;
936   }
937
938   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
939   if (!out_fps_entry || out_fps_entry->fps_n == 0)
940     g_assert_not_reached ();
941
942   if (!can_take_buffer (self, in_fps_entry, out_fps_entry,
943           tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode))
944     goto drop;
945
946   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data,
947       &cc_data_len);
948
949   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
950   cc_data_len =
951       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
952       out.data, out.size, &self->current_output_timecode, out_fps_entry);
953   self->output_frames++;
954   gst_buffer_unmap (outbuf, &out);
955
956 out:
957   gst_buffer_set_size (outbuf, cc_data_len);
958
959   return GST_FLOW_OK;
960
961 drop:
962   cc_data_len = 0;
963   goto out;
964 }
965
966 static GstFlowReturn
967 convert_cea708_cc_data_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
968     GstBuffer * outbuf)
969 {
970   GstMapInfo in, out;
971   guint i, n;
972   guint cea608 = 0;
973
974   n = gst_buffer_get_size (inbuf);
975   if (n % 3 != 0) {
976     GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
977     n = n - (n % 3);
978   }
979
980   n /= 3;
981
982   if (n > 25) {
983     GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
984     n = 25;
985   }
986
987   gst_buffer_map (inbuf, &in, GST_MAP_READ);
988   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
989
990   for (i = 0; i < n; i++) {
991     /* We can only really copy the first field here as there can't be any
992      * signalling in raw CEA608 and we must not mix the streams of different
993      * fields
994      */
995     if (in.data[i * 3] == 0xfc) {
996       out.data[cea608 * 2] = in.data[i * 3 + 1];
997       out.data[cea608 * 2 + 1] = in.data[i * 3 + 2];
998       cea608++;
999     }
1000   }
1001
1002   gst_buffer_unmap (inbuf, &in);
1003   gst_buffer_unmap (outbuf, &out);
1004
1005   gst_buffer_set_size (outbuf, 2 * cea608);
1006
1007   return GST_FLOW_OK;
1008 }
1009
1010 static GstFlowReturn
1011 convert_cea708_cc_data_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1012     GstBuffer * outbuf)
1013 {
1014   GstMapInfo in, out;
1015   guint i, n;
1016   guint cea608 = 0;
1017
1018   n = gst_buffer_get_size (inbuf);
1019   if (n % 3 != 0) {
1020     GST_WARNING_OBJECT (self, "Invalid raw CEA708 buffer size");
1021     n = n - (n % 3);
1022   }
1023
1024   n /= 3;
1025
1026   if (n > 25) {
1027     GST_WARNING_OBJECT (self, "Too many CEA708 triplets %u", n);
1028     n = 25;
1029   }
1030
1031   gst_buffer_map (inbuf, &in, GST_MAP_READ);
1032   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1033
1034   for (i = 0; i < n; i++) {
1035     if (in.data[i * 3] == 0xfc || in.data[i * 3] == 0xfd) {
1036       /* We have to assume a line offset of 0 */
1037       out.data[cea608 * 3] = in.data[i * 3] == 0xfc ? 0x80 : 0x00;
1038       out.data[cea608 * 3 + 1] = in.data[i * 3 + 1];
1039       out.data[cea608 * 3 + 2] = in.data[i * 3 + 2];
1040       cea608++;
1041     }
1042   }
1043
1044   gst_buffer_unmap (inbuf, &in);
1045   gst_buffer_unmap (outbuf, &out);
1046
1047   gst_buffer_set_size (outbuf, 3 * cea608);
1048
1049   return GST_FLOW_OK;
1050 }
1051
1052 static GstFlowReturn
1053 convert_cea708_cc_data_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1054     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1055 {
1056   GstMapInfo in, out;
1057   const struct cdp_fps_entry *in_fps_entry, *out_fps_entry;
1058   guint in_cc_data_len;
1059   guint cc_data_len = MAX_CDP_PACKET_LEN;
1060   guint8 cc_data[MAX_CDP_PACKET_LEN];
1061   guint8 *in_cc_data;
1062
1063   if (inbuf) {
1064     gst_buffer_map (inbuf, &in, GST_MAP_READ);
1065     in_cc_data = in.data;
1066     in_cc_data_len = in.size;
1067     self->input_frames++;
1068   } else {
1069     in_cc_data = NULL;
1070     in_cc_data_len = 0;
1071   }
1072
1073   in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1074   if (!in_fps_entry || in_fps_entry->fps_n == 0)
1075     g_assert_not_reached ();
1076
1077   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1078   if (!out_fps_entry || out_fps_entry->fps_n == 0)
1079     g_assert_not_reached ();
1080
1081   cc_buffer_push_cc_data (self->cc_buffer, in_cc_data, in_cc_data_len);
1082   if (inbuf)
1083     gst_buffer_unmap (inbuf, &in);
1084
1085   if (!can_take_buffer (self, in_fps_entry, out_fps_entry,
1086           tc_meta ? &tc_meta->tc : NULL, &self->current_output_timecode))
1087     goto drop;
1088
1089   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data,
1090       &cc_data_len);
1091
1092   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1093   cc_data_len =
1094       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1095       out.data, out.size, &self->current_output_timecode, out_fps_entry);
1096   self->output_frames++;
1097   gst_buffer_unmap (outbuf, &out);
1098
1099 out:
1100   gst_buffer_set_size (outbuf, cc_data_len);
1101
1102   return GST_FLOW_OK;
1103
1104 drop:
1105   cc_data_len = 0;
1106   goto out;
1107 }
1108
1109 static GstFlowReturn
1110 convert_cea708_cdp_cea608_raw (GstCCConverter * self, GstBuffer * inbuf,
1111     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1112 {
1113   GstMapInfo out;
1114   GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1115   guint cea608_1_len;
1116   const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1117
1118   if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry)) {
1119     gst_buffer_set_size (outbuf, 0);
1120     return GST_FLOW_OK;
1121   }
1122
1123   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1124   if (!out_fps_entry || out_fps_entry->fps_n == 0)
1125     out_fps_entry = in_fps_entry;
1126
1127   if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc,
1128           &self->current_output_timecode))
1129     goto drop;
1130
1131   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1132   cea608_1_len = out.size;
1133   cc_buffer_take_separated (self->cc_buffer, out_fps_entry, out.data,
1134       &cea608_1_len, NULL, 0, NULL, 0);
1135   gst_buffer_unmap (outbuf, &out);
1136   self->output_frames++;
1137
1138   if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
1139     gst_buffer_add_video_time_code_meta (outbuf,
1140         &self->current_output_timecode);
1141     gst_video_time_code_increment_frame (&self->current_output_timecode);
1142   }
1143
1144 out:
1145   gst_buffer_set_size (outbuf, cea608_1_len);
1146   return GST_FLOW_OK;
1147
1148 drop:
1149   cea608_1_len = 0;
1150   goto out;
1151 }
1152
1153 static GstFlowReturn
1154 convert_cea708_cdp_cea608_s334_1a (GstCCConverter * self, GstBuffer * inbuf,
1155     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1156 {
1157   GstMapInfo out;
1158   GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1159   const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1160   guint cc_data_len;
1161   int s334_len;
1162   guint i;
1163
1164   if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry))
1165     goto drop;
1166
1167   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1168   if (!out_fps_entry || out_fps_entry->fps_n == 0)
1169     out_fps_entry = in_fps_entry;
1170
1171   if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc,
1172           &self->current_output_timecode))
1173     goto drop;
1174
1175   gst_buffer_map (outbuf, &out, GST_MAP_READWRITE);
1176
1177   cc_data_len = out.size;
1178   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, out.data,
1179       &cc_data_len);
1180   s334_len = drop_ccp_from_cc_data (out.data, cc_data_len);
1181   if (s334_len < 0)
1182     goto drop;
1183
1184   for (i = 0; i < s334_len / 3; i++) {
1185     guint byte = out.data[i * 3];
1186     /* We have to assume a line offset of 0 */
1187     out.data[i * 3] = (byte == 0xfc || byte == 0xf8) ? 0x80 : 0x00;
1188   }
1189
1190   gst_buffer_unmap (outbuf, &out);
1191   self->output_frames++;
1192
1193   gst_buffer_set_size (outbuf, s334_len);
1194
1195   if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
1196     gst_buffer_add_video_time_code_meta (outbuf,
1197         &self->current_output_timecode);
1198     gst_video_time_code_increment_frame (&self->current_output_timecode);
1199   }
1200
1201   return GST_FLOW_OK;
1202
1203 drop:
1204   gst_buffer_set_size (outbuf, 0);
1205   return GST_FLOW_OK;
1206 }
1207
1208 static GstFlowReturn
1209 convert_cea708_cdp_cea708_cc_data (GstCCConverter * self, GstBuffer * inbuf,
1210     GstBuffer * outbuf, const GstVideoTimeCodeMeta * tc_meta)
1211 {
1212   GstMapInfo out;
1213   GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1214   const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1215   guint out_len = 0;
1216
1217   if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry))
1218     goto out;
1219
1220   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1221   if (!out_fps_entry || out_fps_entry->fps_n == 0)
1222     out_fps_entry = in_fps_entry;
1223
1224   if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc,
1225           &self->current_output_timecode))
1226     goto out;
1227
1228   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1229   out_len = (guint) out.size;
1230   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, out.data, &out_len);
1231
1232   gst_buffer_unmap (outbuf, &out);
1233   self->output_frames++;
1234
1235   if (self->current_output_timecode.config.fps_n != 0 && !tc_meta) {
1236     gst_buffer_add_video_time_code_meta (outbuf,
1237         &self->current_output_timecode);
1238     gst_video_time_code_increment_frame (&self->current_output_timecode);
1239   }
1240
1241 out:
1242   gst_buffer_set_size (outbuf, out_len);
1243
1244   return GST_FLOW_OK;
1245 }
1246
1247 static GstFlowReturn
1248 convert_cea708_cdp_cea708_cdp (GstCCConverter * self, GstBuffer * inbuf,
1249     GstBuffer * outbuf)
1250 {
1251   GstMapInfo out;
1252   GstVideoTimeCode tc = GST_VIDEO_TIME_CODE_INIT;
1253   const struct cdp_fps_entry *in_fps_entry = NULL, *out_fps_entry;
1254   guint8 cc_data[MAX_CDP_PACKET_LEN];
1255   guint cc_data_len = MAX_CDP_PACKET_LEN;
1256   guint out_len = 0;
1257
1258   if (!push_cdp_buffer (self, inbuf, &tc, &in_fps_entry))
1259     goto out;
1260
1261   out_fps_entry = cdp_fps_entry_from_fps (self->out_fps_n, self->out_fps_d);
1262   if (!out_fps_entry || out_fps_entry->fps_n == 0)
1263     out_fps_entry = in_fps_entry;
1264
1265   if (!can_take_buffer (self, in_fps_entry, out_fps_entry, &tc,
1266           &self->current_output_timecode))
1267     goto out;
1268
1269   cc_buffer_take_cc_data (self->cc_buffer, out_fps_entry, cc_data,
1270       &cc_data_len);
1271
1272   gst_buffer_map (outbuf, &out, GST_MAP_WRITE);
1273   out_len =
1274       convert_cea708_cc_data_cea708_cdp_internal (self, cc_data, cc_data_len,
1275       out.data, out.size, &self->current_output_timecode, out_fps_entry);
1276
1277   gst_buffer_unmap (outbuf, &out);
1278   self->output_frames++;
1279
1280 out:
1281   gst_buffer_set_size (outbuf, out_len);
1282
1283   return GST_FLOW_OK;
1284 }
1285
1286 static GstFlowReturn
1287 gst_cc_converter_transform (GstCCConverter * self, GstBuffer * inbuf,
1288     GstBuffer * outbuf)
1289 {
1290   GstVideoTimeCodeMeta *tc_meta = NULL;
1291   GstFlowReturn ret = GST_FLOW_OK;
1292
1293   GST_DEBUG_OBJECT (self, "Converting %" GST_PTR_FORMAT " from %u to %u", inbuf,
1294       self->input_caption_type, self->output_caption_type);
1295
1296   if (inbuf)
1297     tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
1298
1299   if (tc_meta) {
1300     if (self->current_output_timecode.config.fps_n <= 0) {
1301       /* XXX: this assumes the input time codes are well-formed and increase
1302        * at the rate of one frame for each input buffer */
1303       const struct cdp_fps_entry *in_fps_entry;
1304       gint scale_n, scale_d;
1305
1306       in_fps_entry = cdp_fps_entry_from_fps (self->in_fps_n, self->in_fps_d);
1307       if (!in_fps_entry || in_fps_entry->fps_n == 0)
1308         scale_n = scale_d = 1;
1309       else
1310         get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
1311
1312       interpolate_time_code_with_framerate (self, &tc_meta->tc,
1313           self->out_fps_n, self->out_fps_d, scale_n, scale_d,
1314           &self->current_output_timecode);
1315     }
1316   }
1317
1318   switch (self->input_caption_type) {
1319     case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1320
1321       switch (self->output_caption_type) {
1322         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1323           ret = convert_cea608_raw_cea608_s334_1a (self, inbuf, outbuf);
1324           break;
1325         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1326           ret = convert_cea608_raw_cea708_cc_data (self, inbuf, outbuf);
1327           break;
1328         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1329           ret = convert_cea608_raw_cea708_cdp (self, inbuf, outbuf, tc_meta);
1330           break;
1331         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1332         default:
1333           g_assert_not_reached ();
1334           break;
1335       }
1336
1337       break;
1338     case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1339
1340       switch (self->output_caption_type) {
1341         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1342           ret = convert_cea608_s334_1a_cea608_raw (self, inbuf, outbuf);
1343           break;
1344         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1345           ret = convert_cea608_s334_1a_cea708_cc_data (self, inbuf, outbuf);
1346           break;
1347         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1348           ret =
1349               convert_cea608_s334_1a_cea708_cdp (self, inbuf, outbuf, tc_meta);
1350           break;
1351         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1352         default:
1353           g_assert_not_reached ();
1354           break;
1355       }
1356
1357       break;
1358     case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1359
1360       switch (self->output_caption_type) {
1361         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1362           ret = convert_cea708_cc_data_cea608_raw (self, inbuf, outbuf);
1363           break;
1364         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1365           ret = convert_cea708_cc_data_cea608_s334_1a (self, inbuf, outbuf);
1366           break;
1367         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1368           ret =
1369               convert_cea708_cc_data_cea708_cdp (self, inbuf, outbuf, tc_meta);
1370           break;
1371         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1372         default:
1373           g_assert_not_reached ();
1374           break;
1375       }
1376
1377       break;
1378     case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1379
1380       switch (self->output_caption_type) {
1381         case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
1382           ret = convert_cea708_cdp_cea608_raw (self, inbuf, outbuf, tc_meta);
1383           break;
1384         case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:
1385           ret =
1386               convert_cea708_cdp_cea608_s334_1a (self, inbuf, outbuf, tc_meta);
1387           break;
1388         case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
1389           ret =
1390               convert_cea708_cdp_cea708_cc_data (self, inbuf, outbuf, tc_meta);
1391           break;
1392         case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
1393           ret = convert_cea708_cdp_cea708_cdp (self, inbuf, outbuf);
1394           break;
1395         default:
1396           g_assert_not_reached ();
1397           break;
1398       }
1399
1400       break;
1401     default:
1402       g_assert_not_reached ();
1403       break;
1404   }
1405
1406   if (ret != GST_FLOW_OK) {
1407     GST_DEBUG_OBJECT (self, "returning %s", gst_flow_get_name (ret));
1408     return ret;
1409   }
1410
1411   GST_DEBUG_OBJECT (self, "Converted to %" GST_PTR_FORMAT, outbuf);
1412
1413   if (gst_buffer_get_size (outbuf) > 0) {
1414     if (self->current_output_timecode.config.fps_n > 0) {
1415       gst_buffer_add_video_time_code_meta (outbuf,
1416           &self->current_output_timecode);
1417       gst_video_time_code_increment_frame (&self->current_output_timecode);
1418     }
1419
1420     return GST_FLOW_OK;
1421   } else {
1422     return GST_FLOW_OK;
1423   }
1424 }
1425
1426 static gboolean
1427 gst_cc_converter_transform_meta (GstBaseTransform * base, GstBuffer * outbuf,
1428     GstMeta * meta, GstBuffer * inbuf)
1429 {
1430   const GstMetaInfo *info = meta->info;
1431
1432   /* we do this manually for framerate scaling */
1433   if (info->api == GST_VIDEO_TIME_CODE_META_API_TYPE)
1434     return FALSE;
1435
1436   return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (base, outbuf,
1437       meta, inbuf);
1438 }
1439
1440 static gboolean
1441 can_generate_output (GstCCConverter * self)
1442 {
1443   int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
1444   int output_time_cmp;
1445
1446   if (self->in_fps_n == 0 || self->out_fps_n == 0)
1447     return FALSE;
1448
1449   /* compute the relative frame count for each */
1450   if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
1451           self->input_frames, 1, &input_frame_n, &input_frame_d))
1452     /* we should never overflow */
1453     g_assert_not_reached ();
1454
1455   if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
1456           self->output_frames, 1, &output_frame_n, &output_frame_d))
1457     /* we should never overflow */
1458     g_assert_not_reached ();
1459
1460   output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
1461       output_frame_n, output_frame_d);
1462
1463   if (output_time_cmp == 0) {
1464     self->output_frames = 0;
1465     self->input_frames = 0;
1466   }
1467
1468   /* if the next output frame is at or before the current input frame */
1469   if (output_time_cmp >= 0)
1470     return TRUE;
1471
1472   return FALSE;
1473 }
1474
1475 static void
1476 reset_counters (GstCCConverter * self)
1477 {
1478   self->input_frames = 0;
1479   self->output_frames = 1;
1480   gst_video_time_code_clear (&self->current_output_timecode);
1481   gst_clear_buffer (&self->previous_buffer);
1482   cc_buffer_discard (self->cc_buffer);
1483 }
1484
1485 static GstFlowReturn
1486 drain_input (GstCCConverter * self)
1487 {
1488   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (self);
1489   GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
1490   GstFlowReturn ret = GST_FLOW_OK;
1491   guint cea608_1_len, cea608_2_len, ccp_len;
1492
1493   cc_buffer_get_stored_size (self->cc_buffer, &cea608_1_len, &cea608_2_len,
1494       &ccp_len);
1495
1496   while (ccp_len > 0 || cea608_1_len > 0 || cea608_2_len > 0
1497       || can_generate_output (self)) {
1498     GstBuffer *outbuf;
1499
1500     if (!self->previous_buffer) {
1501       GST_WARNING_OBJECT (self, "Attempt to draining without a previous "
1502           "buffer.  Aborting");
1503       return GST_FLOW_OK;
1504     }
1505
1506     outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
1507
1508     if (bclass->copy_metadata) {
1509       if (!bclass->copy_metadata (trans, self->previous_buffer, outbuf)) {
1510         /* something failed, post a warning */
1511         GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
1512             ("could not copy metadata"), (NULL));
1513       }
1514     }
1515
1516     ret = gst_cc_converter_transform (self, NULL, outbuf);
1517     cc_buffer_get_stored_size (self->cc_buffer, &cea608_1_len, &cea608_2_len,
1518         &ccp_len);
1519     if (gst_buffer_get_size (outbuf) <= 0) {
1520       /* try to move the output along */
1521       self->input_frames++;
1522       gst_buffer_unref (outbuf);
1523       continue;
1524     } else if (ret != GST_FLOW_OK) {
1525       gst_buffer_unref (outbuf);
1526       return ret;
1527     }
1528
1529     ret = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (trans), outbuf);
1530     if (ret != GST_FLOW_OK) {
1531       return ret;
1532     }
1533   }
1534
1535   return ret;
1536 }
1537
1538 static GstFlowReturn
1539 gst_cc_converter_generate_output (GstBaseTransform * base, GstBuffer ** outbuf)
1540 {
1541   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (base);
1542   GstCCConverter *self = GST_CCCONVERTER (base);
1543   GstBuffer *inbuf = base->queued_buf;
1544   GstFlowReturn ret;
1545
1546   *outbuf = NULL;
1547   base->queued_buf = NULL;
1548   if (!inbuf && !can_generate_output (self)) {
1549     return GST_FLOW_OK;
1550   }
1551
1552   if (gst_base_transform_is_passthrough (base)) {
1553     *outbuf = inbuf;
1554     ret = GST_FLOW_OK;
1555   } else {
1556     if (inbuf && GST_BUFFER_IS_DISCONT (inbuf)) {
1557       ret = drain_input (self);
1558       reset_counters (self);
1559       if (ret != GST_FLOW_OK)
1560         return ret;
1561     }
1562
1563     *outbuf = gst_buffer_new_allocate (NULL, MAX_CDP_PACKET_LEN, NULL);
1564     if (*outbuf == NULL)
1565       goto no_buffer;
1566
1567     if (inbuf)
1568       gst_buffer_replace (&self->previous_buffer, inbuf);
1569
1570     if (bclass->copy_metadata) {
1571       if (!bclass->copy_metadata (base, self->previous_buffer, *outbuf)) {
1572         /* something failed, post a warning */
1573         GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED,
1574             ("could not copy metadata"), (NULL));
1575       }
1576     }
1577
1578     ret = gst_cc_converter_transform (self, inbuf, *outbuf);
1579     if (gst_buffer_get_size (*outbuf) <= 0) {
1580       gst_buffer_unref (*outbuf);
1581       *outbuf = NULL;
1582       ret = GST_FLOW_OK;
1583     }
1584
1585     if (inbuf)
1586       gst_buffer_unref (inbuf);
1587   }
1588
1589   return ret;
1590
1591 no_buffer:
1592   {
1593     if (inbuf)
1594       gst_buffer_unref (inbuf);
1595     *outbuf = NULL;
1596     GST_WARNING_OBJECT (self, "could not allocate buffer");
1597     return GST_FLOW_ERROR;
1598   }
1599 }
1600
1601 static gboolean
1602 gst_cc_converter_sink_event (GstBaseTransform * trans, GstEvent * event)
1603 {
1604   GstCCConverter *self = GST_CCCONVERTER (trans);
1605
1606   switch (GST_EVENT_TYPE (event)) {
1607     case GST_EVENT_EOS:
1608       GST_DEBUG_OBJECT (self, "received EOS");
1609
1610       drain_input (self);
1611
1612       /* fallthrough */
1613     case GST_EVENT_FLUSH_START:
1614       reset_counters (self);
1615       break;
1616     default:
1617       break;
1618   }
1619
1620   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
1621 }
1622
1623 static gboolean
1624 gst_cc_converter_start (GstBaseTransform * base)
1625 {
1626   GstCCConverter *self = GST_CCCONVERTER (base);
1627
1628   /* Resetting this is not really needed but makes debugging easier */
1629   self->cdp_hdr_sequence_cntr = 0;
1630   self->current_output_timecode = (GstVideoTimeCode) GST_VIDEO_TIME_CODE_INIT;
1631   reset_counters (self);
1632
1633   return TRUE;
1634 }
1635
1636 static gboolean
1637 gst_cc_converter_stop (GstBaseTransform * base)
1638 {
1639   GstCCConverter *self = GST_CCCONVERTER (base);
1640
1641   gst_video_time_code_clear (&self->current_output_timecode);
1642   gst_clear_buffer (&self->previous_buffer);
1643
1644   return TRUE;
1645 }
1646
1647 static void
1648 gst_cc_converter_set_property (GObject * object, guint prop_id,
1649     const GValue * value, GParamSpec * pspec)
1650 {
1651   GstCCConverter *filter = GST_CCCONVERTER (object);
1652
1653   switch (prop_id) {
1654     case PROP_CDP_MODE:
1655       filter->cdp_mode = g_value_get_flags (value);
1656       break;
1657     default:
1658       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1659       break;
1660   }
1661 }
1662
1663 static void
1664 gst_cc_converter_get_property (GObject * object, guint prop_id, GValue * value,
1665     GParamSpec * pspec)
1666 {
1667   GstCCConverter *filter = GST_CCCONVERTER (object);
1668
1669   switch (prop_id) {
1670     case PROP_CDP_MODE:
1671       g_value_set_flags (value, filter->cdp_mode);
1672       break;
1673     default:
1674       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1675       break;
1676   }
1677 }
1678
1679 static void
1680 gst_cc_converter_finalize (GObject * object)
1681 {
1682   GstCCConverter *self = GST_CCCONVERTER (object);
1683
1684   gst_clear_object (&self->cc_buffer);
1685
1686   G_OBJECT_CLASS (parent_class)->finalize (object);
1687 }
1688
1689 static void
1690 gst_cc_converter_class_init (GstCCConverterClass * klass)
1691 {
1692   GObjectClass *gobject_class;
1693   GstElementClass *gstelement_class;
1694   GstBaseTransformClass *basetransform_class;
1695
1696   gobject_class = (GObjectClass *) klass;
1697   gstelement_class = (GstElementClass *) klass;
1698   basetransform_class = (GstBaseTransformClass *) klass;
1699
1700   gobject_class->set_property = gst_cc_converter_set_property;
1701   gobject_class->get_property = gst_cc_converter_get_property;
1702   gobject_class->finalize = gst_cc_converter_finalize;
1703
1704   /**
1705    * GstCCConverter:cdp-mode
1706    *
1707    * Only insert the selection sections into CEA 708 CDP packets.
1708    *
1709    * Various software does not handle any other information than CC data
1710    * contained in CDP packets and might fail parsing the packets otherwise.
1711    *
1712    * Since: 1.20
1713    */
1714   g_object_class_install_property (G_OBJECT_CLASS (klass),
1715       PROP_CDP_MODE, g_param_spec_flags ("cdp-mode",
1716           "CDP Mode",
1717           "Select which CDP sections to store in CDP packets",
1718           GST_TYPE_CC_CONVERTER_CDP_MODE, DEFAULT_CDP_MODE,
1719           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1720
1721   gst_element_class_set_static_metadata (gstelement_class,
1722       "Closed Caption Converter",
1723       "Filter/ClosedCaption",
1724       "Converts Closed Captions between different formats",
1725       "Sebastian Dröge <sebastian@centricular.com>");
1726
1727   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
1728   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
1729
1730   basetransform_class->start = GST_DEBUG_FUNCPTR (gst_cc_converter_start);
1731   basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_cc_converter_stop);
1732   basetransform_class->sink_event =
1733       GST_DEBUG_FUNCPTR (gst_cc_converter_sink_event);
1734   basetransform_class->transform_size =
1735       GST_DEBUG_FUNCPTR (gst_cc_converter_transform_size);
1736   basetransform_class->transform_caps =
1737       GST_DEBUG_FUNCPTR (gst_cc_converter_transform_caps);
1738   basetransform_class->fixate_caps =
1739       GST_DEBUG_FUNCPTR (gst_cc_converter_fixate_caps);
1740   basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_cc_converter_set_caps);
1741   basetransform_class->transform_meta =
1742       GST_DEBUG_FUNCPTR (gst_cc_converter_transform_meta);
1743   basetransform_class->generate_output =
1744       GST_DEBUG_FUNCPTR (gst_cc_converter_generate_output);
1745   basetransform_class->passthrough_on_same_caps = TRUE;
1746
1747   GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
1748       0, "Closed Caption converter");
1749
1750   gst_type_mark_as_plugin_api (GST_TYPE_CC_CONVERTER_CDP_MODE, 0);
1751 }
1752
1753 static void
1754 gst_cc_converter_init (GstCCConverter * self)
1755 {
1756   self->cdp_mode = DEFAULT_CDP_MODE;
1757   self->cc_buffer = cc_buffer_new ();
1758 }