qtmux: add AC-3 muxing support

Adds AC-3 muxing support. It is defined for mp4 and 3gp formats.

One extra feature that was added was the ability to add extension
atoms after set_caps as the AC-3 extension atom needs some data
that has to be extracted from the stream itself and is not
present on caps.
This commit is contained in:
Thiago Santos 2015-06-10 22:27:27 -03:00
parent 674e0cc2df
commit ab18f5035c
6 changed files with 175 additions and 11 deletions

View file

@ -3429,6 +3429,22 @@ atom_trak_set_subtitle_commons (AtomTRAK * trak, AtomsContext * context)
}
void
sample_table_entry_add_ext_atom (SampleTableEntry * ste, AtomInfo * ext)
{
GList **list = NULL;
if (ste->kind == AUDIO) {
list = &(((SampleTableEntryMP4A *) ste)->extension_atoms);
} else if (ste->kind == VIDEO) {
list = &(((SampleTableEntryMP4V *) ste)->extension_atoms);
} else {
g_assert_not_reached ();
return;
}
*list = g_list_prepend (*list, ext);
}
SampleTableEntryMP4A *
atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size)
{
@ -3457,6 +3473,8 @@ atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
/* 0 size means variable size */
atom_trak_set_constant_size_samples (trak, sample_size);
return ste;
}
static AtomInfo *
@ -3479,7 +3497,7 @@ build_pasp_extension (AtomTRAK * trak, gint par_width, gint par_height)
atom_data_free);
}
void
SampleTableEntryMP4V *
atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list)
{
@ -3528,6 +3546,8 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
ste->extension_atoms = g_list_append (ste->extension_atoms,
build_pasp_extension (trak, par_n, par_d));
}
return ste;
}
void
@ -3538,7 +3558,7 @@ subtitle_sample_entry_init (SubtitleSampleEntry * entry)
entry->foreground_color_rgba = 0xFFFFFFFF; /* all white, opaque */
}
void
SampleTableEntryTX3G *
atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context,
SubtitleSampleEntry * entry)
{
@ -3554,6 +3574,8 @@ atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context,
trak->is_video = FALSE;
trak->is_h264 = FALSE;
return tx3g;
}
static void
@ -4696,6 +4718,41 @@ build_ima_adpcm_extension (gint channels, gint rate, gint blocksize)
atom_wave_free);
}
AtomInfo *
build_ac3_extension (guint8 fscod, guint8 bsid, guint8 bsmod, guint8 acmod,
guint8 lfe_on, guint8 bitrate_code)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 *data;
data = g_malloc0 (3);
/* Bits from the spec
* fscod 2
* bsid 5
* bsmod 3
* acmod 3
* lfeon 1
* bit_rate_code 5
* reserved 5
*/
/* Some bit manipulation magic. Need bitwriter */
data[0] = (fscod << 6) | (bsid << 1) | ((bsmod >> 2) & 1);
data[1] =
((bsmod & 0x3) << 6) | (acmod << 3) | ((lfe_on & 1) << 2) | ((bitrate_code
>> 3) & 0x3);
data[2] = ((bitrate_code & 0x7) << 5);
buf = gst_buffer_new_wrapped (data, 3);
atom_data = atom_data_new_from_gst_buffer (FOURCC_dac3, buf);
gst_buffer_unref (buf);
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

