From 2ad30254c330d049e60f3457d305571af3cc4d6d Mon Sep 17 00:00:00 2001 From: David Evans Date: Thu, 17 Nov 2016 13:59:48 +0000 Subject: [PATCH] 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 --- gst/isomp4/fourcc.h | 2 + gst/isomp4/qtdemux.c | 92 ++++++++++++++++++++++++++++++++++++++ gst/isomp4/qtdemux_dump.c | 67 +++++++++++++++++++++++++++ gst/isomp4/qtdemux_dump.h | 4 ++ gst/isomp4/qtdemux_types.c | 2 + 5 files changed, 167 insertions(+) diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h index 43e4f50e19..7c8be4ccc1 100644 --- a/gst/isomp4/fourcc.h +++ b/gst/isomp4/fourcc.h @@ -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') diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 90e7351158..c61a586624 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 @@ 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"); diff --git a/gst/isomp4/qtdemux_dump.c b/gst/isomp4/qtdemux_dump.c index 880bb74cbe..09c6f4239c 100644 --- a/gst/isomp4/qtdemux_dump.c +++ b/gst/isomp4/qtdemux_dump.c @@ -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) { diff --git a/gst/isomp4/qtdemux_dump.h b/gst/isomp4/qtdemux_dump.h index 4234023b40..8b240de06a 100644 --- a/gst/isomp4/qtdemux_dump.h +++ b/gst/isomp4/qtdemux_dump.h @@ -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); diff --git a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c index 693ad5d1cc..8ce37ceb57 100644 --- a/gst/isomp4/qtdemux_types.c +++ b/gst/isomp4/qtdemux_types.c @@ -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},