Version
Media3 main branch
More version details
Reproduced on main (HEAD as of 2026-07-09) and confirmed present in all releases through 1.10.1. The relevant code (PsExtractor.PesReader.consume()) is unchanged across these versions.
Devices that reproduce the issue
Reproduced on main (HEAD as of 2026-07-09) and confirmed present in all releases through 1.10.1. The relevant code (PsExtractor.PesReader.consume()) is unchanged across these versions.
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Yes
Reproduction steps
Root cause: In an MPEG Program Stream, AC-3 (also DTS and LPCM) audio is carried in private_stream_1 (PES stream id 0xBD). In DVD-style content, each such PES packet begins with a 4-byte sub-stream header immediately after the PES header:
sub_stream_id (1 byte) // 0x80–0x87 = AC-3, 0x88–0x8F = DTS, 0xA0–0xA7 = LPCM
number_of_frame_headers (1 byte)
first_access_unit_pointer (2 bytes)
PsExtractor.PesReader.consume() strips the PES header + PTS/DTS extension but does not strip this 4-byte sub-stream header — it forwards it straight into Ac3Reader as if it were AC-3 elementary data:
https://github.com/androidx/media/blob/main/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java#L362-L370
Because AC-3 sync frames do not align to PES packet boundaries, the 4 stray bytes land inside the AC-3 frame that spans each PES boundary, corrupting that frame's bitstream. Ac3Reader re-syncs on the next 0x0B77 syncword, so the corruption is invisible to a frame-count/size check (every emitted frame still starts with a valid syncword) but every frame straddling a boundary is corrupt. ffmpeg's mpegps demuxer strips these bytes, which is why VLC/ffmpeg play the same content correctly. first_access_unit_pointer (which a correct demuxer uses to realign to the first frame that starts within the packet) is also unused.
Minimal desktop repro (no device needed) — feed the attached sample_ac3_ps.ps through PsExtractor with FakeExtractorInput/FakeExtractorOutput, concatenate the AC-3 track's samples, and decode with ffmpeg:
byte[] data = TestUtil.getByteArrayFromFilePath(".../sample_ac3_ps.ps");
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build();
FakeExtractorOutput output = new FakeExtractorOutput();
Extractor extractor = new PsExtractor();
extractor.init(output);
PositionHolder seek = new PositionHolder();
int result = Extractor.RESULT_CONTINUE;
while (result != Extractor.RESULT_END_OF_INPUT) {
result = extractor.read(input, seek);
if (result == Extractor.RESULT_SEEK) input.setPosition((int) seek.position);
}
FakeTrackOutput ac3 = output.trackOutputs.get(0xBD); // AC-3 track id = stream id
try (FileOutputStream fos = new FileOutputStream("/tmp/out.ac3")) {
for (int i = 0; i < ac3.getSampleCount(); i++) fos.write(ac3.getSampleData(i));
}
// then: ffmpeg -v error -i /tmp/out.ac3 -f null - -> many "error decoding" / BSI errors
The first private_stream_1 PES payload of the sample starts with 80 02 00 01 (the sub-stream header), then the AC-3 sync 0b 77 ....
Expected result
The AC-3 elementary stream extracted from private_stream_1 should be byte-for-byte the audio bitstream only (matching ffmpeg's -c:a copy output), decoding without errors.
Actual result
The extracted AC-3 stream is corrupted at every PES boundary by the un-stripped 4-byte sub-stream header, producing AC-3 decode errors and audibly garbled audio.
Measured on the attached 580 KB sample (PsExtractor output → decoded with ffmpeg's AC-3 decoder):
| AC-3 stream source |
AC-3 decode errors |
PsExtractor (current main) |
111 |
ffmpeg -c:a copy reference |
0 |
PsExtractor with sub-stream header stripped |
0 |
Media
sample_ac3_ps.mpeg.zip
Bug Report
Version
Media3 main branch
More version details
Reproduced on
main(HEAD as of 2026-07-09) and confirmed present in all releases through 1.10.1. The relevant code (PsExtractor.PesReader.consume()) is unchanged across these versions.Devices that reproduce the issue
Reproduced on
main(HEAD as of 2026-07-09) and confirmed present in all releases through 1.10.1. The relevant code (PsExtractor.PesReader.consume()) is unchanged across these versions.Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Yes
Reproduction steps
Root cause: In an MPEG Program Stream, AC-3 (also DTS and LPCM) audio is carried in
private_stream_1(PES stream id0xBD). In DVD-style content, each such PES packet begins with a 4-byte sub-stream header immediately after the PES header:PsExtractor.PesReader.consume()strips the PES header + PTS/DTS extension but does not strip this 4-byte sub-stream header — it forwards it straight intoAc3Readeras if it were AC-3 elementary data:https://github.com/androidx/media/blob/main/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java#L362-L370
Because AC-3 sync frames do not align to PES packet boundaries, the 4 stray bytes land inside the AC-3 frame that spans each PES boundary, corrupting that frame's bitstream.
Ac3Readerre-syncs on the next0x0B77syncword, so the corruption is invisible to a frame-count/size check (every emitted frame still starts with a valid syncword) but every frame straddling a boundary is corrupt. ffmpeg'smpegpsdemuxer strips these bytes, which is why VLC/ffmpeg play the same content correctly.first_access_unit_pointer(which a correct demuxer uses to realign to the first frame that starts within the packet) is also unused.Minimal desktop repro (no device needed) — feed the attached
sample_ac3_ps.psthroughPsExtractorwithFakeExtractorInput/FakeExtractorOutput, concatenate the AC-3 track's samples, and decode with ffmpeg:The first
private_stream_1PES payload of the sample starts with80 02 00 01(the sub-stream header), then the AC-3 sync0b 77 ....Expected result
The AC-3 elementary stream extracted from
private_stream_1should be byte-for-byte the audio bitstream only (matching ffmpeg's-c:a copyoutput), decoding without errors.Actual result
The extracted AC-3 stream is corrupted at every PES boundary by the un-stripped 4-byte sub-stream header, producing AC-3 decode errors and audibly garbled audio.
Measured on the attached 580 KB sample (
PsExtractoroutput → decoded with ffmpeg's AC-3 decoder):PsExtractor(currentmain)-c:a copyreferencePsExtractorwith sub-stream header strippedMedia
sample_ac3_ps.mpeg.zip
Bug Report
adb bugreportto android-media-github@google.com after filing this issue.