summaryrefslogtreecommitdiffstats
path: root/demux/demux_mkv.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-07-20 12:48:41 +0200
committerwm4 <wm4@nowhere>2015-07-20 12:56:35 +0200
commit3252d352c901fb55f92d24385776374c25bcd704 (patch)
tree673dc600f46047bfc1b15fd281a76b8099f2c771 /demux/demux_mkv.c
parentb0d1ac93cf07ad32b53a6d1f54ee9974d61539bd (diff)
downloadmpv-3252d352c901fb55f92d24385776374c25bcd704.tar.bz2
mpv-3252d352c901fb55f92d24385776374c25bcd704.tar.xz
demux_mkv: parse FLAC channel layouts
Handle a relatively recently introduced hack, that allows FLAC audio to have arbitrary channel layouts, instead of just the predefined fixed ones. This is actually supported by FFmpeg, but since the demuxer (instead of the decoder) handles this in FFmpeg, we need to add special- code to our mkv demuxer. (The way FFmpeg does this seems a bit backwards, since now every demuxer for a format that can handle FLAC needs to contain this logic as well.) The FLAC hack is relatively terrible: we need to parse the FLAC headers, look for a VorbisComment, parse the VorbisComment, and then retrieve the magic WAVEFORMATEXTENSIBLE_CHANNEL_MASK entry. But the hack is officially endorsed, as the official FLAC tools use it. (Although I couldn't find a trace of it in the format specification. Should I be surprised?)
Diffstat (limited to 'demux/demux_mkv.c')
-rw-r--r--demux/demux_mkv.c67
1 files changed, 67 insertions, 0 deletions
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 089801da4e..f6eabf71b6 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -1345,6 +1345,72 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
return 0;
}
+// Parse VorbisComment and look for WAVEFORMATEXTENSIBLE_CHANNEL_MASK.
+// Do not change *channels if nothing found or an error happens.
+static void parse_vorbis_chmap(struct mp_chmap *channels, unsigned char *data,
+ int size)
+{
+ // Skip the useless vendor string.
+ if (size < 4)
+ return;
+ uint32_t vendor_length = AV_RL32(data);
+ if (vendor_length + 4 > size) // also check for the next AV_RB32 below
+ return;
+ size -= vendor_length + 4;
+ data += vendor_length + 4;
+ uint32_t num_headers = AV_RL32(data);
+ size -= 4;
+ data += 4;
+ for (int n = 0; n < num_headers; n++) {
+ if (size < 4)
+ return;
+ uint32_t len = AV_RL32(data);
+ size -= 4;
+ data += 4;
+ if (len > size)
+ return;
+ if (len > 34 && !memcmp(data, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK=", 34)) {
+ char smask[80];
+ snprintf(smask, sizeof(smask), "%.*s", (int)(len - 34), data + 34);
+ char *end = NULL;
+ uint32_t mask = strtol(smask, &end, 0);
+ if (!end || end[0])
+ mask = 0;
+ struct mp_chmap chmask = {0};
+ mp_chmap_from_waveext(&chmask, mask);
+ if (mp_chmap_is_valid(&chmask))
+ *channels = chmask;
+ }
+ size -= len;
+ data += len;
+ }
+}
+
+// Parse VorbisComment-in-FLAC and look for WAVEFORMATEXTENSIBLE_CHANNEL_MASK.
+// Do not change *channels if nothing found or an error happens.
+static void parse_flac_chmap(struct mp_chmap *channels, unsigned char *data,
+ int size)
+{
+ // Skip FLAC header.
+ if (size < 4)
+ return;
+ data += 4;
+ size -= 4;
+ // Parse FLAC blocks...
+ while (size >= 4) {
+ unsigned btype = data[0] & 0x7F;
+ unsigned bsize = AV_RB24(data + 1);
+ data += 4;
+ size -= 4;
+ if (bsize > size)
+ return;
+ if (btype == 4) // VORBIS_COMMENT
+ parse_vorbis_chmap(channels, data, bsize);
+ data += bsize;
+ size -= bsize;
+ }
+}
+
static const char *const mkv_audio_tags[][2] = {
{ "A_MPEG/L2", "mp3" },
{ "A_MPEG/L3", "mp3" },
@@ -1547,6 +1613,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
extradata_len = size;
memcpy(extradata, ptr, size);
}
+ parse_flac_chmap(&sh_a->channels, extradata, extradata_len);
} else if (!strcmp(codec, "alac")) {
if (track->private_size) {
extradata_len = track->private_size + 12;