qtdemux: Add support for FLAC encapsulated in ISOBMFF

As defined by
https://git.xiph.org/?p=flac.git;a=blob_plain;f=doc/isoflac.txt

https://bugzilla.gnome.org/show_bug.cgi?id=773712
This commit is contained in:
David Evans 2016-11-17 13:59:48 +00:00 committed by Sebastian Dröge
parent 15630db146
commit 2ad30254c3
5 changed files with 167 additions and 0 deletions

View file

@ -86,6 +86,8 @@ G_BEGIN_DECLS
#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_fLaC GST_MAKE_FOURCC('f','L','a','C')
#define FOURCC_dfLa GST_MAKE_FOURCC('d','f','L','a')
#define FOURCC_alaw GST_MAKE_FOURCC('a','l','a','w')
#define FOURCC_alis GST_MAKE_FOURCC('a','l','i','s')
#define FOURCC_appl GST_MAKE_FOURCC('a','p','p','l')

View file

@ -9,6 +9,7 @@
* Copyright (C) <2013> Intel Corporation
* Copyright (C) <2014> Centricular Ltd
* Copyright (C) <2015> YouView TV Ltd.
* Copyright (C) <2016> British Broadcasting Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -7076,6 +7077,7 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
}
case FOURCC_mp4a:
case FOURCC_alac:
case FOURCC_fLaC:
{
guint32 version;
guint32 offset;
@ -7085,6 +7087,8 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
* since a similar layout is used in other cases as well */
if (fourcc == FOURCC_mp4a)
min_size = 20;
else if (fourcc == FOURCC_fLaC)
min_size = 86;
else
min_size = 40;
@ -10858,6 +10862,89 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
"samplesize", G_TYPE_INT, samplesize, NULL);
break;
}
case FOURCC_fLaC:
{
/* The codingname of the sample entry is 'fLaC' */
GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
if (flac) {
/* The 'dfLa' box is added to the sample entry to convey
initializing information for the decoder. */
const GNode *dfla =
qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
if (dfla) {
const guint32 len = QT_UINT32 (dfla->data);
/* Must contain at least dfLa box header (12),
* METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
if (len < 50) {
GST_DEBUG_OBJECT (qtdemux,
"discarding dfla atom with unexpected len %d", len);
} else {
/* skip dfLa header to get the METADATA_BLOCKs */
const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
const guint32 metadata_blocks_len = len - 12;
gchar *stream_marker = g_strdup ("fLaC");
GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
strlen (stream_marker));
guint index = 0;
gboolean is_last = FALSE;
GValue array = G_VALUE_INIT;
GValue value = G_VALUE_INIT;
g_value_init (&array, GST_TYPE_ARRAY);
g_value_init (&value, GST_TYPE_BUFFER);
gst_value_set_buffer (&value, block);
gst_value_array_append_value (&array, &value);
g_value_reset (&value);
gst_buffer_unref (block);
while (is_last == FALSE && index < metadata_blocks_len) {
/* add the METADATA_BLOCK_HEADER size to the signalled size */
const guint block_size = 4 +
(metadata_blocks[index + 1] << 16) +
(metadata_blocks[index + 2] << 8) +
metadata_blocks[index + 3];
is_last = metadata_blocks[index] >> 7;
block = gst_buffer_new_and_alloc (block_size);
gst_buffer_fill (block, 0, &metadata_blocks[index],
block_size);
gst_value_set_buffer (&value, block);
gst_value_array_append_value (&array, &value);
g_value_reset (&value);
gst_buffer_unref (block);
index += block_size;
}
gst_structure_set_value (gst_caps_get_structure (stream->caps,
0), "streamheader", &array);
g_value_unset (&value);
g_value_unset (&array);
/* The sample rate obtained from the stsd may not be accurate
* since it cannot represent rates greater than 65535Hz, so
* override that value with the sample rate from the
* METADATA_BLOCK_STREAMINFO block */
stream->rate =
(QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
}
}
}
break;
}
case FOURCC_sawb:
/* Fallthrough! */
amrwb = TRUE;
@ -13615,6 +13702,11 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
_codec ("Apple lossless audio");
caps = gst_caps_new_empty_simple ("audio/x-alac");
break;
case FOURCC_fLaC:
_codec ("Free Lossless Audio Codec");
caps = gst_caps_new_simple ("audio/x-flac",
"framed", G_TYPE_BOOLEAN, TRUE, NULL);
break;
case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
_codec ("QualComm PureVoice");
caps = gst_caps_from_string ("audio/qcelp");

