diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 271641431b8..e7b383bc6ab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,6 +5,9 @@ * Common Library: * ExoPlayer: * Transformer: +* Extractors: + * Fix media duration parsing in `mdhd` box of MP4 files to handle `-1` + values ([#1819](https://github.com/androidx/media/issues/1819)). * DataSource: * `DataSourceContractTest`: Assert that `DataSource.getUri()` returns the resolved URI (as documented). Where this is different to the requested diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java index 9b51d089d5b..8bc285fc2cc 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java @@ -958,14 +958,28 @@ private static MdhdData parseMdhd(ParsableByteArray mdhd) { int version = parseFullBoxVersion(fullAtom); mdhd.skipBytes(version == 0 ? 8 : 16); long timescale = mdhd.readUnsignedInt(); - long mediaDuration = version == 0 ? mdhd.readUnsignedInt() : mdhd.readUnsignedLongToLong(); + boolean mediaDurationUnknown = true; + int mediaDurationPosition = mdhd.getPosition(); + int mediaDurationByteCount = version == 0 ? 4 : 8; + for (int i = 0; i < mediaDurationByteCount; i++) { + if (mdhd.getData()[mediaDurationPosition + i] != -1) { + mediaDurationUnknown = false; + break; + } + } long mediaDurationUs; - if (mediaDuration == 0) { - // 0 duration normally indicates that the file is fully fragmented (i.e. all of the media - // samples are in fragments). Treat as unknown. + if (mediaDurationUnknown) { + mdhd.skipBytes(mediaDurationByteCount); mediaDurationUs = C.TIME_UNSET; } else { - mediaDurationUs = Util.scaleLargeTimestamp(mediaDuration, C.MICROS_PER_SECOND, timescale); + long mediaDuration = version == 0 ? mdhd.readUnsignedInt() : mdhd.readUnsignedLongToLong(); + if (mediaDuration == 0) { + // 0 duration normally indicates that the file is fully fragmented (i.e. all of the media + // samples are in fragments). Treat as unknown. + mediaDurationUs = C.TIME_UNSET; + } else { + mediaDurationUs = Util.scaleLargeTimestamp(mediaDuration, C.MICROS_PER_SECOND, timescale); + } } int languageCode = mdhd.readUnsignedShort(); String language =