avformat/mpeg: fix demuxing of DVD-Audio LPCM

Substream 0xa0 packets without the DVD-Video dynamic range control
marker byte were assumed to be MLP, while in DVD-Audio AOBs substream
0xa0 always carries LPCM (MLP uses 0xa1), so hi-res LPCM streams were
misdetected and decoded as garbage. Classify them as PCM_DVDA and keep
the private stream header in the packet for the decoder to parse, as
its length is variable and it carries the audio format.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
This commit is contained in:
Kacper Michajłow
2026-07-04 20:00:59 +02:00
parent 047eb18451
commit ff818b87bf

View File

@@ -488,6 +488,7 @@ static int mpegps_read_packet(AVFormatContext *s,
FFStream *sti;
int len, startcode, i, es_type, ret;
int pcm_dvd = 0;
int pcm_dvda = 0;
int request_probe= 0;
enum AVCodecID codec_id = AV_CODEC_ID_NONE;
enum AVMediaType type;
@@ -503,19 +504,33 @@ redo:
goto skip;
if (!m->raw_ac3) {
/* audio: skip header */
avio_skip(s->pb, 3);
len -= 3;
if (startcode >= 0xb0 && startcode <= 0xbf) {
/* MLP/TrueHD audio has a 4-byte header */
avio_r8(s->pb);
len--;
} else if (startcode >= 0xa0 && startcode <= 0xaf) {
ret = ffio_ensure_seekback(s->pb, 3);
if (startcode >= 0xa0 && startcode <= 0xaf) {
uint8_t header[6];
/* Classify the LPCM/MLP substream here; its header is
* skipped at "found" below, once the stream codec is known. */
if (len < 6)
goto skip;
ret = ffio_ensure_seekback(s->pb, 6);
if (ret < 0)
return ret;
pcm_dvd = (avio_rb24(s->pb) & 0xFF) == 0x80;
avio_skip(s->pb, -3);
if (avio_read(s->pb, header, 6) != 6)
return AVERROR_INVALIDDATA;
avio_seek(s->pb, -6, SEEK_CUR);
/* DVD-Video LPCM has the dynamic range control byte here, while
* DVD-Audio LPCM and MLP do not. Only substream 0xa0 carries
* LPCM in DVD-Audio AOBs, MLP uses substream 0xa1. */
pcm_dvd = header[5] == 0x80;
pcm_dvda = startcode == 0xa0 && !pcm_dvd;
} else {
/* audio: skip header */
avio_skip(s->pb, 3);
len -= 3;
if (startcode >= 0xb0 && startcode <= 0xbf) {
/* MLP/TrueHD audio has a 4-byte header */
avio_r8(s->pb);
len--;
}
}
}
}
@@ -602,7 +617,9 @@ redo:
codec_id = AV_CODEC_ID_DTS;
} else if (startcode >= 0xa0 && startcode <= 0xaf) {
type = AVMEDIA_TYPE_AUDIO;
if (!pcm_dvd) {
if (pcm_dvda) {
codec_id = AV_CODEC_ID_PCM_DVDA;
} else if (!pcm_dvd) {
codec_id = AV_CODEC_ID_MLP;
} else {
codec_id = AV_CODEC_ID_PCM_DVD;
@@ -648,13 +665,26 @@ skip:
found:
if (st->discard >= AVDISCARD_ALL)
goto skip;
if (startcode >= 0xa0 && startcode <= 0xaf) {
if (st->codecpar->codec_id == AV_CODEC_ID_MLP) {
if (len < 6)
goto skip;
avio_skip(s->pb, 6);
len -=6;
}
if (startcode >= 0xa0 && startcode <= 0xaf && !m->raw_ac3) {
int header_len = 0;
/* Skip the substream headers of codecs whose decoders do not expect
* them. AV_CODEC_ID_PCM_DVDA parses the header from the packet. */
if (st->codecpar->codec_id == AV_CODEC_ID_MLP) {
/* 3-byte substream header + 6-byte MLP header */
header_len = 9;
} else if (st->codecpar->codec_id == AV_CODEC_ID_PCM_DVD) {
header_len = 3;
}
if (len <= header_len)
goto skip;
avio_skip(s->pb, header_len);
len -= header_len;
} else if (startcode >= 0xa0 && startcode <= 0xaf &&
st->codecpar->codec_id == AV_CODEC_ID_MLP) {
if (len < 6)
goto skip;
avio_skip(s->pb, 6);
len -= 6;
}
ret = av_get_packet(s->pb, pkt, len);