isomp4: add support for Opus in mp4mpux
authorLuis de Bethencourt <luisbg@osg.samsung.com>
Mon, 16 Nov 2015 13:26:50 +0000 (13:26 +0000)
committerLuis de Bethencourt <luisbg@osg.samsung.com>
Thu, 19 Nov 2015 17:08:25 +0000 (17:08 +0000)
Add support for muxing MP4 files containing Opus. Based on the spec
detailed here:
https://www.opus-codec.org/docs/opus_in_isobmff.html

https://bugzilla.gnome.org/show_bug.cgi?id=742643

gst/isomp4/atoms.c
gst/isomp4/atoms.h
gst/isomp4/fourcc.h
gst/isomp4/gstqtmux.c
gst/isomp4/gstqtmuxmap.c

index 84b3d807edf6588ac4f83d58b23a4115ca0a9400..efebf2d3a9ad26f37a0187f28f026338ca1e91cb 100644 (file)
@@ -4727,6 +4727,44 @@ build_ac3_extension (guint8 fscod, guint8 bsid, guint8 bsmod, guint8 acmod,
       atom_data_free);
 }
 
+AtomInfo *
+build_opus_extension (guint32 rate, guint8 channels, guint8 mapping_family,
+    guint8 stream_count, guint8 coupled_count, guint8 channel_mapping[256],
+    guint16 pre_skip, guint16 output_gain)
+{
+  AtomData *atom_data;
+  guint8 *data_block;
+  GstByteWriter bw;
+  gboolean hdl = TRUE;
+  guint data_block_len;
+
+  gst_byte_writer_init (&bw);
+  hdl &= gst_byte_writer_put_uint8 (&bw, 0x00); /* version number */
+  hdl &= gst_byte_writer_put_uint8 (&bw, channels);
+  hdl &= gst_byte_writer_put_uint16_le (&bw, pre_skip);
+  hdl &= gst_byte_writer_put_uint32_le (&bw, rate);
+  hdl &= gst_byte_writer_put_uint16_le (&bw, output_gain);
+  hdl &= gst_byte_writer_put_uint8 (&bw, mapping_family);
+  if (mapping_family > 0) {
+    hdl &= gst_byte_writer_put_uint8 (&bw, stream_count);
+    hdl &= gst_byte_writer_put_uint8 (&bw, coupled_count);
+    hdl &= gst_byte_writer_put_data (&bw, channel_mapping, channels);
+  }
+
+  if (!hdl) {
+    GST_WARNING ("Error creating header");
+    return NULL;
+  }
+
+  data_block_len = gst_byte_writer_get_size (&bw);
+  data_block = gst_byte_writer_reset_and_get_data (&bw);
+  atom_data = atom_data_new_from_data (FOURCC_dops, data_block, data_block_len);
+  g_free (data_block);
+
+  return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
+      atom_data_free);
+}
+
 AtomInfo *
 build_uuid_xmp_atom (GstBuffer * xmp_data)
 {
index f8789e4706f8802f6deced3d53571d01ee9f6306..8016809fe8816f5e4b7317e3877122e9b95217d2 100644 (file)
@@ -964,6 +964,11 @@ AtomInfo *   build_fiel_extension        (gint fields);
 AtomInfo *   build_ac3_extension         (guint8 fscod, guint8 bsid,
                                           guint8 bsmod, guint8 acmod,
                                           guint8 lfe_on, guint8 bitrate_code);
+AtomInfo *   build_opus_extension        (guint32 rate, guint8 channels, guint8 mapping_family,
+                                          guint8 stream_count, guint8 coupled_count,
+                                          guint8 channel_mapping[256], guint16 pre_skip,
+                                          guint16 output_gain);
+
 AtomInfo *   build_amr_extension         (void);
 AtomInfo *   build_h263_extension        (void);
 AtomInfo *   build_gama_atom             (gdouble gamma);
index 9ebb4c05d3adcf4eaa41f7f20e45271ca92c3e00..bba6ad742d42c9c3117a645d8604444a555460fd 100644 (file)
@@ -163,6 +163,8 @@ G_BEGIN_DECLS
 #define FOURCC_mp4s     GST_MAKE_FOURCC('m','p','4','s')
 #define FOURCC_mp4v     GST_MAKE_FOURCC('m','p','4','v')
 #define FOURCC_name     GST_MAKE_FOURCC('n','a','m','e')
+#define FOURCC_opus     GST_MAKE_FOURCC('O','p','u','s')
+#define FOURCC_dops     GST_MAKE_FOURCC('d','O','p','s')
 #define FOURCC_pasp     GST_MAKE_FOURCC('p','a','s','p')
 #define FOURCC_pcst     GST_MAKE_FOURCC('p','c','s','t')
 #define FOURCC_pgap     GST_MAKE_FOURCC('p','g','a','p')
index 7f72443e05b2804de2a05b616276dd8a08bdf099..a2968f1f089c3d85921a84133da05b6edc95c551 100644 (file)
 #include <gst/audio/audio.h>
 #include <gst/video/video.h>
 #include <gst/tag/tag.h>
+#include <gst/pbutils/pbutils.h>
 
 #include <sys/types.h>
 #ifdef G_OS_WIN32
@@ -3665,6 +3666,49 @@ gst_qt_mux_audio_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
      * the stream itself. Abuse the prepare_buf_func so we parse a frame
      * and get the needed data */
     qtpad->prepare_buf_func = gst_qt_mux_prepare_parse_ac3_frame;
+  } else if (strcmp (mimetype, "audio/x-opus") == 0) {
+    /* Based on the specification defined in:
+     * https://www.opus-codec.org/docs/opus_in_isobmff.html */
+    guint8 channels, mapping_family, stream_count, coupled_count;
+    guint16 pre_skip;
+    gint16 output_gain;
+    guint32 rate;
+    guint8 channel_mapping[256];
+    const GValue *streamheader;
+    const GValue *first_element;
+    GstBuffer *header;
+
+    entry.fourcc = FOURCC_opus;
+    entry.sample_size = 16;
+
+    streamheader = gst_structure_get_value (structure, "streamheader");
+    if (streamheader && GST_VALUE_HOLDS_ARRAY (streamheader) &&
+        gst_value_array_get_size (streamheader) != 0) {
+      first_element = gst_value_array_get_value (streamheader, 0);
+      header = gst_value_get_buffer (first_element);
+      if (!gst_codec_utils_opus_parse_header (header, &rate, &channels,
+              &mapping_family, &stream_count, &coupled_count, channel_mapping,
+              &pre_skip, &output_gain)) {
+        GST_ERROR_OBJECT (qtmux, "Incomplete OpusHead");
+        goto refuse_caps;
+      }
+    } else {
+      GST_WARNING_OBJECT (qtmux,
+          "no streamheader field in caps %" GST_PTR_FORMAT, caps);
+
+      if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
+              &mapping_family, &stream_count, &coupled_count,
+              channel_mapping)) {
+        GST_ERROR_OBJECT (qtmux, "Incomplete Opus caps");
+        goto refuse_caps;
+      }
+      pre_skip = 0;
+      output_gain = 0;
+    }
+
+    entry.channels = channels;
+    ext_atom = build_opus_extension (rate, channels, mapping_family,
+        stream_count, coupled_count, channel_mapping, pre_skip, output_gain);
   }
 
   if (!entry.fourcc)
