mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-23 23:58:17 +00:00
qtdemux: Add support for ISO/IEC 23003-5 raw PCM audio
And also support the ISO/IEC 14496-12 chnl channel layout box as required by ISO/IEC 23003-5. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8171>
This commit is contained in:
parent
fd91abfc9a
commit
0aeba9275c
4 changed files with 705 additions and 3 deletions
|
@ -299,6 +299,10 @@ G_BEGIN_DECLS
|
|||
#define FOURCC_wvtt GST_MAKE_FOURCC('w','v','t','t')
|
||||
#define FOURCC_zlib GST_MAKE_FOURCC('z','l','i','b')
|
||||
#define FOURCC_lpcm GST_MAKE_FOURCC('l','p','c','m')
|
||||
#define FOURCC_ipcm GST_MAKE_FOURCC('i','p','c','m')
|
||||
#define FOURCC_fpcm GST_MAKE_FOURCC('f','p','c','m')
|
||||
#define FOURCC_pcmC GST_MAKE_FOURCC('p','c','m','C')
|
||||
#define FOURCC_chnl GST_MAKE_FOURCC('c','h','n','l')
|
||||
#define FOURCC_av01 GST_MAKE_FOURCC('a','v','0','1')
|
||||
#define FOURCC_av1C GST_MAKE_FOURCC('a','v','1','C')
|
||||
#define FOURCC_av1f GST_MAKE_FOURCC('a','v','1','f')
|
||||
|
|
|
@ -6110,6 +6110,36 @@ clipped:
|
|||
}
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qtdemux_reorder_audio_channels (GstQTDemux * demux,
|
||||
QtDemuxStream * stream, GstBuffer * buffer)
|
||||
{
|
||||
buffer = gst_buffer_make_writable (buffer);
|
||||
|
||||
GstMapInfo map;
|
||||
|
||||
if (!gst_buffer_map (buffer, &map, GST_MAP_READWRITE))
|
||||
return buffer;
|
||||
|
||||
if (map.size % (CUR_STREAM (stream)->bytes_per_sample *
|
||||
CUR_STREAM (stream)->n_channels) != 0) {
|
||||
GST_WARNING_OBJECT (demux,
|
||||
"Odd number of frames in raw audio buffer of length %" G_GSIZE_FORMAT
|
||||
" with %u bps and %u channels", map.size,
|
||||
CUR_STREAM (stream)->bytes_per_sample, CUR_STREAM (stream)->n_channels);
|
||||
goto out;
|
||||
}
|
||||
|
||||
gst_audio_reorder_channels_with_reorder_map (map.data, map.size,
|
||||
CUR_STREAM (stream)->bytes_per_sample, CUR_STREAM (stream)->n_channels,
|
||||
CUR_STREAM (stream)->reorder_map);
|
||||
|
||||
out:
|
||||
gst_buffer_unmap (buffer, &map);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qtdemux_align_buffer (GstQTDemux * demux,
|
||||
GstBuffer * buffer, gsize alignment)
|
||||
|
@ -6603,6 +6633,9 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
|||
if (stream->alignment > 1)
|
||||
buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
|
||||
|
||||
if (CUR_STREAM (stream)->needs_reorder)
|
||||
buf = gst_qtdemux_reorder_audio_channels (qtdemux, stream, buf);
|
||||
|
||||
pts = GST_BUFFER_PTS (buf);
|
||||
duration = GST_BUFFER_DURATION (buf);
|
||||
|
||||
|
@ -6802,6 +6835,9 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
|
|||
|
||||
if (stream->alignment > 1)
|
||||
buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
|
||||
if (CUR_STREAM (stream)->needs_reorder)
|
||||
buffer = gst_qtdemux_reorder_audio_channels (qtdemux, stream, buffer);
|
||||
|
||||
gst_pad_push (stream->pad, buffer);
|
||||
|
||||
stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
|
||||
|
@ -9150,6 +9186,20 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
|
|||
qtdemux_parse_container (qtdemux, node, buffer + 36, end);
|
||||
break;
|
||||
}
|
||||
case FOURCC_ipcm:
|
||||
case FOURCC_fpcm:
|
||||
{
|
||||
if (length < 36) {
|
||||
GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
|
||||
GST_FOURCC_ARGS (fourcc));
|
||||
break;
|
||||
}
|
||||
|
||||
GST_MEMDUMP_OBJECT (qtdemux, fourcc == FOURCC_ipcm ? "ipcm" : "fpcm",
|
||||
buffer, end - buffer);
|
||||
qtdemux_parse_container (qtdemux, node, buffer + 36, end);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (!strcmp (type->name, "unknown"))
|
||||
GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
|
||||
|
@ -11803,6 +11853,533 @@ qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
// ISO/IEC 23091-3
|
||||
static const GstAudioChannelPosition chnl_positions[] = {
|
||||
// 0
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
// 10
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER,
|
||||
// 20
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE2,
|
||||
GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER,
|
||||
// 30
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_SURROUND_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_SURROUND_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // reserved
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // reserved
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // reserved
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // reserved
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // low frequency enhancement 3
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // left edge of screen
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // right edge of screen
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // half-way between centre of screen and
|
||||
// left edge of screen
|
||||
// 40
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // half-way between centre of screen and
|
||||
// right edge of screen
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // left back surround
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID, // right back surround
|
||||
// 43-125 reserved
|
||||
// 126 explicit position
|
||||
// 127 unknown / undefined
|
||||
};
|
||||
|
||||
// Pre-defined channel layouts
|
||||
//
|
||||
// Each layout is terminated by INVALID to allow counting the number of
|
||||
// channels in the layout.
|
||||
static const GstAudioChannelPosition chnl_layouts[][25] = {
|
||||
// 0
|
||||
{ GST_AUDIO_CHANNEL_POSITION_INVALID, },
|
||||
// 1
|
||||
{ GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_INVALID, },
|
||||
// 2
|
||||
{ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_INVALID, },
|
||||
// 3
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 4
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 5
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 6
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 7
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 8
|
||||
{ GST_AUDIO_CHANNEL_POSITION_INVALID, },
|
||||
// 9
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 10
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 11
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 12
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 13
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1, GST_AUDIO_CHANNEL_POSITION_LFE2,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
// 14
|
||||
{
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE1,
|
||||
GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
},
|
||||
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static void
|
||||
qtdemux_parse_chnl (GstQTDemux * qtdemux, GstByteReader * br,
|
||||
QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry)
|
||||
{
|
||||
GstAudioChannelPosition positions[64];
|
||||
guint n_channels = 0;
|
||||
|
||||
guint8 version = gst_byte_reader_get_uint8_unchecked (br);
|
||||
guint32 flags = gst_byte_reader_get_uint24_be_unchecked (br);
|
||||
|
||||
if (version == 0 && flags == 0) {
|
||||
guint8 stream_structure;
|
||||
if (!gst_byte_reader_get_uint8 (br, &stream_structure)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// stream carries channels
|
||||
if (stream_structure & 1) {
|
||||
guint8 defined_layout;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &defined_layout)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
n_channels = entry->n_channels;
|
||||
|
||||
if (defined_layout == 0) {
|
||||
for (unsigned int i = 0; i < n_channels; i++) {
|
||||
guint8 speaker_position;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &speaker_position)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// explicit position
|
||||
if (speaker_position == 126) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Explicit speaker position not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (speaker_position >= G_N_ELEMENTS (chnl_positions) ||
|
||||
chnl_positions[speaker_position] ==
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Unsupported speaker channel %u position %u", i,
|
||||
speaker_position);
|
||||
goto error;
|
||||
}
|
||||
|
||||
positions[i] = chnl_positions[speaker_position];
|
||||
}
|
||||
} else {
|
||||
guint64 omitted_channels_map;
|
||||
|
||||
if (!gst_byte_reader_get_uint64_be (br, &omitted_channels_map)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (defined_layout >= G_N_ELEMENTS (chnl_layouts) ||
|
||||
chnl_layouts[defined_layout][0] ==
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Unsupported defined layout %u",
|
||||
defined_layout);
|
||||
goto error;
|
||||
}
|
||||
|
||||
const GstAudioChannelPosition *layout = chnl_layouts[defined_layout];
|
||||
|
||||
// The omitted channel map defines which of the channels of the
|
||||
// pre-defined layout are *not* included.
|
||||
for (unsigned int c = 0; c < n_channels; c++) {
|
||||
// Find c-th channel in layout that is not omitted
|
||||
unsigned int l_c = 0;
|
||||
for (unsigned int i = 0; i < 64; i++) {
|
||||
// If there are not enough non-omitted channels in the layout we end
|
||||
// up here and return
|
||||
if (layout[i] == GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Invalid defined layout %u with %u channels and omitted channels map %016"
|
||||
G_GINT64_MODIFIER "x", defined_layout, n_channels,
|
||||
omitted_channels_map);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// The i-th channel of the layout is included
|
||||
if (((omitted_channels_map >> i) & 1) == 0) {
|
||||
// The channel we're looking for
|
||||
if (l_c == c) {
|
||||
positions[c] = layout[l_c];
|
||||
break;
|
||||
}
|
||||
l_c += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are not enough non-omitted channels in the omitted
|
||||
// channels map then return here
|
||||
if (positions[c] == GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Invalid defined layout %u with %u channels and omitted channels map %016"
|
||||
G_GINT64_MODIFIER "x", defined_layout, n_channels,
|
||||
omitted_channels_map);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stream carries objects
|
||||
if (stream_structure & 2) {
|
||||
guint8 object_count;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &object_count)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
GST_WARNING_OBJECT (qtdemux, "Stream carries %u objects", object_count);
|
||||
goto error;
|
||||
}
|
||||
} else if (version == 1 && flags == 0) {
|
||||
guint8 b;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &b)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
guint8 stream_structure = b >> 4;
|
||||
// guint8 format_ordering = b & 0x0f;
|
||||
|
||||
guint8 base_channel_count;
|
||||
if (!gst_byte_reader_get_uint8 (br, &base_channel_count)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// stream carries channels
|
||||
if (stream_structure & 1) {
|
||||
guint8 defined_layout;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &defined_layout)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (defined_layout == 0) {
|
||||
guint8 layout_channel_count;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &layout_channel_count)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (layout_channel_count == 0) {
|
||||
// Not present so configure a default based on the sample entry
|
||||
goto error;
|
||||
}
|
||||
|
||||
n_channels = layout_channel_count;
|
||||
for (unsigned int i = 0; i < layout_channel_count; i++) {
|
||||
guint8 speaker_position;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (br, &speaker_position)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// explicit position
|
||||
if (speaker_position == 126) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Explicit speaker position not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (speaker_position >= G_N_ELEMENTS (chnl_positions) ||
|
||||
chnl_positions[speaker_position] ==
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Unsupported speaker channel %u position %u", i,
|
||||
speaker_position);
|
||||
goto error;
|
||||
}
|
||||
|
||||
positions[i] = chnl_positions[speaker_position];
|
||||
}
|
||||
} else {
|
||||
if (!gst_byte_reader_get_uint8 (br, &b)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
|
||||
guint8 channel_order_definition = (b >> 1) & 0x07;
|
||||
guint8 omitted_channels_present = b & 0x01;
|
||||
|
||||
if (channel_order_definition != 0) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Channel order definition %u not supported",
|
||||
channel_order_definition);
|
||||
goto error;
|
||||
}
|
||||
|
||||
guint64 omitted_channels_map = 0;
|
||||
if (omitted_channels_present) {
|
||||
if (!gst_byte_reader_get_uint64_be (br, &omitted_channels_map)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Too short chnl box");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
const GstAudioChannelPosition *layout = chnl_layouts[defined_layout];
|
||||
|
||||
// Calculate number of channels: number of channels in the layout
|
||||
// minus number of omitted channels
|
||||
n_channels = 0;
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (chnl_layouts[0]); i++) {
|
||||
if (layout[i] == GST_AUDIO_CHANNEL_POSITION_INVALID)
|
||||
break;
|
||||
|
||||
n_channels += 1;
|
||||
}
|
||||
for (unsigned int i = 0; i < 64; i++) {
|
||||
if ((omitted_channels_map >> i) == 1) {
|
||||
n_channels -= 1;
|
||||
}
|
||||
// No channels present
|
||||
if (n_channels == 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
// The omitted channel map defines which of the channels of the
|
||||
// pre-defined layout are *not* included.
|
||||
for (unsigned int c = 0; c < n_channels; c++) {
|
||||
// Find c-th channel in layout that is not omitted
|
||||
unsigned int l_c = 0;
|
||||
for (unsigned int i = 0; i < 64; i++) {
|
||||
// If there are not enough non-omitted channels in the layout we end
|
||||
// up here and return
|
||||
if (layout[i] == GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Invalid defined layout %u with %u channels and omitted channels map %016"
|
||||
G_GINT64_MODIFIER "x", defined_layout, n_channels,
|
||||
omitted_channels_map);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// The i-th channel of the layout is included
|
||||
if (((omitted_channels_map >> i) & 1) == 0) {
|
||||
// The channel we're looking for
|
||||
if (l_c == c) {
|
||||
positions[c] = layout[l_c];
|
||||
break;
|
||||
}
|
||||
l_c += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are not enough non-omitted channels in the omitted
|
||||
// channels map then return here
|
||||
if (positions[c] == GST_AUDIO_CHANNEL_POSITION_INVALID) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Invalid defined layout %u with %u channels and omitted channels map %016"
|
||||
G_GINT64_MODIFIER "x", defined_layout, n_channels,
|
||||
omitted_channels_map);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stream carries objects
|
||||
if (stream_structure & 2) {
|
||||
guint8 object_count = base_channel_count - n_channels;
|
||||
GST_WARNING_OBJECT (qtdemux, "Stream carries %u objects", object_count);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Unsupported chnl version %u flags %06x", version, flags);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
{
|
||||
gchar *s = gst_audio_channel_positions_to_string (positions, n_channels);
|
||||
|
||||
GST_DEBUG_OBJECT (qtdemux, "Retrieved channel positions %s", s);
|
||||
|
||||
g_free (s);
|
||||
}
|
||||
#endif
|
||||
|
||||
guint64 channel_mask;
|
||||
GstAudioChannelPosition valid_positions[64];
|
||||
|
||||
if (!gst_audio_channel_positions_to_mask (positions, n_channels, FALSE,
|
||||
&channel_mask)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Can't convert channel positions to mask");
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy (valid_positions, positions, sizeof (positions[0]) * n_channels);
|
||||
if (!gst_audio_channel_positions_to_valid_order (valid_positions, n_channels)) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Can't convert channel positions to GStreamer channel order");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (n_channels > 1) {
|
||||
if (!gst_audio_get_channel_reorder_map (n_channels, positions,
|
||||
valid_positions, entry->reorder_map)) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Can't calculate channel reorder map");
|
||||
goto error;
|
||||
}
|
||||
entry->needs_reorder =
|
||||
memcmp (positions, valid_positions,
|
||||
sizeof (positions[0]) * n_channels) != 0;
|
||||
}
|
||||
|
||||
gst_caps_set_simple (entry->caps, "channel-mask", GST_TYPE_BITMASK,
|
||||
channel_mask, NULL);
|
||||
|
||||
// Update based on the actual channel count from this box
|
||||
entry->samples_per_frame = n_channels;
|
||||
entry->bytes_per_frame = n_channels * entry->bytes_per_sample;
|
||||
entry->samples_per_packet = entry->samples_per_frame;
|
||||
entry->bytes_per_packet = entry->bytes_per_sample;
|
||||
|
||||
stream->min_buffer_size = 1024 * entry->bytes_per_frame;
|
||||
stream->max_buffer_size = entry->rate * entry->bytes_per_frame;
|
||||
GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
|
||||
stream->max_buffer_size);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
{
|
||||
// Set a default channel mask on errors
|
||||
guint64 default_mask =
|
||||
gst_audio_channel_get_fallback_mask (entry->n_channels);
|
||||
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Configuring default channel mask for %u channels", entry->n_channels);
|
||||
|
||||
gst_caps_set_simple (entry->caps, "channel-mask", GST_TYPE_BITMASK,
|
||||
default_mask, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the traks.
|
||||
* With each track we associate a new QtDemuxStream that contains all the info
|
||||
* about the trak.
|
||||
|
@ -13304,6 +13881,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak, guint32 * mvhd_matrix)
|
|||
case FOURCC_sowt:
|
||||
case FOURCC_raw_:
|
||||
case FOURCC_lpcm:
|
||||
case FOURCC_ipcm:
|
||||
case FOURCC_fpcm:
|
||||
/* Sometimes these are set to 0 in the sound sample descriptions so
|
||||
* let's try to infer useful values from the other information we
|
||||
* have available */
|
||||
|
@ -13681,6 +14260,89 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak, guint32 * mvhd_matrix)
|
|||
|
||||
break;
|
||||
}
|
||||
case FOURCC_ipcm:
|
||||
case FOURCC_fpcm:
|
||||
{
|
||||
GNode *fmt;
|
||||
GNode *pcmC;
|
||||
|
||||
fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
|
||||
|
||||
pcmC = qtdemux_tree_get_child_by_type (fmt, FOURCC_pcmC);
|
||||
if (pcmC) {
|
||||
const guint8 *data = pcmC->data;
|
||||
gsize len = QT_UINT32 (data);
|
||||
if (len >= 8 + 6) {
|
||||
GstByteReader br = GST_BYTE_READER_INIT (data, len);
|
||||
|
||||
gst_byte_reader_skip_unchecked (&br, 4 + 4);
|
||||
|
||||
guint32 version_flags =
|
||||
gst_byte_reader_get_uint32_be_unchecked (&br);
|
||||
// Version 0, no flags
|
||||
if (version_flags == 0) {
|
||||
guint8 format_flags = gst_byte_reader_get_uint8_unchecked (&br);
|
||||
guint8 pcm_sample_size =
|
||||
gst_byte_reader_get_uint8_unchecked (&br);
|
||||
GstAudioFormat audio_format = GST_AUDIO_FORMAT_UNKNOWN;
|
||||
|
||||
if (fourcc == FOURCC_ipcm) {
|
||||
audio_format =
|
||||
gst_audio_format_build_integer (TRUE,
|
||||
(format_flags & 0x01) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN,
|
||||
pcm_sample_size, pcm_sample_size);
|
||||
} else {
|
||||
switch (pcm_sample_size) {
|
||||
case 32:
|
||||
audio_format =
|
||||
(format_flags & 0x01) ? GST_AUDIO_FORMAT_F32LE :
|
||||
GST_AUDIO_FORMAT_F32BE;
|
||||
break;
|
||||
case 64:
|
||||
audio_format =
|
||||
(format_flags & 0x01) ? GST_AUDIO_FORMAT_F64LE :
|
||||
GST_AUDIO_FORMAT_F64BE;
|
||||
break;
|
||||
default:
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Unsupported floating point PCM sample size %u",
|
||||
pcm_sample_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
gst_caps_set_simple (entry->caps,
|
||||
"format", G_TYPE_STRING,
|
||||
audio_format !=
|
||||
GST_AUDIO_FORMAT_UNKNOWN ?
|
||||
gst_audio_format_to_string (audio_format) : "UNKNOWN",
|
||||
NULL);
|
||||
|
||||
entry->bytes_per_sample = pcm_sample_size / 8;
|
||||
entry->samples_per_frame = entry->n_channels;
|
||||
entry->bytes_per_frame =
|
||||
entry->n_channels * entry->bytes_per_sample;
|
||||
entry->samples_per_packet = entry->samples_per_frame;
|
||||
entry->bytes_per_packet = entry->bytes_per_sample;
|
||||
|
||||
stream->min_buffer_size = 1024 * entry->bytes_per_frame;
|
||||
stream->max_buffer_size = entry->rate * entry->bytes_per_frame;
|
||||
GST_DEBUG ("setting min/max buffer sizes to %d/%d",
|
||||
stream->min_buffer_size, stream->max_buffer_size);
|
||||
|
||||
stream->alignment = pcm_sample_size / 8;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Unsupported pcmC version/flags %08x", version_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"%" GST_FOURCC_FORMAT " without pcmC box",
|
||||
GST_FOURCC_ARGS (fourcc));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -14037,15 +14699,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak, guint32 * mvhd_matrix)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FOURCC_opus:
|
||||
case FOURCC_opus:{
|
||||
/* Fully handled elsewhere */
|
||||
break;
|
||||
}
|
||||
case FOURCC_lpcm:
|
||||
case FOURCC_ipcm:
|
||||
case FOURCC_fpcm:
|
||||
case FOURCC_in24:
|
||||
case FOURCC_in32:
|
||||
case FOURCC_fl32:
|
||||
case FOURCC_fl64:
|
||||
case FOURCC_s16l:
|
||||
/* Fully handled elsewhere */
|
||||
case FOURCC_s16l:{
|
||||
GNode *fmt, *chnl;
|
||||
|
||||
// Parse channel layout information for raw PCM
|
||||
fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
|
||||
chnl = qtdemux_tree_get_child_by_type (fmt, FOURCC_chnl);
|
||||
|
||||
if (chnl) {
|
||||
const guint8 *data = chnl->data;
|
||||
gsize len = QT_UINT32 (data);
|
||||
if (len >= 8 + 4) {
|
||||
GstByteReader br = GST_BYTE_READER_INIT (data, len);
|
||||
// Skip over fourcc and length
|
||||
gst_byte_reader_skip_unchecked (&br, 4 + 4);
|
||||
qtdemux_parse_chnl (qtdemux, &br, stream, entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GST_INFO_OBJECT (qtdemux,
|
||||
"unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
||||
|
@ -16283,6 +16966,15 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FOURCC_ipcm:
|
||||
case FOURCC_fpcm:
|
||||
{
|
||||
_codec ("RAW PCM audio");
|
||||
caps =
|
||||
gst_caps_new_simple ("audio/x-raw", "layout", G_TYPE_STRING,
|
||||
"interleaved", NULL);
|
||||
break;
|
||||
}
|
||||
case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
|
||||
{
|
||||
_codec ("AC4");
|
||||
|
|
|
@ -347,6 +347,8 @@ struct _QtDemuxStreamStsdEntry
|
|||
guint bytes_per_sample;
|
||||
guint bytes_per_frame;
|
||||
guint compression;
|
||||
gboolean needs_reorder;
|
||||
gint reorder_map[64];
|
||||
|
||||
/* if we use chunks or samples */
|
||||
gboolean sampled;
|
||||
|
|
|
@ -249,6 +249,10 @@ static const QtNodeType qt_node_types[] = {
|
|||
{FOURCC_vttc, "VTTCueBox 14496-30", QT_FLAG_CONTAINER},
|
||||
{FOURCC_metx, "XML MetaData Sample Entry", 0},
|
||||
{FOURCC_cstb, "Correct Start Time Box", 0},
|
||||
{FOURCC_ipcm, "RAW Integer PCM", 0},
|
||||
{FOURCC_fpcm, "RAW Float PCM", 0},
|
||||
{FOURCC_pcmC, "RAW PCM Configuration Box", 0},
|
||||
{FOURCC_chnl, "Channel Layout Box", 0},
|
||||
{0, "unknown", 0,},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue