On this page
A 20-second MP3 stream became three recording files in mediamtx, and audio between them was lost. The recorder doubled the timestamps of MPEG audio and cut the recording every 5 seconds. PR #6361 fixed it and was merged about two days after I opened it.
When it happens
A path has record: yes and recordFormat: fmp4. A client publishes MP3 or MP2 (MPEG-1/2 Audio) at its real pace. After 5 seconds the log says detected drift between recording duration and absolute time, resetting, and a new recording starts 2 seconds later. The cycle repeats: three files, each with about 5 seconds of audio that it says lasts about 10.
How the recorder counts time
The recorder takes a sample’s duration from the next sample’s DTS (decode time). It also compares absolute time with the DTS time since the start. Past ntpDriftTolerance, 5 seconds, formatFMP4Track.write returns the drift error and the recording resets.
Why the gap existed
In January 2024 this case changed dts: pts to PTS plus PTS and renamed its running sum pts to dt, which nothing read. DTS time ran at twice the clock, so the gap reached 5 seconds at second 5. The drift check came in December 2025.
The fix
Hunks of the MPEG-1/2 Audio case in format_fmp4.go, trimmed to changed lines, names after @@ removed:
@@ -732 +732 @@
- var dt time.Duration
+ pts := u.PTS
@@ -752,2 +752,2 @@
- dts: u.PTS + u.PTS,
- ntp: u.NTP,
+ dts: pts,
+ ntp: u.NTP.Add(timestampToDuration(pts-u.PTS, clockRate)),
@@ -759,2 +759 @@
- dt += time.Duration(h.SampleCount()) *
- time.Second / time.Duration(h.SampleRate)
+ pts += int64(h.SampleCount()) * int64(clockRate) / int64(h.SampleRate)
pts starts at the unit’s PTS, grows by each frame’s length in clock ticks, and is the DTS. The NTP moves by the same offset.
The test
TestRecorderFMP4MPEG1AudioDuration writes three units of two frames. A frame of 1152 samples at 44.1 kHz lasts 2351 ticks; the units start 4702 apart. It reads the parts back and checks that all five samples last 2351 ticks. The newest frame is held, so six frames give five samples.
Before the fix it failed with expected: 0x92f and actual : 0x0. After it, 10 of 10 runs passed, and -race passes. A 20-second run at real pace with the fix gave one file, no drift line.
What the maintainer did
@aler9 merged it as one commit with the comment “merged, thanks!”. My changes match the merged ones.
The lesson
- Run the stream at its real pace once. The wrong field was a duration, but the harm was a cut: the drift check compares recorded time with the clock.
- When a fix copies a sibling, copy its whole idiom: the Opus case moves
dtsandntptogether. The test checks durations only, so it passes without thentpline.
If a recorder of yours checks time against a clock, reply on X.