-
Notifications
You must be signed in to change notification settings - Fork 749
Description
Version
Media3 1.8.0
More version details
No response
Devices that reproduce the issue
Pixel 8 Pro (emulator, API 36), physical Pixel 8 Pro (Android 16.0)
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Yes
Reproduction steps
- Produce a fragmented MP4 file using FFmpeg with standard flags:
ffmpeg -i input.wav -c:a aac -b:a 128k -movflags empty_moov+default_base_moof+separate_moof -frag_duration 1000000 output.mp4 - Play the file using
ProgressiveMediaSourcewith default extractors (or viaexoPlayer.setMediaItem()) - Attempt to seek
Expected result
Seeking works. The file contains an mfra (Movie Fragment Random Access) box at the end, which is defined in ISO 14496-12 §8.8.9 specifically to enable random access in fragmented MP4 files. The mfra contains tfra entries mapping presentation times to moof byte offsets, and an mfro trailer for locating the mfra from the end of the file.
The file structure is:
ftyp size=28 offset=0
moov size=701 offset=28 (empty moov, as expected for fMP4)
moof size=276 offset=729 <-- ExoPlayer sees this, outputs Unseekable
mdat size=16333 offset=1005
... (30 moof+mdat fragment pairs) ...
mfra size=618 offset=493039 <-- contains full tfra index, ignored by ExoPlayer
Other players handle this correctly:
- iOS AVPlayer: seeks within fMP4 files
- Web browsers via MSE: seek within buffered fMP4 ranges
- FFmpeg/mpv: seek using the mfra index
Actual result
FragmentedMp4Extractor outputs SeekMap.Unseekable when it encounters the first moof box without a preceding sidx box. The mfra box at the end of the file is never read.
player.isCurrentMediaItemSeekable returns false, and seeking is not possible.
For inputs where ExtractorInput.getLength() returns a known value (HTTP with Content-Length, local files), the extractor could seek to the end of the file to read mfro/mfra and build a ChunkIndex from the tfra entries — similar to how Mp4Extractor handles moov at the end of non-fragmented files.
Workaround
We implemented a custom Extractor wrapper that:
- Pre-fetches the last ~4KB of the file via an HTTP Range request to read the
mfrabox - Parses the
tfraentries into a customSeekMapimplementation - Wraps
ExtractorOutputto intercept theseekMap(Unseekable)call and replaces it with thetfra-based seekable map
This confirms that FragmentedMp4Extractor works correctly for actual extraction and seeking once positioned — the gap is only in the seek map construction.
Media
Will email the sample fragmented MP4 file to android-media-github@google.com.