closedcaption: move cc_data->cdp to shared file
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / closedcaption / gstccconverter.c
index 66913ab..9fcbc89 100644 (file)
@@ -28,6 +28,7 @@
 #include <gst/video/video.h>
 #include <string.h>
 
+#include "ccutils.h"
 #include "gstccconverter.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
@@ -79,11 +80,11 @@ static GType
 gst_cc_converter_cdp_mode_get_type (void)
 {
   static const GFlagsValue values[] = {
-    {GST_CC_CONVERTER_CDP_MODE_TIME_CODE,
+    {GST_CC_CDP_MODE_TIME_CODE,
         "Store time code information in CDP packets", "time-code"},
-    {GST_CC_CONVERTER_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
+    {GST_CC_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
         "cc-data"},
-    {GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO,
+    {GST_CC_CDP_MODE_CC_SVC_INFO,
         "Store CC service information in CDP packets", "cc-svc-info"},
     {0, NULL, NULL}
   };
@@ -471,49 +472,6 @@ invalid_caps:
   }
 }
 
-struct cdp_fps_entry
-{
-  guint8 fps_idx;
-  guint fps_n, fps_d;
-  guint max_cc_count;
-  guint max_ccp_count;
-  guint max_cea608_count;
-};
-
-static const struct cdp_fps_entry cdp_fps_table[] = {
-  {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
-  {0x2f, 24, 1, 25, 22, 2},
-  {0x3f, 25, 1, 24, 22, 2},
-  {0x4f, 30000, 1001, 20, 18, 2},
-  {0x5f, 30, 1, 20, 18, 2},
-  {0x6f, 50, 1, 12, 11, 1},
-  {0x7f, 60000, 1001, 10, 9, 1},
-  {0x8f, 60, 1, 10, 9, 1},
-};
-static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
-
-static const struct cdp_fps_entry *
-cdp_fps_entry_from_id (guint8 id)
-{
-  int i;
-  for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
-    if (cdp_fps_table[i].fps_idx == id)
-      return &cdp_fps_table[i];
-  }
-  return &null_fps_entry;
-}
-
-static const struct cdp_fps_entry *
-cdp_fps_entry_from_fps (guint fps_n, guint fps_d)
-{
-  int i;
-  for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
-    if (cdp_fps_table[i].fps_n == fps_n && cdp_fps_table[i].fps_d == fps_d)
-      return &cdp_fps_table[i];
-  }
-  return &null_fps_entry;
-}
-
 static void
 get_framerate_output_scale (GstCCConverter * self,
     const struct cdp_fps_entry *in_fps_entry, gint * scale_n, gint * scale_d)
@@ -647,6 +605,9 @@ compact_cc_data (guint8 * cc_data, guint cc_data_len)
   return out_len;
 }
 
+#define CC_DATA_EXTRACT_TOO_MANY_FIELD1 -2
+#define CC_DATA_EXTRACT_TOO_MANY_FIELD2 -3
+
 static gint
 cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
     guint8 * cea608_field1, guint * cea608_field1_len,
@@ -687,7 +648,7 @@ cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
         if (*cea608_field1_len + 2 > field_1_len) {
           GST_WARNING ("Too many cea608 input bytes %u for field 1",
               *cea608_field1_len + 2);
-          return -1;
+          return CC_DATA_EXTRACT_TOO_MANY_FIELD1;
         }
 
         if (byte1 != 0x80 || byte2 != 0x80) {
@@ -703,7 +664,7 @@ cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
         if (*cea608_field2_len + 2 > field_2_len) {
           GST_WARNING ("Too many cea608 input bytes %u for field 2",
               *cea608_field2_len + 2);
-          return -1;
+          return CC_DATA_EXTRACT_TOO_MANY_FIELD2;
         }
         if (byte1 != 0x80 || byte2 != 0x80) {
           cea608_field2[(*cea608_field2_len)++] = byte1;
@@ -979,32 +940,32 @@ fit_and_scale_cc_data (GstCCConverter * self,
 
   /* This is slightly looser than checking for the exact framerate as the cdp
    * spec allow for 0.1% difference between framerates to be considered equal */
-  if (in_fps_entry->max_cc_count == out_fps_entry->max_cc_count) {
-    if (tc && tc->config.fps_n != 0)
-      interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
-          out_fps_entry->fps_d, 1, 1, &self->current_output_timecode);
-
-    self->scratch_ccp_len = 0;
-    self->scratch_cea608_1_len = 0;
-    self->scratch_cea608_2_len = 0;
-    self->input_frames = 0;
-    self->output_frames = 0;
-  } else {
+  {
     int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
-    int output_time_cmp, scale_n, scale_d, rate_cmp;
+    int output_time_cmp, scale_n, scale_d;
 
     /* TODO: handle input discont */
 
-    /* compute the relative frame count for each */
-    if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
-            self->input_frames, 1, &input_frame_n, &input_frame_d))
-      /* we should never overflow */
-      g_assert_not_reached ();
+    if (self->in_fps_n == 0) {
+      input_frame_n = self->input_frames;
+      input_frame_d = 1;
+    } else {
+      /* compute the relative frame count for each */
+      if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
+              self->input_frames, 1, &input_frame_n, &input_frame_d))
+        /* we should never overflow */
+        g_assert_not_reached ();
+    }
 