View file

@ -340,6 +340,9 @@ qtdemux_dump_stsd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
if (!qtdemux_dump_stsd_avc1 (qtdemux, &sub, size, depth + 1))
return FALSE;
break;
case FOURCC_fLaC:
/* will be dumped by node_dump_foreach */
break;
case FOURCC_mp4s:
if (!gst_byte_reader_get_uint32_be (&sub, &ver_flags) ||
!gst_byte_reader_get_uint32_be (&sub, &num_entries))
@ -890,6 +893,70 @@ qtdemux_dump_svmi (GstQTDemux * qtdemux, GstByteReader * data, int depth)
return TRUE;
}
gboolean
qtdemux_dump_dfLa (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{
const gchar *block_types[] = {
"STREAMINFO", "PADDING", "APPLICATION", "SEEKTABLE", "VORBIS_COMMENT",
"CUESHEET", "PICTURE", "UNKNOWN", "INVALID"
};
guint32 ver_flags, block_header, block_size;
gint8 block_type;
gboolean isLast = FALSE;
if (!gst_byte_reader_get_uint32_be (data, &ver_flags))
return FALSE;
GST_LOG ("%*s version/flags: %08x", depth, "", ver_flags);
do {
if (!gst_byte_reader_get_uint32_be (data, &block_header))
break;
isLast = (block_header >> 31) & 1;
block_type = (block_header >> 24) & 0x7F;
block_size = block_header & 0xFFFFFF;
if (block_type == 127)
block_type = 8;
else if (block_type > 6)
block_type = 7;
GST_LOG ("%*s block_type: %s", depth, "", block_types[block_type]);
GST_LOG ("%*s last-block-flag: %s", depth, "", isLast ? "true" : "false");
GST_LOG ("%*s length: %d", depth, "", block_size);
if (!gst_byte_reader_skip (data, block_size))
break;
} while (!isLast);
return TRUE;
}
gboolean
qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{
guint16 data_ref_id, n_channels, sample_size;
guint32 sample_rate;
if (!gst_byte_reader_skip (data, 6) ||
!gst_byte_reader_get_uint16_be (data, &data_ref_id) ||
!gst_byte_reader_skip (data, 8) ||
!gst_byte_reader_get_uint16_be (data, &n_channels) ||
!gst_byte_reader_get_uint16_be (data, &sample_size) ||
!gst_byte_reader_skip (data, 4) ||
!gst_byte_reader_get_uint32_be (data, &sample_rate))
return FALSE;
GST_LOG ("%*s data reference: %d", depth, "", data_ref_id);
GST_LOG ("%*s channel count: %d", depth, "", n_channels);
GST_LOG ("%*s sample size: %d", depth, "", sample_size);
GST_LOG ("%*s sample rate: %d", depth, "", (sample_rate >> 16));
return TRUE;
}
gboolean
qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{

View file

@ -85,6 +85,10 @@ gboolean qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_svmi (GstQTDemux *qtdemux, GstByteReader *data,
int depth);
gboolean qtdemux_dump_dfLa (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_node_dump (GstQTDemux * qtdemux, GNode * node);

View file

@ -89,6 +89,8 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_fiel, "fiel", 0,},
{FOURCC_jp2x, "jp2x", 0,},
{FOURCC_alac, "alac", 0,},
{FOURCC_fLaC, "fLaC", 0, qtdemux_dump_fLaC},
{FOURCC_dfLa, "dfLa", 0, qtdemux_dump_dfLa},
{FOURCC_wave, "wave", QT_FLAG_CONTAINER},
{FOURCC_appl, "appl", QT_FLAG_CONTAINER},
{FOURCC_esds, "esds", 0},