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
diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h
index 43e4f50..7c8be4c 100644
--- a/gst/isomp4/fourcc.h
+++ b/gst/isomp4/fourcc.h
@@ -86,6 +86,8 @@
#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')
diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
index 90e7351..c61a586 100644
--- a/gst/isomp4/qtdemux.c
+++ b/gst/isomp4/qtdemux.c
@@ -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 @@
}
case FOURCC_mp4a:
case FOURCC_alac:
+ case FOURCC_fLaC:
{
guint32 version;
guint32 offset;
@@ -7085,6 +7087,8 @@
* 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 @@
"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 @@
_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");
diff --git a/gst/isomp4/qtdemux_dump.c b/gst/isomp4/qtdemux_dump.c
index 880bb74..09c6f42 100644
--- a/gst/isomp4/qtdemux_dump.c
+++ b/gst/isomp4/qtdemux_dump.c
@@ -340,6 +340,9 @@
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))
@@ -891,6 +894,70 @@
}
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)
{
int len;
diff --git a/gst/isomp4/qtdemux_dump.h b/gst/isomp4/qtdemux_dump.h
index 4234023..8b240de 100644
--- a/gst/isomp4/qtdemux_dump.h
+++ b/gst/isomp4/qtdemux_dump.h
@@ -85,6 +85,10 @@
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);
diff --git a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c
index 693ad5d..8ce37ce 100644
--- a/gst/isomp4/qtdemux_types.c
+++ b/gst/isomp4/qtdemux_types.c
@@ -89,6 +89,8 @@
{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},