Skip to content

FragmentedMp4Muxer: missing mehd and zero moov durations cause duration=0 (and playback failure) in platform extractors #3430

Open

Description

@ninh-huynh

Version

Media3 1.11.1

More version details

On main branch (commit 8a21ccc) still reproducible

Devices that reproduce the issue

  • Galaxy Note 20 (SM-N980F/DS) running Android 12
  • Galaxy S10 (SM-G973F) running Android 12

Devices that do not reproduce the issue

  • Galaxy ZFold 4 (SM-F936B) running Android 15
  • Galaxy S21 FE 5G (SM-G990E) running Android 16

Reproducible in the demo app?

Yes

Reproduction steps

  1. Open Tranformer Demo
  2. Choose Preset Input (H264 video and AAC audio (portrait, H < W, 0°))
  3. Tick the "Produce Fragmented Mp4" and hit "Export".
  4. Copy the output to the /sdcard/Movies by these command
adb pull /sdcard/Android/data/androidx.media3.demo.transformer/cache/transformer-output-47579613.mp4 .
adb push transformer-output-47579613.mp4 /sdcard/Movies/
  1. Open the Gallery app (system app from Samsung device), observe the duration and try to play it.

Expected result

  • The duration of the video should display correctly (10s)
  • [The video should be playable on Gallery app (system app from Samsung device)]
Screen_Recording_20260925_002217_Gallery.mp4

Actual result

  • The duration is displayed as 00:00/00:00
  • [And the video is not playable]
Screen_Recording_20260925_002131_Gallery.mp4

Media

This is the fmp4 file produced by Demo Transfomer

transformer-output-47579613.mp4

This is the same fmp4 as above, except one thing: I just override the mvhd and mdhd with correct duration (2s). Using this video, the gallery app can display duration correctly and played normally.

transformer-output-47579613_patched.mp4

I believe that this issue mostly relies on specific device. Since using the same output, copy it to difference device (like the S21 FE), the duration is displayed correctly and it is also playable from the Gallery app.

Bug Report

Activity

  1. snow2405 commented on Oct 19, 2026

    @snow2405
    Contributor

    Hi @ninh-huynh

    Thanks for raising this and sharing the detailed analysis!

    Generally speaking with regards to how the FragmentedMp4Muxer (used by InAppFragmentedMp4Muxer.Factory) works:

    1. Per ISO/IEC 14496-12, the mvhd, tkhd, and mdhd durations in moov only describe samples present in the initial movie's sample table (stbl). In a fully fragmented MP4 where all samples live inside moof fragments, those durations are required to be 0 (setting non-zero tkhd/mdhd durations when stbl is empty breaks spec-compliant MSE/DASH players).
    2. Total duration for a fragmented MP4 is instead signaled using the mehd (Movie Extends Header) box inside moov.mvex or derived from the fragments/mfra box (which is why Android 15 and 16 display the duration and play the file normally). FragmentedMp4Muxer currently omits mehd because it operates on an append-only WritableByteChannel and writes moov at the start before the total duration is known.

    To help us understand your use case:

    • Do you specifically need fragmented MP4 ("Produce Fragmented Mp4" / InAppFragmentedMp4Muxer) for your workflow (e.g., for streaming or progressive output), or does standard MP4 (leaving "Produce Fragmented Mp4" unchecked / using InAppMp4Muxer, which writes moov at the start with populated mvhd/tkhd/mdhd durations) work for you?
    • If you do require fragmented MP4 on Android 12, are you able to check whether adding a mehd box inside moov.mvex (while keeping mvhd/tkhd/mdhd at 0) is sufficient for the Android 12 Samsung Gallery app to play the file?
  2. ninh-huynh commented on Oct 21, 2026

    @ninh-huynh
    Author

    Hi @snow2405 , thanks for the detailed explanation!

    1. Do we need fragmented MP4?

    Yes. Our use case runs transcoding and uploading at the same time: fragments are uploaded as soon as they are written. A standard MP4 doesn't work for this, because InAppMp4Muxer has to seek back to the start of the file at the end to finalize moov. By then, the beginning of the file has already been uploaded.

    I understand that, per the spec, the mvhd/tkhd/mdhd durations must stay 0 in a fully fragmented file. On the mehd side, the muxer is append-only, but in transcoding scenarios the caller usually knows the expected output duration before muxing starts. Would it be possible to add an option to FragmentedMp4Muxer.Builder, such as setDurationHintUs(long), so that the muxer can write mehd into moov.mvex immediately? The muxer could keep omitting mehd when no hint is provided.

    2. Is mehd sufficient for the Samsung Gallery app?

    Unfortunately, no. I patched the output to add a correct mehd box inside moov.mvex, keeping mvhd/tkhd/mdhd at 0. The issue still reproduces on some Samsung devices (tested via Samsung Remote Test Lab). ExoPlayer reads the duration correctly from mehd, so I think the option would still help other players that support it.

    The results seem to depend on the Gallery app version rather than the Android version. On Android 13, Gallery 14.0.x fails while Gallery 14.5.x works:

    Device Android Gallery Result
    Galaxy M53 (SM-M536B) 12 13.1.04.3 ❌
    Galaxy M12 (SM-M127G) 12 13.1.04.04 ❌
    Galaxy A32 (SM-A325F) 12 13.1.04.3 ❌
    Galaxy A53 5G (SM-A536E) 12 13.1.05.16 ❌
    Galaxy S20+ (SM-G985F) 13 14.0.00.37 ❌
    Galaxy Note20 5G (SM-N981U) 13 14.5.00.33 ✅
    Galaxy A32 5G (SM-A326B) 13 14.5.00.36 ✅
    Galaxy A13 5G (SM-A136B) 14 15.1.03.12 ✅
    Galaxy S21 FE 5G (SM-G990B) 14 15.1.03.12 ✅

    So this looks like a limitation of older Samsung Gallery versions (below roughly 14.5), which seem to rely only on mvhd/mdhd. This matches my original test, where setting non-zero mvhd/mdhd durations made the file play.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions