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>
case FOURCC_alac:
case FOURCC_fLaC:
case FOURCC_aavd:
+ case FOURCC_opus:
{
guint32 version;
guint32 offset;
min_size = 20;
else if (fourcc == FOURCC_fLaC)
min_size = 86;
+ else if (fourcc == FOURCC_opus)
+ min_size = 55;
else
min_size = 40;
}
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;
-
- 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);
+ GNode *opus;
+ GNode *dops;
+
+ 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;
+ }
+
+ 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, channels,
+ entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
channel_mapping_family, stream_count, coupled_count,
channel_mapping);
g_free (channel_mapping);
}
break;
}
+ case FOURCC_opus:
case FOURCC_lpcm:
case FOURCC_in24:
case FOURCC_in32:
}
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)
{
guint32 ver_flags;
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);
{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},