tsmux: Simplify Opus caps parsing by using codecutils helpers
authorSebastian Dröge <sebastian@centricular.com>
Tue, 3 Nov 2015 17:51:03 +0000 (19:51 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 3 Nov 2015 18:35:41 +0000 (20:35 +0200)
https://bugzilla.gnome.org/show_bug.cgi?id=757152

gst/mpegtsdemux/tsdemux.c
gst/mpegtsmux/Makefile.am
gst/mpegtsmux/mpegtsmux.c

index bcc3018..7e22c35 100644 (file)
@@ -1229,123 +1229,105 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
                 {0, 1, 2, 3, 4, 5, 6, 7},
               };
 
-              guint8 codecdata[22 + 256] = {
-                'O', 'p', 'u', 's',
-                'H', 'e', 'a', 'd',
-                1, 0, 0, 0,
-                0, 0, 0, 0,
-                0, 0, 0, 0,
-                0, 0, 0, 0,
-                0, 0, 0, 0,
-                0, 0,
-              };
-              GstBuffer *codec_data_buf;
-              guint channels;
-              GValue v_arr = G_VALUE_INIT;
-              GValue v_buf = G_VALUE_INIT;
-              GstTagList *tags;
-              gint codecdata_len = -1;
+              gint channels = -1, stream_count, coupled_count, mapping_family;
+              guint8 *channel_mapping = NULL;
 
               channels = channel_config_code ? (channel_config_code & 0x0f) : 2;
