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.
}
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)
{
/* 0 size means variable size */
atom_trak_set_constant_size_samples (trak, sample_size);
+
+ return ste;
}
static AtomInfo *
atom_data_free);
}
-void
+SampleTableEntryMP4V *
atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list)
{
ste->extension_atoms = g_list_append (ste->extension_atoms,
build_pasp_extension (trak, par_n, par_d));
}
+
+ return ste;
}
void
entry->foreground_color_rgba = 0xFFFFFFFF; /* all white, opaque */
}
-void
+SampleTableEntryTX3G *
atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context,
SubtitleSampleEntry * entry)
{
trak->is_video = FALSE;
trak->is_h264 = FALSE;
+
+ return tx3g;
}
static void
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)
{
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,
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);
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);
#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')
#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')
#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>
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)
{
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)
/* 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);
/* 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;
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;
/* 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;
"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, " \
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 */
"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) */