-    if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
-            self->output_frames, 1, &output_frame_n, &output_frame_d))
-      /* we should never overflow */
-      g_assert_not_reached ();
+    if (self->in_fps_n == 0) {
+      output_frame_n = self->output_frames;
+      output_frame_d = 1;
+    } else {
+      if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
+              self->output_frames, 1, &output_frame_n, &output_frame_d))
+        /* we should never overflow */
+        g_assert_not_reached ();
+    }
 
     output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
         output_frame_n, output_frame_d);
@@ -1012,18 +973,12 @@ fit_and_scale_cc_data (GstCCConverter * self,
     /* compute the relative rates of the two framerates */
     get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
 
-    rate_cmp = gst_util_fraction_compare (scale_n, scale_d, 1, 1);
-
-    GST_TRACE_OBJECT (self, "performing framerate conversion at scale %d/%d "
+    GST_TRACE_OBJECT (self, "performing conversion at scale %d/%d "
         "of cc data of with sizes, ccp:%u, cea608-1:%u, cea608-2:%u", scale_n,
         scale_d, VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
         VAL_OR_0 (cea608_2_len));
 
-    if (rate_cmp == 0) {
-      /* we are not scaling. Should never happen with current conditions
-       * above */
-      g_assert_not_reached ();
-    } else if (output_time_cmp < 0) {
+    if (output_time_cmp < 0) {
       /* we can't generate an output yet */
       guint cd_len = ccp_data_len ? *ccp_data_len : 0;
       guint c1_len = cea608_1_len ? *cea608_1_len : 0;
@@ -1038,7 +993,7 @@ fit_and_scale_cc_data (GstCCConverter * self,
       if (cea608_2_len)
         *cea608_2_len = 0;
       return FALSE;
-    } else if (rate_cmp != 0) {
+    } else {
       /* we are changing the framerate and may overflow the max output packet
        * size. Split them where necessary. */
       gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0;
@@ -1130,8 +1085,6 @@ fit_and_scale_cc_data (GstCCConverter * self,
         self->scratch_cea608_1_len = 0;
         self->scratch_cea608_2_len = 0;
       }
-    } else {
-      g_assert_not_reached ();
     }
 
     if (tc && tc->config.fps_n != 0)
@@ -1151,122 +1104,19 @@ fit_and_scale_cc_data (GstCCConverter * self,
   return TRUE;
 }
 
-/* Converts raw CEA708 cc_data and an optional timecode into CDP */
 static guint
 convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
     const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
     const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
 {
-  GstByteWriter bw;
-  guint8 flags, checksum;
-  guint i, len;
-
-  GST_DEBUG_OBJECT (self, "writing out cdp packet from cc_data with length %u",
-      cc_data_len);
-
-  gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
-  gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
-  /* Write a length of 0 for now */
-  gst_byte_writer_put_uint8_unchecked (&bw, 0);
-
-  gst_byte_writer_put_uint8_unchecked (&bw, fps_entry->fps_idx);
-
-  if (cc_data_len / 3 > fps_entry->max_cc_count) {
-    GST_WARNING_OBJECT (self, "Too many cc_data triplets for framerate: %u. "
-        "Truncating to %u", cc_data_len / 3, fps_entry->max_cc_count);
-    cc_data_len = 3 * fps_entry->max_cc_count;
-  }
-
-  /* caption_service_active */
-  flags = 0x02;
-
-  /* ccdata_present */
-  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA))
-    flags |= 0x40;
-
-  /* time_code_present */
-  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
-      && tc->config.fps_n > 0)
-    flags |= 0x80;
-
-  /* reserved */
-  flags |= 0x01;
-
-  gst_byte_writer_put_uint8_unchecked (&bw, flags);
-
-  gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
-
-  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
-      && tc->config.fps_n > 0) {
-    guint8 u8;
-
-    gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
-    /* reserved 11 - 2 bits */
-    u8 = 0xc0;
-    /* tens of hours - 2 bits */
-    u8 |= ((tc->hours / 10) & 0x3) << 4;
-    /* units of hours - 4 bits */
-    u8 |= (tc->hours % 10) & 0xf;
-    gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
-    /* reserved 1 - 1 bit */
-    u8 = 0x80;
-    /* tens of minutes - 3 bits */
-    u8 |= ((tc->minutes / 10) & 0x7) << 4;
-    /* units of minutes - 4 bits */
-    u8 |= (tc->minutes % 10) & 0xf;
-    gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
-    /* field flag - 1 bit */
-    u8 = tc->field_count < 2 ? 0x00 : 0x80;
-    /* tens of seconds - 3 bits */
-    u8 |= ((tc->seconds / 10) & 0x7) << 4;
-    /* units of seconds - 4 bits */
-    u8 |= (tc->seconds % 10) & 0xf;
-    gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
-    /* drop frame flag - 1 bit */
-    u8 = (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
-        0x00;
-    /* reserved0 - 1 bit */
-    /* tens of frames - 2 bits */
-    u8 |= ((tc->frames / 10) & 0x3) << 4;
-    /* units of frames 4 bits */
-    u8 |= (tc->frames % 10) & 0xf;
-    gst_byte_writer_put_uint8_unchecked (&bw, u8);
-  }
-
-  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA)) {
-    gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
-    gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | fps_entry->max_cc_count);
-    gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
-    while (fps_entry->max_cc_count > cc_data_len / 3) {
-      gst_byte_writer_put_uint8_unchecked (&bw, 0xfa);
-      gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
-      gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
-      cc_data_len += 3;
-    }
-  }
+  guint ret;
 
