FragmentedMp4Extractor: mfra-indexed fMP4 with zero moov duration is seekable but reports C.TIME_UNSET duration

Aperta
#3,524 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 5 giorni

@snow2405 ci sta già lavorando.

Dal 7/11/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

bug needs triage
Version

Media3 1.11.1

More version details

Also present on main: the new getTrackDurationUs falls back to the mdhd duration, which is 0 in these files as well.

Devices that reproduce the issue

Pixel 9a, Android 17

Devices that do not reproduce the issue

None known. The behaviour comes from the extractor, not from the device.

Reproducible in the demo app?

Not tested

Reproduction steps

This follows up #3088. Since 1.11, DefaultExtractorsFactory always sets FLAG_READ_MFRA_FOR_SEEK_MAP, so a fragmented MP4 indexed only by a trailing mfra is now seekable. When the moov carries no duration, though, it still has no duration.

ffmpeg writes exactly this layout with -movflags frag_keyframe+empty_moov: mvhd/tkhd/mdhd durations are all 0, there is no mehd and no sidx, and the only index is the mfra at the end:

ftyp, moov (durations 0, mvex with trex only), moof, mdat, moof, mdat, ..., mfra
  1. Make a 3 s file: ffmpeg -f lavfi -i color=c=black:s=32x32:r=10:d=3 -f lavfi -i sine=f=440:d=3:sample_rate=8000 -c:v libx264 -g 10 -pix_fmt yuv420p -c:a aac -b:a 16k -shortest -movflags frag_keyframe+empty_moov out.mp4 (ffprobe reports duration=3.2).
  2. Play it with ProgressiveMediaSource and the default extractors, or run it through FragmentedMp4Extractor and look at the SeekMap it outputs.

Instrumented test (Pixel 9a). It sniffs with DefaultExtractorsFactory, drives read(), reopens the input at the offset on RESULT_SEEK, and stops at the first seekMap():

FragmentedMp4Extractor -> FragmentedMp4Extractor$MfraSeekMap
  isSeekable = true
  durationUs = -9223372036854775807 (C.TIME_UNSET)
  getSeekPoints(Long.MAX_VALUE) = [timeUs=2200000, position=7099]
Expected result

The duration is known once the mfra has been read, matching what ffprobe/ffmpeg report (3.2 s here, 292.4 s for the real file below). The player shows the duration and the seek bar can be used from the start.

Actual result

MfraSeekMap is seekable, but its duration is C.TIME_UNSET, because it takes durationUs from the moov. ProgressiveMediaPeriod.onLoadCompleted fills in a duration only after the whole file has loaded. For a 27 MB, 4:52 video with a capped buffer that happens near the end of playback, so until then the UI shows no duration or remaining time and the seek bar has nothing to map onto, even though seeking would work.

The tfra entries only give fragment start times, so they alone would undercount. In the real file the last tfra time is 287.84 s against 292.4 s, and fragments there vary from 0.12 s to 7 s. ffmpeg gets the exact value by also parsing the last fragment: its tfdt plus the trun sample durations (falling back to tfhd/trex defaults).

Possible fix: when the moov duration is unset and the mfra was parsed, read the moof the last tfra entry points to (the extractor is already positioned at the end of the file for mfro/mfra), compute tfdt + sum(sample durations) for the primary track, and pass that as the MfraSeekMap duration. If that read fails, the last tfra time is still a better lower bound than TIME_UNSET.

Media

A real-world file (fragmented MP4, 27,652,257 bytes, 292.4 s, H.264 + AAC, 110 fragments): https://blossom.primal.net/b3553ff5ac31930d6962de5fd16db55594e71dc2abfec8e14447d9205e3e3953.mp4

The 3 s synthetic file from step 1 shows the same behaviour.

Bug Report
  • You will email the zip file produced by adb bugreport to [email protected] after filing this issue.
Lingua principale
Java
Stelle
3.1k
Fork
1k
Merge medio
7g 12h
PR unite (30g)
5

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di androidx/media

Tutte le issue di androidx/media

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.