mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
qtdemux: parse Opus and dOps as qtdemux nodes and add size checks
This allows checking the nodes conformity and dumping parsed values. Note: Audio Sample Entry version parsing and offset handling is handled as part of `FOURCC_soun` common processing and in `qtdemux_parse_node`. Also, only read `stream_count` and `coupled_count` when `channel_mapping_family` != 0. See: https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4891>
This commit is contained in:
parent
8d2dc95567
commit
670ce68f06
4 changed files with 172 additions and 19 deletions
|
@ -8301,6 +8301,7 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
|
|||
case FOURCC_alac:
|
||||
case FOURCC_fLaC:
|
||||
case FOURCC_aavd:
|
||||
case FOURCC_opus:
|
||||
{
|
||||
guint32 version;
|
||||
guint32 offset;
|
||||
|
@ -8312,6 +8313,8 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
|
|||
min_size = 20;
|
||||
else if (fourcc == FOURCC_fLaC)
|
||||
min_size = 86;
|
||||
else if (fourcc == FOURCC_opus)
|
||||
min_size = 55;
|
||||
else
|
||||
min_size = 40;
|
||||
|
||||
|
@ -12883,34 +12886,95 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
|
|||
}
|
||||
case FOURCC_opus:
|
||||
{
|
||||
const guint8 *dops_data;
|
||||
guint8 *channel_mapping = NULL;
|
||||
guint32 rate;
|
||||
guint8 channels;
|
||||
guint32 dops_len, rate;
|
||||
guint8 n_channels;
|
||||
guint8 channel_mapping_family;
|
||||
guint8 stream_count;
|
||||
guint8 coupled_count;
|
||||
guint8 i;
|
||||
|
||||
version = GST_READ_UINT16_BE (stsd_entry_data + 16);
|
||||
if (version == 1)
|
||||
dops_data = stsd_entry_data + 51;
|
||||
else
|
||||
dops_data = stsd_entry_data + 35;
|
||||
GNode *opus;
|
||||
GNode *dops;
|
||||
|
||||
channels = GST_READ_UINT8 (dops_data + 10);
|
||||
rate = GST_READ_UINT32_BE (dops_data + 13);
|
||||
channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
|
||||
stream_count = GST_READ_UINT8 (dops_data + 20);
|
||||
coupled_count = GST_READ_UINT8 (dops_data + 21);
|
||||
|
||||
if (channels > 0) {
|
||||
channel_mapping = g_malloc (channels * sizeof (guint8));
|
||||
for (i = 0; i < channels; i++)
|
||||
channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
|
||||
opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
|
||||
if (opus == NULL) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Opus Sample Entry not found");
|
||||
goto corrupt_file;
|
||||
}
|
||||
|
||||
entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
|
||||
dops = qtdemux_tree_get_child_by_type (opus, FOURCC_dops);
|
||||
if (dops == NULL) {
|
||||
GST_WARNING_OBJECT (qtdemux, "Opus Specific Box not found");
|
||||
goto corrupt_file;
|
||||
}
|
||||
|
||||
/* Opus Specific Box content:
|
||||
* 4 bytes: length
|
||||
* 4 bytes: "dOps"
|
||||
* 1 byte: Version;
|
||||
* 1 byte: OutputChannelCount;
|
||||
* 2 bytes: PreSkip (big-endians);
|
||||
* 4 bytes: InputSampleRate (big-endians);
|
||||
* 2 bytes: OutputGain (big-endians);
|
||||
* 1 byte: ChannelMappingFamily;
|
||||
* if (ChannelMappingFamily != 0) {
|
||||
* 1 byte: StreamCount;
|
||||
* 1 byte: CoupledCount;
|
||||
* for (OutputChannel in 0..OutputChannelCount) {
|
||||
* 1 byte: ChannelMapping;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
dops_len = QT_UINT32 ((guint8 *) dops->data);
|
||||
if (len < offset + dops_len) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Opus Sample Entry has bogus size %" G_GUINT32_FORMAT, len);
|
||||
goto corrupt_file;
|
||||
}
|
||||
if (dops_len < 19) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
|
||||
dops_len);
|
||||
goto corrupt_file;
|
||||
}
|
||||
|
||||
n_channels = GST_READ_UINT8 ((guint8 *) dops->data + 9);
|
||||
rate = GST_READ_UINT32_BE ((guint8 *) dops->data + 12);
|
||||
channel_mapping_family = GST_READ_UINT8 ((guint8 *) dops->data + 18);
|
||||
|
||||
if (channel_mapping_family != 0) {
|
||||
if (dops_len < 21 + n_channels) {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
|
||||
dops_len);
|
||||
goto corrupt_file;
|
||||
}
|
||||
|
||||
stream_count = GST_READ_UINT8 ((guint8 *) dops->data + 19);
|
||||
coupled_count = GST_READ_UINT8 ((guint8 *) dops->data + 20);
|
||||
|
||||
if (n_channels > 0) {
|
||||
channel_mapping = g_malloc (n_channels * sizeof (guint8));
|
||||
for (i = 0; i < n_channels; i++)
|
||||
channel_mapping[i] =
|
||||
GST_READ_UINT8 ((guint8 *) dops->data + i + 21);
|
||||
}
|
||||
} else if (n_channels == 1) {
|
||||
stream_count = 1;
|
||||
coupled_count = 0;
|
||||
} else if (n_channels == 2) {
|
||||
stream_count = 1;
|
||||
coupled_count = 1;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (qtdemux,
|
||||
"Opus unexpected nb of channels %d without channel mapping",
|
||||
n_channels);
|
||||
goto corrupt_file;
|
||||
}
|
||||
|
||||
entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
|
||||
channel_mapping_family, stream_count, coupled_count,
|
||||
channel_mapping);
|
||||
g_free (channel_mapping);
|
||||
|
@ -13266,6 +13330,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FOURCC_opus:
|
||||
case FOURCC_lpcm:
|
||||
case FOURCC_in24:
|
||||
case FOURCC_in32:
|
||||
|
|
|
@ -1053,6 +1053,88 @@ qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data, int depth)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qtdemux_dump_opus (GstQTDemux * qtdemux, GstByteReader * data, int depth)
|
||||
{
|
||||
guint16 version, 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_get_uint16_be (data, &version) ||
|
||||
!gst_byte_reader_skip (data, 6) ||
|
||||
!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 version: %d", depth, "", version);
|
||||
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_dops (GstQTDemux * qtdemux, GstByteReader * data, int depth)
|
||||
{
|
||||
guint8 version, n_channels, channel_mapping_family;
|
||||
guint8 stream_count = 1, coupled_count = 0, i = 0;
|
||||
guint8 *channel_mapping = NULL;
|
||||
guint16 pre_skip, output_gain;
|
||||
guint32 sample_rate;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (data, &version) ||
|
||||
!gst_byte_reader_get_uint8 (data, &n_channels) ||
|
||||
!gst_byte_reader_get_uint16_be (data, &pre_skip) ||
|
||||
!gst_byte_reader_get_uint32_be (data, &sample_rate) ||
|
||||
!gst_byte_reader_get_uint16_be (data, &output_gain) ||
|
||||
!gst_byte_reader_get_uint8 (data, &channel_mapping_family))
|
||||
return FALSE;
|
||||
|
||||
if (channel_mapping_family != 0) {
|
||||
if (!gst_byte_reader_get_uint8 (data, &stream_count) ||
|
||||
!gst_byte_reader_get_uint8 (data, &coupled_count))
|
||||
return FALSE;
|
||||
|
||||
if (n_channels > 0) {
|
||||
channel_mapping = g_malloc (n_channels * sizeof (guint8));
|
||||
|
||||
for (i = 0; i < n_channels; i++)
|
||||
if (!gst_byte_reader_get_uint8 (data, &channel_mapping[i])) {
|
||||
g_free (channel_mapping);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG ("%*s version: %d", depth, "", version);
|
||||
GST_LOG ("%*s channel count: %d", depth, "", n_channels);
|
||||
GST_LOG ("%*s pre skip: %d", depth, "", pre_skip);
|
||||
GST_LOG ("%*s sample rate: %d", depth, "", sample_rate);
|
||||
GST_LOG ("%*s output gain: %d", depth, "", output_gain);
|
||||
GST_LOG ("%*s channel mapping family: %d", depth, "",
|
||||
channel_mapping_family);
|
||||
|
||||
if (channel_mapping_family != 0) {
|
||||
GST_LOG ("%*s stream count: %d", depth, "", stream_count);
|
||||
GST_LOG ("%*s coupled count: %d", depth, "", coupled_count);
|
||||
|
||||
if (n_channels > 0) {
|
||||
for (i = 0; i < n_channels; i++)
|
||||
GST_LOG ("%*s channel mapping: %d -> %d", depth, "", i,
|
||||
channel_mapping[i]);
|
||||
|
||||
g_free (channel_mapping);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qtdemux_dump_gmin (GstQTDemux * qtdemux, GstByteReader * data, int depth)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,10 @@ gboolean qtdemux_dump_dfLa (GstQTDemux * qtdemux, GstByteReader * data,
|
|||
int depth);
|
||||
gboolean qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data,
|
||||
int depth);
|
||||
gboolean qtdemux_dump_opus (GstQTDemux * qtdemux, GstByteReader * data,
|
||||
int depth);
|
||||
gboolean qtdemux_dump_dops (GstQTDemux * qtdemux, GstByteReader * data,
|
||||
int depth);
|
||||
gboolean qtdemux_dump_gmin (GstQTDemux * qtdemux, GstByteReader * data,
|
||||
int depth);
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ static const QtNodeType qt_node_types[] = {
|
|||
{FOURCC_alac, "alac", 0,},
|
||||
{FOURCC_fLaC, "fLaC", 0, qtdemux_dump_fLaC},
|
||||
{FOURCC_dfLa, "dfLa", 0, qtdemux_dump_dfLa},
|
||||
{FOURCC_opus, "opus", 0, qtdemux_dump_opus},
|
||||
{FOURCC_dops, "dOps", 0, qtdemux_dump_dops},
|
||||
{FOURCC_wave, "wave", QT_FLAG_CONTAINER},
|
||||
{FOURCC_appl, "appl", QT_FLAG_CONTAINER},
|
||||
{FOURCC_cfhd, "cfhd", QT_FLAG_CONTAINER},
|
||||
|
|
Loading…
Reference in a new issue