mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
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:
parent
674e0cc2df
commit
ab18f5035c
6 changed files with 175 additions and 11 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
Loading…
Reference in a new issue