index 77f39a447abc655cfc7c03e4bcaa401572748603..61b11a44304d9e21e05c518d97cb129461b4e63e 100644 (file)
   "audio/x-alac, " \
   COMMON_AUDIO_CAPS(2, MAX)
 
+#define OPUS_CAPS \
+  "audio/x-opus, " \
+  "channel-mapping-family = (int) [0, 255], " \
+  COMMON_AUDIO_CAPS(8, MAX)
+
+
 #define TEXT_UTF8 \
   "text/x-raw, " \
   "format=(string)utf8"
@@ -184,7 +190,8 @@ GstQTMuxFormatProp gst_qt_mux_format_list[] = {
         GST_STATIC_CAPS ("video/quicktime, variant = (string) iso"),
         GST_STATIC_CAPS (MPEG4V_CAPS "; " H264_CAPS ";"
             "video/x-mp4-part," COMMON_VIDEO_CAPS),
-        GST_STATIC_CAPS (MP3_CAPS "; " AAC_CAPS " ; " AC3_CAPS " ; " ALAC_CAPS),
+        GST_STATIC_CAPS (MP3_CAPS "; "
+            AAC_CAPS " ; " AC3_CAPS " ; " ALAC_CAPS " ; " OPUS_CAPS),
       GST_STATIC_CAPS (TEXT_UTF8)}
   ,
   /* Microsoft Smooth Streaming fmp4/isml */