isomp4: add support for Opus in mp4mpux

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
This commit is contained in:
Luis de Bethencourt 2015-11-16 13:26:50 +00:00
parent cc119e6eb9
commit 5ed8cba024
5 changed files with 97 additions and 1 deletions

View 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)
{

View 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);

View 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')

View file

@ -122,6 +122,7 @@
#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)

View file

@ -132,6 +132,12 @@
"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 */