Certain MP3 files report their length as Infinite instead of actual length when playing with HTML5 Audio element
Categories
(Core :: Audio/Video: Playback, defect, P3)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox152 | --- | fixed |
People
(Reporter: philkindleness, Assigned: kinetik)
Details
Attachments
(4 files)
Steps to reproduce:
- Using the attached archive, try to play the file "1.mp3" with
Player.html. Open the browser console to see the reported value, or look at the time indicator on the player. - Repeat the process with "2.mp3".
- Finally, repeat the process with "3.mp3".
Actual results:
- "1.mp3" reports that its length is Infinite.
- "2.mp3" correctly reports that it is 1 minute and 29 seconds long.
- "3.mp3" reports that its length is also Infinite.
Expected results:
- "1.mp3" should report that it is 1 minute long.
- "2.mp3" should report that it is 1 minute and 29 seconds long.
- "3.mp3" should report that it is 2 minutes and 21 seconds long.
| Assignee | ||
Updated•23 days ago
|
| Assignee | ||
Updated•23 days ago
|
| Assignee | ||
Comment 1•22 days ago
|
||
HTMLMediaElement.duration is reported as Infinity when an MP3 without a Xing/Info VBR header is loaded via a data: URL. The same files load with the correct duration via file: and blob: URLs.
This affects KhinoPlayer because Player.html does FileReader.readAsDataURL(file) and assigns the result to audio.src, so every load goes via a data: URL.
What's different between the three attached files
| File | Duration | First audio frame offset | Xing/Info header | Notes |
|---|---|---|---|---|
| 1.mp3 | 60.08 s | 56 178 | No | CBR 192k; ID3v2 contains a 1282×720 cover art JPEG |
| 2.mp3 | 88.99 s | 17 142 | Yes (Info @ 17178) |
CBR 320k; ID3v2 with 300×300 cover art |
| 3.mp3 | 141.59 s | 173 186 | No | CBR 192k; ID3v2 holds ~240 bytes of real frames followed by ~173 KB of zero padding |
Only file 2 carries an Info header (the CBR variant of Xing) immediately after the ID3 tag. Files 1 and 3 are plain CBR streams whose duration must be derived as (streamLength − firstFrameOffset) × 8 / bitrate.
A small test page that loads each file via direct file URL, blob URL, and data URL gives:
1.mp3 direct=60.082 blob=60.082 data=Infinity (intermittent)
2.mp3 direct=88.987 blob=88.987 data=88.987
3.mp3 direct=141.589 blob=141.589 data=Infinity (more reliably)
file: and blob: URLs always produce the correct duration. Only data: produces Infinity, and only for files without a Xing/Info header. The failure is timing-dependent, but file 3 races almost every time.
| Assignee | ||
Comment 2•20 days ago
|
||
ChannelMediaResource::OnStartRequest only read Content-Length for HTTP
channels. For data: URIs (and jar:, resource:, ...) the local length
stayed at -1, so MediaCacheStream::NotifyDataStarted was told the
length was unknown, the cache stream length never got set, and demuxers
that estimate duration from the stream length saw -1 and returned
Nothing (which surfaces in JS as audio.duration === Infinity).
This is what made KhinoPlayer-style players that base64-encode mp3
files and assign them to audio.src report Infinite duration for files
without a complete Xing/Info header (e.g. Torchlight Title.mp3).
For the non-HTTP branch, ask the channel for its content length and
fall back to -1 if it doesn't know. Channels that genuinely don't know
their length (e.g. live streams over a custom protocol) are unaffected.
The mochitest fetches a 30-second CBR mp3 (no Xing header), turns it
into a data: URL via FileReader.readAsDataURL the way KhinoPlayer does,
and verifies the audio element reports a finite, correct duration.
| Assignee | ||
Comment 3•20 days ago
|
||
If MP3TrackDemuxer::Init runs before the resource length is known (e.g.
a CBR mp3 with no Xing/Info header loaded via a non-HTTP transport that
reports Content-Length late), Duration() returns Nothing and we were
storing TimeUnit::FromInfinity() in mInfo->mDuration. That bubbles up
to MediaFormatReader::OnDemuxerInitDone, which sets
mInfo.mMetadataDuration = Some(Infinity) because Infinity is treated as
"positive". Then MediaDecoderStateMachine::OnMetadataRead unconditionally
assigns mMaster->mDuration = Info().mMetadataDuration, clobbering any
finite duration BufferedRangeUpdated has already produced from cached
byte ranges.
Logging confirms the race in the data:-URL repro: BufferedRangeUpdated
sets mDuration to 30.024 s, then OnMetadataRead overwrites it back to
Infinity, and audio.duration stays Infinity for the lifetime of the
element even though audio.seekable.end is finite.
Only assign mInfo->mDuration when we actually have a finite duration.
Leaving it at the default zero keeps mInfo.mMetadataDuration unset, so
the state machine preserves the duration BufferedRangeUpdated computes,
and the durationchange event delivers a finite value.
The mochitest now waits for canplaythrough rather than loadedmetadata
so it observes the post-metadata-read state where this race resolves.
| Assignee | ||
Comment 4•20 days ago
|
||
ADTSTrackDemuxer::Init has the same shape as the MP3 case fixed in the
preceding commit: when StreamLength() is unknown, Duration() returns
TimeUnit::FromInfinity(), and writing that into mInfo->mDuration
propagates Some(Infinity) into mInfo.mMetadataDuration in
MediaFormatReader. OnMetadataRead in the state machine then clobbers
any finite mDuration that BufferedRangeUpdated has already produced.
Only assign mInfo->mDuration when Duration() returns a finite value, so
mInfo.mMetadataDuration stays unset and the buffered-ranges-driven
duration update isn't overwritten.
The mochitest is the analog of test_mp3_dataurl_duration.html for ADTS.
The byte-size-divided-by-average-frame-length estimate the demuxer uses
is ~8% off for variable-rate AAC, so the tolerance is wide; this test's
job is to catch the Infinity-latching regression, not the unrelated
estimation accuracy.
Wave and Flac don't share this shape: WaveDemuxer reads mDataLength
from the data chunk header at init and Duration() returns a finite
value regardless of stream length; FlacTrackDemuxer::Duration returns
max(parsed-frame-duration, info.mDuration), neither of which is ever
Infinity.
Comment 6•19 days ago
|
||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/7079b643af3f
https://hg.mozilla.org/mozilla-central/rev/d5fce2457d0f
https://hg.mozilla.org/mozilla-central/rev/9d854b97973e
Description
•