@ -925,15 +925,15 @@ typedef struct
void subtitle_sample_entry_init (SubtitleSampleEntry * entry);
void atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
SampleTableEntryMP4A * atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
AudioSampleEntry * entry, guint32 scale,
AtomInfo * ext, gint sample_size);
void atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
SampleTableEntryMP4V * atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
VisualSampleEntry * entry, guint32 rate,
GList * ext_atoms_list);
void atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context,
SampleTableEntryTX3G * atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context,
SubtitleSampleEntry * entry);
void atom_trak_update_bitrates (AtomTRAK * trak, guint32 avg_bitrate,
@ -942,6 +942,8 @@ void atom_trak_update_bitrates (AtomTRAK * trak, guint32 avg_bitrate,
void atom_trak_tx3g_update_dimension (AtomTRAK * trak, guint32 width,
guint32 height);
void sample_table_entry_add_ext_atom (SampleTableEntry * ste, AtomInfo * ext);
AtomInfo * build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data);
AtomInfo * build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data,
guint32 avg_bitrate, guint32 max_bitrate);
@ -958,6 +960,9 @@ AtomInfo * build_jp2h_extension (AtomTRAK * trak, gint width, gint heig
AtomInfo * build_jp2x_extension (const GstBuffer * prefix);
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_amr_extension (void);
AtomInfo * build_h263_extension (void);
AtomInfo * build_gama_atom (gdouble gamma);

View file

@ -83,6 +83,7 @@ G_BEGIN_DECLS
#define FOURCC__too GST_MAKE_FOURCC(0xa9,'t','o','o')
#define FOURCC__wrt GST_MAKE_FOURCC(0xa9,'w','r','t')
#define FOURCC_aART GST_MAKE_FOURCC('a','A','R','T')
#define FOURCC_ac_3 GST_MAKE_FOURCC('a','c','-','3')
#define FOURCC_agsm GST_MAKE_FOURCC('a','g','s','m')
#define FOURCC_alac GST_MAKE_FOURCC('a','l','a','c')
#define FOURCC_alaw GST_MAKE_FOURCC('a','l','a','w')
@ -101,6 +102,7 @@ G_BEGIN_DECLS
#define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n')
#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b')
#define FOURCC_ctts GST_MAKE_FOURCC('c','t','t','s')
#define FOURCC_dac3 GST_MAKE_FOURCC('d','a','c','3')
#define FOURCC_data GST_MAKE_FOURCC('d','a','t','a')
#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m')
#define FOURCC_desc GST_MAKE_FOURCC('d','e','s','c')

View file

@ -117,6 +117,8 @@
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/base/gstbytereader.h>
#include <gst/base/gstbitreader.h>
#include <gst/audio/audio.h>
#include <gst/video/video.h>
#include <gst/tag/tag.h>
@ -720,6 +722,82 @@ gst_qt_mux_prepare_tx3g_buffer (GstQTPad * qtpad, GstBuffer * buf,
return newbuf;
}
static void
gst_qt_mux_pad_add_ac3_extension (GstQTMux * qtmux, GstQTPad * qtpad,
guint8 fscod, guint8 frmsizcod, guint8 bsid, guint8 bsmod, guint8 acmod,
guint8 lfe_on)
{
AtomInfo *ext;
g_return_if_fail (qtpad->trak_ste);
ext = build_ac3_extension (fscod, bsid, bsmod, acmod, lfe_on, frmsizcod >> 1); /* bitrate_code is inside frmsizcod */
sample_table_entry_add_ext_atom (qtpad->trak_ste, ext);
}
static GstBuffer *
gst_qt_mux_prepare_parse_ac3_frame (GstQTPad * qtpad, GstBuffer * buf,
GstQTMux * qtmux)
{
GstMapInfo map;
GstByteReader reader;
guint off;
if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
GST_WARNING_OBJECT (qtpad->collect.pad, "Failed to map buffer");
return buf;
}
if (G_UNLIKELY (map.size < 8))
goto done;
gst_byte_reader_init (&reader, map.data, map.size);
off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000,
0, map.size);
if (off != -1) {
GstBitReader bits;
guint8 fscod, frmsizcod, bsid, bsmod, acmod, lfe_on;
GST_DEBUG_OBJECT (qtpad->collect.pad, "Found ac3 sync point at offset: %u",
off);
gst_bit_reader_init (&bits, map.data, map.size);
/* off + sync + crc */
gst_bit_reader_skip_unchecked (&bits, off * 8 + 16 + 16);
fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2);
frmsizcod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 6);
bsid = gst_bit_reader_get_bits_uint8_unchecked (&bits, 5);
bsmod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3);
acmod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3);
if ((acmod & 0x1) && (acmod != 0x1)) /* 3 front channels */
gst_bit_reader_skip_unchecked (&bits, 2);
if ((acmod & 0x4)) /* if a surround channel exists */
gst_bit_reader_skip_unchecked (&bits, 2);
if (acmod == 0x2) /* if in 2/0 mode */
gst_bit_reader_skip_unchecked (&bits, 2);
lfe_on = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
gst_qt_mux_pad_add_ac3_extension (qtmux, qtpad, fscod, frmsizcod, bsid,
bsmod, acmod, lfe_on);
/* AC-3 spec says that those values should be constant for the
* whole stream when muxed in mp4. We trust the input follows it */
GST_DEBUG_OBJECT (qtpad->collect.pad, "Data parsed, removing "
"prepare buffer function");
qtpad->prepare_buf_func = NULL;
}
done:
gst_buffer_unmap (buf, &map);
return buf;
}
static GstBuffer *
gst_qt_mux_create_empty_tx3g_buffer (GstQTPad * qtpad, gint64 duration)
{
@ -3497,6 +3575,18 @@ gst_qt_mux_audio_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
entry.samples_per_packet = GST_READ_UINT32_BE (map.data + 4);
gst_buffer_unmap (codec_config, &map);
gst_buffer_unref (codec_config);
} else if (strcmp (mimetype, "audio/x-ac3") == 0) {
entry.fourcc = FOURCC_ac_3;
/* Fixed values according to TS 102 366 but it also mentions that
* they should be ignored */
entry.channels = 2;
entry.sample_size = 16;
/* AC-3 needs an extension atom but its data can only be obtained from
* 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;
}
if (!entry.fourcc)
@ -3505,7 +3595,9 @@ gst_qt_mux_audio_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
/* ok, set the pad info accordingly */
qtpad->fourcc = entry.fourcc;
qtpad->sample_size = constant_size;
atom_trak_set_audio_type (qtpad->trak, qtmux->context, &entry,
qtpad->trak_ste =
(SampleTableEntry *) atom_trak_set_audio_type (qtpad->trak,
qtmux->context, &entry,
qtmux->trak_timescale ? qtmux->trak_timescale : entry.sample_rate,
ext_atom, constant_size);
@ -3866,8 +3958,9 @@ gst_qt_mux_video_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
/* ok, set the pad info accordingly */
qtpad->fourcc = entry.fourcc;
qtpad->sync = sync;
atom_trak_set_video_type (qtpad->trak, qtmux->context, &entry, rate,
ext_atom_list);
qtpad->trak_ste =
(SampleTableEntry *) atom_trak_set_video_type (qtpad->trak,
qtmux->context, &entry, rate, ext_atom_list);
gst_object_unref (qtmux);
return TRUE;
@ -3942,7 +4035,9 @@ gst_qt_mux_subtitle_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
goto refuse_caps;
qtpad->fourcc = entry.fourcc;
atom_trak_set_subtitle_type (qtpad->trak, qtmux->context, &entry);
qtpad->trak_ste =
(SampleTableEntry *) atom_trak_set_subtitle_type (qtpad->trak,
qtmux->context, &entry);
gst_object_unref (qtmux);
return TRUE;

View file

@ -118,6 +118,7 @@ struct _GstQTPad
/* all the atom and chunk book-keeping is delegated here
* unowned/uncounted reference, parent MOOV owns */
AtomTRAK *trak;
SampleTableEntry *trak_ste;
/* fragmented support */
/* meta data book-keeping delegated here */
AtomTRAF *traf;

View file

@ -115,6 +115,10 @@
"stream-format = (string) raw, " \
COMMON_AUDIO_CAPS (8, MAX)
#define AC3_CAPS \
"audio/x-ac3, " \
COMMON_AUDIO_CAPS (6, MAX)
#define AMR_CAPS \
"audio/AMR, " \
"rate = (int) 8000, " \
@ -184,7 +188,7 @@ 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 " ; " ALAC_CAPS),
GST_STATIC_CAPS (MP3_CAPS "; " AAC_CAPS " ; " AC3_CAPS " ; " ALAC_CAPS),
GST_STATIC_CAPS (TEXT_UTF8)}
,
/* Microsoft Smooth Streaming fmp4/isml */
@ -210,7 +214,7 @@ GstQTMuxFormatProp gst_qt_mux_format_list[] = {
"Gst3GPPMux",
GST_STATIC_CAPS ("video/quicktime, variant = (string) 3gpp"),
GST_STATIC_CAPS (H263_CAPS "; " MPEG4V_CAPS "; " H264_CAPS),
GST_STATIC_CAPS (AMR_CAPS "; " MP3_CAPS "; " AAC_CAPS),
GST_STATIC_CAPS (AMR_CAPS "; " MP3_CAPS "; " AAC_CAPS "; " AC3_CAPS),
GST_STATIC_CAPS (TEXT_UTF8)}
,
/* ISO 15444-3: Motion-JPEG-2000 (also ISO base media extension) */