Skip to content

Fix timestamp overflow causing stream corruption after ~71 minutes#398

Draft
joshkautz wants to merge 1 commit into
QuantumEntangledAndy:masterfrom
joshkautz:fix/timestamp-overflow
Draft

Fix timestamp overflow causing stream corruption after ~71 minutes#398
joshkautz wants to merge 1 commit into
QuantumEntangledAndy:masterfrom
joshkautz:fix/timestamp-overflow

Conversation

@joshkautz
Copy link
Copy Markdown

@joshkautz joshkautz commented Mar 3, 2026

Problem

The video and audio timestamp counters in send_to_sources use u32, which overflows after approximately 71 minutes of continuous streaming at 30fps:

4,294,967,295 / (1,000,000 µs / 30 fps) ≈ 4295 seconds ≈ 71.6 minutes

After overflow, timestamps wrap to zero. GStreamer interprets this as a discontinuity and corrupts or drops the stream. This makes Neolink unreliable for any use case requiring continuous streaming beyond ~1 hour (NVR recording, surveillance, home automation).

Fix

Change vid_ts and aud_ts from u32 to u64. A u64 microsecond counter can represent over 584,000 years of continuous streaming, effectively eliminating timestamp overflow as a failure mode.

This is a minimal, targeted change — only the timestamp variables and their immediate usage are affected. No behavior change for streams shorter than 71 minutes.

Changes

  • src/rtsp/factory.rs: Change vid_ts/aud_ts declarations from 0u32 to u64
  • src/rtsp/factory.rs: Update send_to_sources signature from &mut u32 to &mut u64
  • src/rtsp/factory.rs: Update MICROSECONDS constant from u32 to u64
  • Remove now-unnecessary as u64 casts on Duration::from_micros calls

The video and audio timestamp counters use u32, which overflows after
approximately 71 minutes of continuous streaming at 30fps
(4,294,967,295 / (1,000,000 / 30) ≈ 4295 seconds ≈ 71.6 minutes).

After overflow, timestamps wrap to zero causing GStreamer to interpret
the discontinuity as a pipeline error, corrupting the stream.

Change vid_ts and aud_ts from u32 to u64, which can represent over
584,000 years of continuous streaming. This makes long-running
24/7 streams reliable.

Fixes the timestamp component of stream degradation reported in QuantumEntangledAndy#378.
@joshkautz
Copy link
Copy Markdown
Author

Note: I made this change for my own use case (a 24/7 license plate recognition system that streams continuously). If the direction of this change doesn't align with where you want to take the project, feel free to close it — no hard feelings. Just figured it might be useful to others hitting similar long-running stream issues.

janost added a commit to MutuallyAssuredDeployment/neolink that referenced this pull request Mar 18, 2026
Upstream PR QuantumEntangledAndy#398. u32 microsecond counters overflow at ~71 min at 30fps,
causing GStreamer to interpret the wrap as a discontinuity and corrupt
the stream. u64 can represent 584,000+ years.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
janost added a commit to MutuallyAssuredDeployment/neolink that referenced this pull request Mar 19, 2026
@jkempson
Copy link
Copy Markdown

Hit this today on two RLC-820A cameras going through neolink into Frigate. Stalls every 72m24s on both.

The two cameras drifted 7s per cycle relative to each other, ending up 48s apart after 8 cycles. Two independent counters in neolink, not an external trigger.

72.4 vs the 71.6 in the PR took me a minute to reconcile. u32::MAX µs is 71m35s regardless of fps. My cameras are set to 25 fps but actually run at about 24.7, and 107374 frames at 24.7 fps is 72m24s.

On Intel QSV, ffmpeg stalls for 25s at each wrap. Strace shows the writer thread blocked on a condvar for 24.76s. On VAAPI the decoder crashes every 2 min with "Failed to sync surface" and "hwdownload: -5".

Rebuilt neolink with this patch, ran 9 hours, none of the 7 expected wraps fired. Happy to see it merged.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants