[FFmpeg-devel] [PATCH 2/2] avformat/riffdec: follow the MS docs more strictly for setting wav channel layouts
Marton Balint
cus at passwd.hu
Sun Mar 17 21:57:29 EET 2024
- Only parse the defined masks in dwChannelMask, unless strict_std_compliance
is less than normal. This matches with the behaviour of the wav muxer.
- Ignore additional bits in dwChannelMasks as the MS documentation suggests [1]
- Assume UNKNOWN channels for missing bits as the MS documentation suggests [1]
[1] https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85)#details-about-dwchannelmask
Signed-off-by: Marton Balint <cus at passwd.hu>
---
libavformat/riffdec.c | 28 +++++++++++++++++++++++++---
1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c
index 6cc912133c..0c7b2c6bdb 100644
--- a/libavformat/riffdec.c
+++ b/libavformat/riffdec.c
@@ -58,7 +58,7 @@ enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid)
* an openended structure.
*/
-static void parse_waveformatex(AVFormatContext *ctx, AVIOContext *pb, AVCodecParameters *par)
+static int parse_waveformatex(AVFormatContext *ctx, AVIOContext *pb, AVCodecParameters *par, int channels)
{
ff_asf_guid subformat;
int bps;
@@ -69,7 +69,26 @@ static void parse_waveformatex(AVFormatContext *ctx, AVIOContext *pb, AVCodecPar
par->bits_per_coded_sample = bps;
mask = avio_rl32(pb); /* dwChannelMask */
- av_channel_layout_from_mask(&par->ch_layout, mask);
+ if (ctx->strict_std_compliance >= FF_COMPLIANCE_NORMAL)
+ mask &= 0x3ffff;
+
+ /* According to MS docs, most significant bits should be ignored if mask
+ * has more bits than the number of channels */
+ while (av_popcount64(mask) > channels)
+ mask &= ~(1LL << av_log2(mask));
+
+ /* If the mask has less bits than channels, then we assign the remaining
+ * channels as UNKNOWN. */
+ if (mask && av_popcount64(mask) < channels) {
+ int ret = av_channel_layout_custom_init(&par->ch_layout, channels);
+ if (ret < 0)
+ return ret;
+ for (int i = 0, idx = 0; mask; i++, mask >>= 1)
+ if (mask & 1)
+ par->ch_layout.u.map[idx++].id = i;
+ } else {
+ av_channel_layout_from_mask(&par->ch_layout, mask);
+ }
ff_get_guid(pb, &subformat);
if (!memcmp(subformat + 4,
@@ -88,6 +107,7 @@ static void parse_waveformatex(AVFormatContext *ctx, AVIOContext *pb, AVCodecPar
"unknown subformat:"FF_PRI_GUID"\n",
FF_ARG_GUID(subformat));
}
+ return 0;
}
/* "big_endian" values are needed for RIFX file format */
@@ -145,7 +165,9 @@ int ff_get_wav_header(AVFormatContext *ctx, AVIOContext *pb,
size -= 18;
cbSize = FFMIN(size, cbSize);
if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */
- parse_waveformatex(ctx, pb, par);
+ ret = parse_waveformatex(ctx, pb, par, channels);
+ if (ret < 0)
+ return ret;
cbSize -= 22;
size -= 22;
}
--
2.35.3
More information about the ffmpeg-devel
mailing list