-  gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
-  gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
+  ret = convert_cea708_cc_data_to_cdp (GST_OBJECT (self),
+      (GstCCCDPMode) self->cdp_mode, self->cdp_hdr_sequence_cntr, cc_data,
+      cc_data_len, cdp, cdp_len, tc, fps_entry);
   self->cdp_hdr_sequence_cntr++;
-  /* We calculate the checksum afterwards */
-  gst_byte_writer_put_uint8_unchecked (&bw, 0);
-
-  len = gst_byte_writer_get_pos (&bw);
-  gst_byte_writer_set_pos (&bw, 2);
-  gst_byte_writer_put_uint8_unchecked (&bw, len);
-
-  checksum = 0;
-  for (i = 0; i < len; i++) {
-    checksum += cdp[i];
-  }
-  checksum &= 0xff;
-  checksum = 256 - checksum;
-  cdp[len - 1] = checksum;
 
-  return len;
+  return ret;
 }
 
 /* Converts CDP into raw CEA708 cc_data */
@@ -1371,7 +1221,7 @@ convert_cea708_cdp_cea708_cc_data_internal (GstCCConverter * self,
       return 0;
     }
 
-    drop_frame = ! !(u8 & 0x80);
+    drop_frame = !(!(u8 & 0x80));
     frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
 
     gst_video_time_code_init (tc, fps_entry->fps_n, fps_entry->fps_d, NULL,
@@ -1518,15 +1368,6 @@ cc_data_to_cea608_ccp (GstCCConverter * self, guint8 * cc_data,
     guint new_cea608_1_len = 0, new_cea608_2_len = 0;
     guint8 *new_cea608_1 = cea608_1, *new_cea608_2 = cea608_2;
 
-    if (cea608_1_len) {
-      new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
-      new_cea608_1 = &cea608_1[*cea608_1_len];
-    }
-    if (cea608_2_len) {
-      new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
-      new_cea608_2 = &cea608_2[*cea608_2_len];
-    }
-
     cc_data_len = compact_cc_data (cc_data, cc_data_len);
 
     if (cc_data_len / 3 > in_fps_entry->max_cc_count) {
@@ -1535,11 +1376,34 @@ cc_data_to_cea608_ccp (GstCCConverter * self, guint8 * cc_data,
       cc_data_len = 3 * in_fps_entry->max_cc_count;
     }
 
-    ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
-        &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
-    if (ccp_offset < 0) {
-      GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
-      goto fail;
+    while (TRUE) {
+      if (cea608_1_len) {
+        new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
+        new_cea608_1 = &cea608_1[*cea608_1_len];
+      }
+      if (cea608_2_len) {
+        new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
+        new_cea608_2 = &cea608_2[*cea608_2_len];
+      }
+
+      ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
+          &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
+
+      if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD1 && cea608_1_len) {
+        GST_WARNING_OBJECT (self, "cea608 field 1 overflow, dropping all "
+            "previously stored field 1 data and trying again");
+        *cea608_1_len = 0;
+      } else if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD2 && cea608_2_len) {
+        GST_WARNING_OBJECT (self, "cea608 field 2 overflow, dropping all "
+            "previously stored field 2 data and trying again");
+        *cea608_2_len = 0;
+      } else if (ccp_offset < 0) {
+        GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
+        goto fail;
+      } else {
+        /* success */
+        break;
+      }
     }
 
     if ((new_cea608_1_len + new_cea608_2_len) / 2 >