-              codecdata[9] = channels;
               if (channel_config_code == 0 || channel_config_code == 0x80) {
                 /* Dual Mono */
-                codecdata[18] = 255;
+                mapping_family = 255;
                 if (channel_config_code == 0) {
-                  codecdata[19] = 1;
-                  codecdata[20] = 1;
+                  stream_count = 1;
+                  coupled_count = 1;
                 } else {
-                  codecdata[19] = 2;
-                  codecdata[20] = 0;
+                  stream_count = 2;
+                  coupled_count = 0;
                 }
-                memcpy (&codecdata[21], channel_map_a[1], channels);
-                codecdata_len = 24;
+                channel_mapping = g_new0 (guint8, channels);
+                memcpy (channel_mapping, &channel_map_a[1], channels);
               } else if (channel_config_code <= 8) {
-                codecdata[18] = (channels > 2) ? 1 : 0;
-                codecdata[19] =
+                mapping_family = (channels > 2) ? 1 : 0;
+                stream_count =
                     channel_config_code -
                     coupled_stream_counts[channel_config_code];
-                codecdata[20] = coupled_stream_counts[channel_config_code];
-                memcpy (&codecdata[21], channel_map_a[channels - 1], channels);
-                if (codecdata[18] == 0)
-                  codecdata_len = 19;
-                else
-                  codecdata_len = 21 + channels;
+                coupled_count = coupled_stream_counts[channel_config_code];
+                if (mapping_family != 0) {
+                  channel_mapping = g_new0 (guint8, channels);
+                  memcpy (channel_mapping, &channel_map_a[channels - 1],
+                      channels);
+                }
               } else if (channel_config_code >= 0x82
                   && channel_config_code <= 0x88) {
-                codecdata[18] = 1;
-                codecdata[19] = channels;
-                codecdata[20] = 0;
-                memcpy (&codecdata[21], channel_map_b[channels - 1], channels);
-                codecdata_len = 21 + channels;
+                mapping_family = 1;
+                stream_count = channels;
+                coupled_count = 0;
+                channel_mapping = g_new0 (guint8, channels);
+                memcpy (channel_mapping, &channel_map_b[channels - 1],
+                    channels);
               } else if (channel_config_code == 0x81) {
-                guint8 channel_count, mapping_family;
-
                 if (gst_byte_reader_get_remaining (&br) < 2) {
                   GST_WARNING_OBJECT (demux,
                       "Invalid Opus descriptor with extended channel configuration");
+                  channels = -1;
                   break;
                 }
 
-                channel_count = gst_byte_reader_get_uint8_unchecked (&br);
+                channels = gst_byte_reader_get_uint8_unchecked (&br);
                 mapping_family = gst_byte_reader_get_uint8_unchecked (&br);
 
                 /* Overwrite values from above */
-                if (channel_count == 0) {
+                if (channels == 0) {
                   GST_WARNING_OBJECT (demux,
                       "Invalid Opus descriptor with extended channel configuration");
+                  channels = -1;
                   break;
                 }
 
-                channels = channel_count;
-                codecdata[9] = channels;
-                if (mapping_family == 0 && channel_count <= 2) {
-                  codecdata[18] = 0;
-                  codecdata[19] =
-                      channel_count - coupled_stream_counts[channel_count];
-                  codecdata[20] = coupled_stream_counts[channel_count];
-                  codecdata_len = 19;
+                if (mapping_family == 0 && channels <= 2) {
+                  stream_count = channels - coupled_stream_counts[channels];
+                  coupled_count = coupled_stream_counts[channels];
                 } else {
                   GstBitReader breader;
                   guint8 stream_count_minus_one, coupled_stream_count;
                   gint stream_count_minus_one_len, coupled_stream_count_len;
                   gint channel_mapping_len, i;
 
-                  codecdata[18] = mapping_family;
-
                   gst_bit_reader_init (&breader,
                       gst_byte_reader_get_data_unchecked
                       (&br, gst_byte_reader_get_remaining
                           (&br)), gst_byte_reader_get_remaining (&br));
 
-                  stream_count_minus_one_len = ceil (log2 (channel_count));
+                  stream_count_minus_one_len = ceil (log2 (channels));
                   if (!gst_bit_reader_get_bits_uint8 (&breader,
                           &stream_count_minus_one,
                           stream_count_minus_one_len)) {
                     GST_WARNING_OBJECT (demux,
                         "Invalid Opus descriptor with extended channel configuration");
+                    channels = -1;
                     break;
                   }
 
-                  codecdata[19] = stream_count_minus_one + 1;
+                  stream_count = stream_count_minus_one + 1;
                   coupled_stream_count_len =
-                      ceil (log2 (stream_count_minus_one_len + 2));
+                      ceil (log2 (stream_count_minus_one + 2));
 
                   if (!gst_bit_reader_get_bits_uint8 (&breader,
                           &coupled_stream_count, coupled_stream_count_len)) {
                     GST_WARNING_OBJECT (demux,
                         "Invalid Opus descriptor with extended channel configuration");
+                    channels = -1;
                     break;
                   }
 
-                  codecdata[20] = coupled_stream_count;
+                  coupled_count = coupled_stream_count;
 
                   channel_mapping_len =
                       ceil (log2 (stream_count_minus_one + 1 +
                           coupled_stream_count + 1));
-                  for (i = 0; i < channel_count; i++) {
+                  channel_mapping = g_new0 (guint8, channels);
+                  for (i = 0; i < channels; i++) {
                     if (!gst_bit_reader_get_bits_uint8 (&breader,
-                            &codecdata[21 + i], channel_mapping_len)) {
+                            &channel_mapping[i], channel_mapping_len)) {
                       GST_WARNING_OBJECT (demux,
                           "Invalid Opus descriptor with extended channel configuration");
                       break;
@@ -1353,42 +1335,44 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
                   }
 
                   /* error above */
-                  if (i != channel_count)
+                  if (i != channels) {
+                    channels = -1;
+                    g_free (channel_mapping);
+                    channel_mapping = NULL;
                     break;
-
-                  codecdata_len = 22 + channel_count;
+                  }
                 }
               } else {
                 g_assert_not_reached ();
               }
 
-              if (codecdata_len != -1) {
+              if (channels != -1) {
                 is_audio = TRUE;
                 template = gst_static_pad_template_get (&audio_template);
                 name = g_strdup_printf ("audio_%04x", bstream->pid);
-                caps = gst_caps_new_empty_simple ("audio/x-opus");
-
-                g_value_init (&v_arr, GST_TYPE_ARRAY);
-                g_value_init (&v_buf, GST_TYPE_BUFFER);
-                codec_data_buf =
-                    gst_buffer_new_wrapped (g_memdup (codecdata, codecdata_len),
-                    codecdata_len);
-                gst_value_take_buffer (&v_buf, codec_data_buf);
-                gst_value_array_append_and_take_value (&v_arr, &v_buf);
-
-
-                tags = gst_tag_list_new_empty ();
-                g_value_init (&v_buf, GST_TYPE_BUFFER);
-                codec_data_buf =
-                    gst_tag_list_to_vorbiscomment_buffer (tags,
-                    (const guint8 *) "OpusTags", 8, "No comments");
-                gst_tag_list_unref (tags);
-                gst_value_take_buffer (&v_buf, codec_data_buf);
-                gst_value_array_append_and_take_value (&v_arr, &v_buf);
-
-                gst_caps_set_value (caps, "streamheader", &v_arr);
+                caps = gst_caps_new_simple ("audio/x-opus",
+                    "channels", G_TYPE_INT, channels,
+                    "channel-mapping-family", G_TYPE_INT, mapping_family,
+                    "stream-count", G_TYPE_INT, stream_count,
+                    "coupled-count", G_TYPE_INT, coupled_count, NULL);
+
+                if (channel_mapping) {
+                  GValue v_arr = G_VALUE_INIT;
+                  GValue v = G_VALUE_INIT;
+                  gint i;
+
+                  g_value_init (&v_arr, GST_TYPE_ARRAY);
+                  g_value_init (&v, G_TYPE_INT);
+                  for (i = 0; i < channels; i++) {
+                    g_value_set_int (&v, channel_mapping[i]);
+                    gst_value_array_append_value (&v_arr, &v);
+                  }
 
-                g_value_unset (&v_arr);
+                  gst_caps_set_value (caps, "channel-mapping", &v_arr);
+                  g_value_unset (&v_arr);
+                  g_value_unset (&v);
+                  g_free (channel_mapping);
+                }
               }
             } else {
               GST_WARNING_OBJECT (demux,
index 37fc935..0fa78d7 100644 (file)
@@ -13,6 +13,7 @@ libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
 libgstmpegtsmux_la_LIBADD = $(top_builddir)/gst/mpegtsmux/tsmux/libtsmux.la \
        $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ \
        -lgstaudio-@GST_API_VERSION@ -lgsttag-@GST_API_VERSION@ \
+       -lgstpbutils-@GST_API_VERSION@ \
        $(GST_BASE_LIBS) $(GST_LIBS)
 libgstmpegtsmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstmpegtsmux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
index 0f6346b..e18aa75 100644 (file)
@@ -91,6 +91,7 @@
 #include <gst/tag/tag.h>
 #include <gst/video/video.h>
 #include <gst/mpegts/mpegts.h>
+#include <gst/pbutils/pbutils.h>
 
 #include "mpegtsmux.h"
 
@@ -143,7 +144,9 @@ static GstStaticPadTemplate mpegtsmux_sink_factory =
         "mute = (boolean) { FALSE, TRUE }; "
         "audio/x-ac3, framed = (boolean) TRUE;"
         "audio/x-dts, framed = (boolean) TRUE;"
-        "audio/x-opus;"
+        "audio/x-opus, "
+        "channels = (int) [1, 8], "
+        "channel-mapping-family = (int) {0, 1};"
         "subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true"));
 
 static GstStaticPadTemplate mpegtsmux_src_factory =
@@ -671,43 +674,22 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
     /* needs a particularly sized layout */
     ts_data->prepare_func = mpegtsmux_prepare_teletext;
   } else if (strcmp (mt, "audio/x-opus") == 0) {
-    GstBuffer *streamheader = NULL;
-    const GValue *v;
-    GstMapInfo map;
-
-    v = gst_structure_get_value (s, "streamheader");
-    if (v && G_VALUE_HOLDS (v, GST_TYPE_ARRAY)
-        && gst_value_array_get_size (v) >= 1) {
-      const GValue *h = gst_value_array_get_value (v, 0);
-
-      streamheader = gst_value_get_buffer (h);
-    }
+    guint8 channels, mapping_family, stream_count, coupled_count;
+    guint8 channel_mapping[256];
 
-    /* FIXME: We need to either map all values for the OpusHead header
-     * to caps, or always require/generate an OpusHead streamheader for the
-     * caps. E.g. in rtpopusdepay */
-    if (!streamheader || gst_buffer_get_size (streamheader) < 22) {
-      gint channels;
-
-      if (gst_structure_get_int (s, "channels", &channels) && channels <= 2) {
-        opus_channel_config_code = channels;
-      } else {
-        GST_FIXME_OBJECT (pad,
-            "Multichannel Opus without streamheader not handled");
-        goto not_negotiated;
-      }
+    if (!gst_codec_utils_opus_parse_caps (caps, NULL, &channels,
+            &mapping_family, &stream_count, &coupled_count, channel_mapping)) {
+      GST_ERROR_OBJECT (pad, "Incomplete Opus caps");
+      goto not_negotiated;
     }
 
-    gst_buffer_map (streamheader, &map, GST_MAP_READ);
-    if (map.data[9] == 2 && map.data[18] == 255 && map.data[19] == 1
-        && map.data[20] == 1) {
+    if (channels <= 2 && mapping_family == 0) {
+      opus_channel_config_code = channels;
+    } else if (channels == 2 && mapping_family == 255 && stream_count == 1
+        && coupled_count == 1) {
       /* Dual mono */
       opus_channel_config_code = 0;
-    } else if (map.data[9] >= 1 && map.data[9] <= 2 && map.data[18] == 0) {
-      /* RTP mapping */
-      opus_channel_config_code = map.data[9];
-    } else if (map.data[9] >= 2 && map.data[9] <= 8 && map.data[18] == 1
-        && map.size >= 21 + map.data[9]) {
+    } else if (channels >= 2 && channels <= 8 && mapping_family == 1) {
       static const guint8 coupled_stream_counts[9] = {
         1, 0, 1, 1, 2, 2, 2, 3, 3
       };
@@ -733,27 +715,21 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
       };
 
       /* Vorbis mapping */
-      if (map.data[19] == map.data[9] - coupled_stream_counts[map.data[9]] &&
-          map.data[20] == coupled_stream_counts[map.data[9]] &&
-          memcmp (&map.data[21], channel_map_a[map.data[9] - 1],
-              map.data[9]) == 0) {
-        opus_channel_config_code = map.data[9];
-      } else if (map.data[19] == map.data[9] &&
-          map.data[20] == 0 &&
-          memcmp (&map.data[21], channel_map_b[map.data[9] - 1],
-              map.data[9]) == 0) {
-        opus_channel_config_code = map.data[9] | 0x80;
+      if (stream_count == channels - coupled_stream_counts[channels] &&
+          coupled_count == coupled_stream_counts[channels] &&
+          memcmp (channel_mapping, channel_map_a[channels - 1],
+              channels) == 0) {
+        opus_channel_config_code = channels;
+      } else if (stream_count == channels - coupled_stream_counts[channels] &&
+          coupled_count == coupled_stream_counts[channels] &&
+          memcmp (channel_mapping, channel_map_b[channels - 1],
+              channels) == 0) {
+        opus_channel_config_code = channels | 0x80;
       } else {
-        gst_buffer_unmap (streamheader, &map);
         GST_FIXME_OBJECT (pad, "Opus channel mapping not handled");
         goto not_negotiated;
       }
-    } else {
-      gst_buffer_unmap (streamheader, &map);
-      GST_FIXME_OBJECT (pad, "Opus channel mapping not handled");
-      goto not_negotiated;
     }
-    gst_buffer_unmap (streamheader, &map);
 
     st = TSMUX_ST_PS_OPUS;
     ts_data->prepare_func = mpegtsmux_prepare_opus;