From 33a58cddf7a98911b5553e0348c797348e9bc63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= Date: Thu, 6 Aug 2026 21:05:13 +0200 Subject: [PATCH] Fix live stream presentation timestamps Use capture presentation time directly for MoQ timestamps instead of adding composition time, which is already reflected in PTS. --- machinery/src/cloud/livemoq/annexb.go | 10 ++++++++++ machinery/src/cloud/livemoq/annexb_test.go | 9 +++++++++ machinery/src/cloud/livemoq_enabled.go | 6 +----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/machinery/src/cloud/livemoq/annexb.go b/machinery/src/cloud/livemoq/annexb.go index 106c659..9d9eb3d 100644 --- a/machinery/src/cloud/livemoq/annexb.go +++ b/machinery/src/cloud/livemoq/annexb.go @@ -59,6 +59,16 @@ func BroadcastPath(prefix string, deviceKey string) string { return prefix + "/" + strings.Trim(deviceKey, "/") + "/live.hang" } +// TimestampUs converts the capture presentation timestamp from milliseconds. +// CompositionTime must not be added: it is already represented in the PTS and +// is only used by muxers to derive DTS for streams containing B-frames. +func TimestampUs(presentationTimeMs int64) uint64 { + if presentationTimeMs < 0 { + return 0 + } + return uint64(presentationTimeMs) * 1000 +} + func hasAnnexBStartCode(payload []byte) bool { return len(payload) >= 4 && payload[0] == 0 && payload[1] == 0 && ((payload[2] == 0 && payload[3] == 1) || payload[2] == 1) diff --git a/machinery/src/cloud/livemoq/annexb_test.go b/machinery/src/cloud/livemoq/annexb_test.go index 1ece27a..1f37609 100644 --- a/machinery/src/cloud/livemoq/annexb_test.go +++ b/machinery/src/cloud/livemoq/annexb_test.go @@ -72,3 +72,12 @@ func TestBroadcastPath(t *testing.T) { t.Fatalf("BroadcastPath() default = %q", got) } } + +func TestTimestampUs(t *testing.T) { + if got := TimestampUs(1234); got != 1_234_000 { + t.Fatalf("TimestampUs() = %d, want 1234000", got) + } + if got := TimestampUs(-1); got != 0 { + t.Fatalf("TimestampUs() negative = %d, want 0", got) + } +} diff --git a/machinery/src/cloud/livemoq_enabled.go b/machinery/src/cloud/livemoq_enabled.go index 3569aac..64d7418 100644 --- a/machinery/src/cloud/livemoq_enabled.go +++ b/machinery/src/cloud/livemoq_enabled.go @@ -152,17 +152,13 @@ func publishLiveStreamMoQ(ctx context.Context, config liveMoQConfig) error { writing = true log.Log.Info("cloud.publishLiveStreamMoQ(): first H.264 keyframe received; broadcast is live") } - presentationTimeMs := packet.Time + packet.CompositionTime - if presentationTimeMs < 0 { - presentationTimeMs = 0 - } payload, err := livemoq.NormalizeH264AccessUnit(packet.Data) if err != nil { return fmt.Errorf("normalize H.264 access unit: %w", err) } frame := moq.Frame{ Payload: payload, - TimestampUs: uint64(presentationTimeMs) * 1000, + TimestampUs: livemoq.TimestampUs(packet.Time), } if err := stream.WriteFrame(frame); err != nil { return fmt.Errorf("write H.264 access unit: %w", err)