ccconverter: Add property to specify which sections to include in CDP packets
authorSebastian Dröge <sebastian@centricular.com>
Wed, 25 Nov 2020 14:24:25 +0000 (16:24 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Mon, 7 Dec 2020 17:23:42 +0000 (19:23 +0200)
Various software, including ffmpeg's Decklink support, fails parsing CDP
packets that contain anything but CC data in the CDP packets.

Based on this property, timecodes are not written into the CDP packets
even if they're present.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1833>

docs/plugins/gst_plugins_cache.json
ext/closedcaption/gstccconverter.c
ext/closedcaption/gstccconverter.h

index 5248eb8..71d8f72 100644 (file)
                         "presence": "always"
                     }
                 },
-                "properties": {},
+                "properties": {
+                    "cdp-mode": {
+                        "blurb": "Select which CDP sections to store in CDP packets",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "cc-svc-info+cc-data+time-code",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstCCConverterCDPMode",
+                        "writable": true
+                    }
+                },
                 "rank": "none"
             },
             "ccextractor": {
         "filename": "gstclosedcaption",
         "license": "LGPL",
         "other-types": {
+            "GstCCConverterCDPMode": {
+                "kind": "flags",
+                "values": [
+                    {
+                        "desc": "Store time code information in CDP packets",
+                        "name": "time-code",
+                        "value": "0x00000001"
+                    },
+                    {
+                        "desc": "Store CC data in CDP packets",
+                        "name": "cc-data",
+                        "value": "0x00000002"
+                    },
+                    {
+                        "desc": "Store CC service information in CDP packets",
+                        "name": "cc-svc-info",
+                        "value": "0x00000004"
+                    }
+                ]
+            },
             "GstCeaCcOverlayWinHPos": {
                 "kind": "enum",
                 "values": [
index 43073fb..fe6aadd 100644 (file)
 GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
 #define GST_CAT_DEFAULT gst_cc_converter_debug
 
+/**
+ * GstCCConverterCDPMode:
+ * @GST_CC_CONVERTER_CDP_MODE_TIME_CODE: Store time code information in CDP packets
+ * @GST_CC_CONVERTER_CDP_MODE_CC_DATA: Store CC data in CDP packets
+ * @GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO: Store CC service information in CDP packets
+ *
+ * Since: 1.20
+ */
+
+enum
+{
+  PROP_0,
+  PROP_CDP_MODE,
+};
+
+#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)
+
 /* Ordered by the amount of information they can contain */
 #define CC_CAPS \
         "closedcaption/x-cea-708,format=(string) cdp; " \
@@ -55,6 +72,32 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
 G_DEFINE_TYPE (GstCCConverter, gst_cc_converter, GST_TYPE_BASE_TRANSFORM);
 #define parent_class gst_cc_converter_parent_class
 
+#define GST_TYPE_CC_CONVERTER_CDP_MODE (gst_cc_converter_cdp_mode_get_type())
+static GType
+gst_cc_converter_cdp_mode_get_type (void)
+{
+  static const GFlagsValue values[] = {
+    {GST_CC_CONVERTER_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",
+        "cc-data"},
+    {GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO,
+        "Store CC service information in CDP packets", "cc-svc-info"},
+    {0, NULL, NULL}
+  };
+  static volatile GType id = 0;
+
+  if (g_once_init_enter ((gsize *) & id)) {
+    GType _id;
+
+    _id = g_flags_register_static ("GstCCConverterCDPMode", values);
+
+    g_once_init_leave ((gsize *) & id, _id);
+  }
+
+  return id;
+}
+
 static gboolean
 gst_cc_converter_transform_size (GstBaseTransform * base,
     GstPadDirection direction,
@@ -1001,11 +1044,16 @@ convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
     cc_data_len = 3 * fps_entry->max_cc_count;
   }
 
-  /* ccdata_present | caption_service_active */
-  flags = 0x42;
+  /* caption_service_active */
+  flags = 0x02;
+
+  /* ccdata_present */
+  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA))
+    flags |= 0x40;
 
   /* time_code_present */
-  if (tc && tc->config.fps_n > 0)
+  if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
+      && tc->config.fps_n > 0)
     flags |= 0x80;
 
   /* reserved */
@@ -1015,7 +1063,8 @@ convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
 
   gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
 
-  if (tc && tc->config.fps_n > 0) {
+  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);
@@ -1054,14 +1103,16 @@ convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
     gst_byte_writer_put_uint8_unchecked (&bw, u8);
   }
 
-  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;
+  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;
+    }
   }
 
   gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
@@ -2436,14 +2487,68 @@ gst_cc_converter_stop (GstBaseTransform * base)
 }
 
 static void
+gst_cc_converter_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstCCConverter *filter = GST_CCCONVERTER (object);
+
+  switch (prop_id) {
+    case PROP_CDP_MODE:
+      filter->cdp_mode = g_value_get_flags (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_cc_converter_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstCCConverter *filter = GST_CCCONVERTER (object);
+
+  switch (prop_id) {
+    case PROP_CDP_MODE:
+      g_value_set_flags (value, filter->cdp_mode);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
 gst_cc_converter_class_init (GstCCConverterClass * klass)
 {
+  GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
   GstBaseTransformClass *basetransform_class;
 
+  gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
   basetransform_class = (GstBaseTransformClass *) klass;
 
+  gobject_class->set_property = gst_cc_converter_set_property;
+  gobject_class->get_property = gst_cc_converter_get_property;
+
+  /**
+   * GstCCConverter:cdp-mode
+   *
+   * Only insert the selection sections into CEA 708 CDP packets.
+   *
+   * Various software does not handle any other information than CC data
+   * contained in CDP packets and might fail parsing the packets otherwise.
+   *
+   * Since: 1.20
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_CDP_MODE, g_param_spec_flags ("cdp-mode",
+          "CDP Mode",
+          "Select which CDP sections to store in CDP packets",
+          GST_TYPE_CC_CONVERTER_CDP_MODE, DEFAULT_CDP_MODE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_set_static_metadata (gstelement_class,
       "Closed Caption Converter",
       "Filter/ClosedCaption",
@@ -2472,9 +2577,12 @@ gst_cc_converter_class_init (GstCCConverterClass * klass)
 
   GST_DEBUG_CATEGORY_INIT (gst_cc_converter_debug, "ccconverter",
       0, "Closed Caption converter");
+
+  gst_type_mark_as_plugin_api (GST_TYPE_CC_CONVERTER_CDP_MODE, 0);
 }
 
 static void
 gst_cc_converter_init (GstCCConverter * self)
 {
+  self->cdp_mode = DEFAULT_CDP_MODE;
 }
index 7ed6e42..7529889 100644 (file)
@@ -43,10 +43,18 @@ typedef struct _GstCCConverterClass GstCCConverterClass;
 #define MAX_CDP_PACKET_LEN 256
 #define MAX_CEA608_LEN 32
 
+typedef enum {
+  GST_CC_CONVERTER_CDP_MODE_TIME_CODE   = (1<<0),
+  GST_CC_CONVERTER_CDP_MODE_CC_DATA     = (1<<1),
+  GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO = (1<<2)
+} GstCCConverterCDPMode;
+
 struct _GstCCConverter
 {
   GstBaseTransform parent;
 
+  GstCCConverterCDPMode cdp_mode;
+
   GstVideoCaptionType input_caption_type;
   GstVideoCaptionType output_caption_type;