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
ProgressiveMediaSource with default extractors (or via exoPlayer.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
mfra box
- Parses the
tfra entries into a custom SeekMap implementation
- Wraps
ExtractorOutput to intercept the seekMap(Unseekable) call and replaces it with the tfra-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 [email protected].
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
ffmpeg -i input.wav -c:a aac -b:a 128k -movflags empty_moov+default_base_moof+separate_moof -frag_duration 1000000 output.mp4ProgressiveMediaSourcewith default extractors (or viaexoPlayer.setMediaItem())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. Themfracontainstfraentries mapping presentation times tomoofbyte offsets, and anmfrotrailer for locating themfrafrom the end of the file.The file structure is:
Other players handle this correctly:
Actual result
FragmentedMp4ExtractoroutputsSeekMap.Unseekablewhen it encounters the firstmoofbox without a precedingsidxbox. Themfrabox at the end of the file is never read.player.isCurrentMediaItemSeekablereturnsfalse, and seeking is not possible.For inputs where
ExtractorInput.getLength()returns a known value (HTTP withContent-Length, local files), the extractor could seek to the end of the file to readmfro/mfraand build aChunkIndexfrom thetfraentries — similar to howMp4Extractorhandlesmoovat the end of non-fragmented files.Workaround
We implemented a custom
Extractorwrapper that:mfraboxtfraentries into a customSeekMapimplementationExtractorOutputto intercept theseekMap(Unseekable)call and replaces it with thetfra-based seekable mapThis confirms that
FragmentedMp4Extractorworks 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 [email protected].