When working with RTSP video streams, accurate timing information is crucial for many applications. This document explains the difference between stream timestamps and system time, and why using the correct timestamp matters.
When you receive frames from an RTSP stream, you might be tempted to use DateTime.Now to track timing:
// Not recommended for precise timing
var frame1 = ReceiveFrame();
var time1 = DateTime.Now;
var frame2 = ReceiveFrame();
var time2 = DateTime.Now;
double deltaTime = (time2 - time1).TotalSeconds;Problems with this approach:
- Network jitter: Frames can be delayed unpredictably over the network
- Variable processing time: GStreamer decoding time varies
- Thread scheduling: Your thread may be paused by the OS
- Lost frame detection: Cannot detect if frames were dropped
Camera (RTSP) Network GStreamer Your Code (C#)
─────────────────────────────────────────────────────────────────────────────────────
Frame 1 @ 0.000s ────────▶ [20ms delay] ────▶ [5ms decode] ────▶ DateTime.Now = 0.025s
Frame 2 @ 0.033s ────────▶ [15ms delay] ────▶ [8ms decode] ────▶ DateTime.Now = 0.056s
Frame 3 @ 0.066s ────────▶ [25ms delay] ────▶ [5ms decode] ────▶ DateTime.Now = 0.096s
Delta time with DateTime.Now:
- Frame 1→2: 0.056 - 0.025 = 0.031s (should be 0.033s)
- Frame 2→3: 0.096 - 0.056 = 0.040s (should be 0.033s)
Error: up to 21% inaccuracy!
The PTS (Presentation Timestamp) is the actual timestamp from the RTSP stream, assigned by the camera and preserved through the decoding pipeline.
Camera (RTSP) GStreamer Your Code (C#)
──────────────────────────────────────────────────────────────────────────────────
Frame 1 + PTS=0.000s ────▶ Decode + preserve PTS ────▶ frame.StreamTimestamp = 0.000s
Frame 2 + PTS=0.033s ────▶ Decode + preserve PTS ────▶ frame.StreamTimestamp = 0.033s
Frame 3 + PTS=0.066s ────▶ Decode + preserve PTS ────▶ frame.StreamTimestamp = 0.066s
Delta time with StreamTimestamp:
- Frame 1→2: 0.033 - 0.000 = 0.033s (accurate!)
- Frame 2→3: 0.066 - 0.033 = 0.033s (accurate!)
| Method | Measured Delta | Standard Deviation | Max Error |
|---|---|---|---|
DateTime.Now |
Variable | ±6-10ms | up to 30% |
frame.ReceivedAt |
Variable | ±4-6ms | up to 20% |
frame.StreamTimestamp |
33.3ms | ±0.1ms | <1% |
Conclusion: StreamTimestamp is significantly more accurate than system time methods.
VideoFrame previousFrame = null;
capture.OnFrameReceived += (frame) =>
{
if (previousFrame != null)
{
// Use StreamTimestamp for accurate timing
double deltaTime = (frame.StreamTimestamp - previousFrame.StreamTimestamp).TotalSeconds;
// Use deltaTime for your calculations...
}
previousFrame = frame;
};capture.OnFrameReceived += (frame) =>
{
// ReceivedAt is fine for logging/debugging
Console.WriteLine($"[{frame.ReceivedAt:HH:mm:ss.fff}] Frame received");
// But don't use it for precise timing calculations
};public class VideoFrame
{
// Use this for precise timing calculations
public TimeSpan StreamTimestamp { get; set; }
// Actual timestamp from the RTSP stream (PTS)
// Precision: nanoseconds
// Source: camera clock
// Raw timestamp in nanoseconds (if you need more control)
public ulong StreamTimestampNanoseconds { get; set; }
// Only for logging/debugging - NOT for precise timing
public DateTime ReceivedAt { get; set; }
// Time when frame was received in C#
// Includes network delay + processing time
}A bonus of using PTS: you can detect when frames are dropped!
VideoFrame previousFrame = null;
const double EXPECTED_FRAME_TIME = 1.0 / 30.0; // 30 FPS = 0.033s
capture.OnFrameReceived += (frame) =>
{
if (previousFrame != null)
{
double deltaTime = (frame.StreamTimestamp - previousFrame.StreamTimestamp).TotalSeconds;
// If delta > 1.5x expected, frames were probably dropped
if (deltaTime > EXPECTED_FRAME_TIME * 1.5)
{
int lostFrames = (int)(deltaTime / EXPECTED_FRAME_TIME) - 1;
Console.WriteLine($"Warning: {lostFrames} frame(s) dropped!");
}
}
previousFrame = frame;
};- Timestamp indicating when the frame should be displayed
- Generated by the RTSP camera
- Unit: nanoseconds (64-bit)
- Monotonically increasing
- Timestamp indicating when the frame should be decoded
- Used as fallback if PTS is not available
- Usually equal to PTS for RTSP streams
- System clock time
- Do NOT use for precise timing calculations
- Use only for logging and debugging
| Use Case | Recommended Property |
|---|---|
| Precise timing between frames | StreamTimestamp |
| Frame synchronization | StreamTimestamp |
| Logging/debugging | ReceivedAt |
| Detecting dropped frames | StreamTimestamp |
| Display timestamps to user | Either (depending on need) |
Always use StreamTimestamp when precision matters!