Valid CMAF audio fragment
trun.sample_count > 0mdatcontains real AAC sample payload- The segment time range matches the playlist's
#EXTINF - The player can parse and append it to the audio SourceBuffer
This report focuses on a specific HLS encoding/packaging output defect: the playlist declares an audio media segment, but the corresponding CMAF chunk contains only an empty media-fragment shell with no AAC samples.
The direct input that triggers the black screen is an
invalid CMAF audio fragment generated by the upstream HLS encoding/packaging pipeline.
The fragment contains moof and mdat, so it structurally presents itself as a media
fragment. However, trun.sample_count = 0 and mdat has no payload, leaving no audio
data to decode.
hls.js reports FRAG_PARSING_ERROR: Found no media. When Safari processes an audio chunk that has
media-fragment structure but no actual samples, MediaSource/SourceBuffer can enter an invalid state and the
video can black-screen.
35bc4254b1c8229207a8_a.m3u8Found no media in fragment 7 of track 0
The audio playlist ends as shown below. In HLS, #EXTINF declares the duration of the media segment
URI that immediately follows it. Therefore, #EXTINF:1 tells the player to expect approximately
one second of audio from chunk 7.
#EXTINF:2,
35bc4254b1c8229207a8_a_000000006.cmfa
#EXTINF:1,
35bc4254b1c8229207a8_a_000000007.cmfa
#EXT-X-ENDLIST
This establishes a clear playlist contract: URI 7 is a one-second audio media segment. However, the downloaded
file has trun.sample_count = 0 and an mdat payload of 0 bytes.
00000000: 0000 0018 7374 7970 ... ....styp
00000018: 0000 0058 6d6f 6f66 ... ...Xmoof
...
00000060: 0000 0010 7472 756e 0000 0000 0000 0000
^^^^^^^^
sample_count = 0
00000070: 0000 0008 6d64 6174 ....mdat
^^^^^^^^^
box size = 8, meaning there is no payload
moof, trun.sample_count, and mdatCMAF uses the fragmented MP4 structure. The player must combine sample metadata with encoded media bytes to determine when to decode samples, how many samples to decode, and where each sample is stored. These three structures provide that mapping.
mooftraf, tfhd, tfdt, and trun boxes describe the track,
decode start time, sample count, sample durations, and sample sizes. The player uses this information to
place the fragment on the correct playback timeline.
trun.sample_countmdat. sample_count = 0 explicitly states that the fragment contains no
audio samples.
mdatmdat box header is not media content, so a total box size of 8 means the payload is
0 bytes.
moov in the init segment
└─ Provides codec, track ID, timescale, and other global metadata
moof in the media segment
└─ trun.sample_count = N
├─ Describes the timing and size of N samples
└─ Maps to the corresponding N AAC payloads in mdat
mdat
└─ Provides the actual AAC bytes
Player
└─ Parse timeline from moof → read samples from mdat → append to SourceBuffer → decode
Think of moof as the index and playback instructions, mdat as the actual media
content, and trun.sample_count as the number of playable entries in the index. The browser can
build a continuous audio timeline only when the index and content agree.
moof/mdat shell, presenting
itself as a media fragment, while the index declares sample_count = 0 and the data area contains
no payload. At the same time, the HLS playlist declares one second of audio with #EXTINF:1. The
player therefore receives conflicting timeline and media information.
trun.sample_count > 0mdat contains real AAC sample payload#EXTINFmoof/mdat shell existssample_count = 0The chunk downloaded from the CDN is already the empty 120-byte media fragment described above. The defect exists before the data enters ViewMaster; the player did not remove AAC data during transport or parsing.
The final output proves that the HLS pipeline emitted a regular media segment with no samples. The output alone cannot determine whether the AAC encoder failed to emit the final frames or whether the muxer/packager created an empty segment while aligning durations, flushing the encoder, or cutting the final segment.
moof and an empty mdat, then adds the URI to the playlist.This is the most likely inference from the output. Encoding pipeline logs and source-media durations are required to identify the exact layer where the defect occurs.
trun.sample_count > 0 and a non-empty
mdat payload.
#EXT-X-GAP instead of emitting an empty moof/mdat.
curl -sL \
"https://cdn.liftoff-creatives.io/customers/9dbbcce438/video/hls-2-sec-QVBR/125_35bc4254b1c8229207a8/35bc4254b1c8229207a8_a_000000007.cmfa" \
| xxd
BASE="https://cdn.liftoff-creatives.io/customers/9dbbcce438/video/hls-2-sec-QVBR/125_35bc4254b1c8229207a8/35bc4254b1c8229207a8"
curl -sL "${BASE}_ainit.cmfa" -o /tmp/audio-init.cmfa
curl -sL "${BASE}_a_000000007.cmfa" -o /tmp/audio-chunk.cmfa
cat /tmp/audio-init.cmfa /tmp/audio-chunk.cmfa > /tmp/audio.mp4
ffprobe -v error \
-select_streams a \
-show_entries packet=pts_time,duration_time,size \
-of json \
/tmp/audio.mp4
The result is "packets": [], confirming that the chunk contains no parseable AAC samples.
ViewMaster drops the invalid audio track when it identifies this audio parsing error to prevent Safari from black-screening. This is client-side fault tolerance, not a root-cause fix. The root solution is for the encoding/packaging pipeline to stop generating empty media-fragment shells.