Skip to content

feat(bindings)!: exact {value, scale} timestamps in every binding - #6946

Merged
moq-bot[bot] merged 21 commits into
mainfrom
quest/m1/binding-timestamps
Nov 2, 2026
Merged

moq-bot[bot] merged 21 commits into
mainfrom
quest/m1/binding-timestamps

Conversation

@kixelated

@kixelated kixelated commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Binding timestamps stored as microseconds lose precision: one 90 kHz tick reads as 11 µs and republishes as tick zero. Completes quest/m1/binding-timestamps.md.

Approach

  • Carry exact {value, scale} timestamps through moq-ffi, the C ABI, every generated binding and wrapper, and OBS. Preserve absent timestamps on untimed tracks and reject invalid timestamp records on writes.
  • Provide duration helpers in Python, Go, Swift, Kotlin and Dart. Duration conversion rounds down at the language boundary; retaining the timestamp record preserves exact republishing.
  • Keep optional C read timestamps owned by their read handle, with stable pointers until that handle is freed.
  • Update consumers, documentation and boundary tests for the current APIs. Reject Go's zero-scale duration conversion before saturation. Flate/binary APIs already removed on main stay removed.

Impact

  • Wire: unchanged. Rust moq-net and JS protocol APIs are unchanged.
  • Breaking moq-ffi and generated C/C++: replace microsecond timestamp fields and arguments with MoqTimestamp / Timestamp { value, scale }; rename timestamp_us to timestamp and end_us to end. Decoded video timestamp_us() becomes timestamp(); catalog now() returns the record. A read carries the track's scale; writes at another scale floor to the track's scale.
  • Breaking moq-c: use moq_timestamp { value, scale }. Optional timestamps use nullable pointers instead of microsecond fields and presence flags; required timestamps are passed by value. Read pointers live until their read is freed. JSON snapshot update and stream append take a trailing timestamp pointer; NULL retains broadcast-clock stamping. Raw writes use NULL for untimed, which timed tracks refuse.
  • Breaking Python: use moq.Timestamp instead of timedelta for timestamps, with Timestamp.from_timedelta / to_timedelta for conversion. Wrapper-owned Anchor, Range and VideoDecodedFrame carry wrapper timestamps; decoded video exposes timestamp().
  • Breaking Go: replace time.Duration timestamps with moq.Timestamp, using TimestampFromDuration / TimestampToDuration. TimestampUs() becomes Timestamp() and catalog Now() returns the record. A zero Timestamp{} is invalid; TimestampToDuration panics on scale zero, and an audio frame with an unset timestamp is refused.
  • Breaking Swift: replace timestampUs: / .timestampUs with timestamp: / .timestamp records. Timestamp(Duration) and .duration require macOS 13 / iOS 16. Catalog now() returns a timestamp.
  • Breaking Kotlin: replace timestampUs with timestamp; replace the removed duration extensions with Duration.toTimestamp() / Timestamp.toDuration().
  • Breaking Dart: replace timestampUs with timestamp; replace the removed *Duration extensions with Duration.toTimestamp() / Timestamp.toDuration().
  • OBS: publish the encoder's exact timebase, including NTSC rates, and floor to nanoseconds at the libobs read boundary. Gap durations, max delay and max age remain durations.

Alternatives

  • Keep microseconds: loses non-microsecond ticks on every read and republish.
  • Store durations in every wrapper: repeats the lossy boundary conversion. Keep exact records and convert explicitly instead.

Validation

  • just ci passes all selected lanes, including Rust/C ABI unit tests, Python, Go, Kotlin, Dart, generated C/C++ probes, OBS compile checks, JS and docs. The Rust run passes 428 tests, with two existing skips.
  • just rs c-tests passes, including the installed C exact-timestamp test.
  • The existing zero-scale Go test fails against the original conversion helper and passes against the recovered fix; the complete Go suite also passes under CI.
  • Trusted installed quest, run outside the head checkout against freshly fetched origin/main, passes: 678 documents.
  • just test interop --all passes: Rust, Python, Go, C++, browser JS, Node, Bun, C and GStreamer, including tone checks, permissions, finished media, late joins and finite tails. The harness logged two forced Rust publisher teardowns; it exited zero with all assertions passing.
  • Swift is skipped on this Linux host because no Swift toolchain is available. This attempt makes no Swift test-pass claim.

Merge with main

Merged main at 88e6990 (no rebase) after the land queue ejected the PR with a merge conflict. Main ran clang-format over the handwritten C/C++, ktfmt over the Kotlin and swift-format over the Swift, so every conflict was a reformatted file: c/moq/test/probe.c, cpp/moq/{bench/main.cpp,test/probe.cpp}, and seven Kotlin wrapper/test files. Each takes main's file and reapplies this PR's timestamp edits, then is formatted with the repo's tool (clang-format with cpp/obs/.clang-format, just kt fix, just swift fix). Kotlin drops the kotlin.time.Duration/microseconds imports main still had for the removed duration extensions.

The earlier mechanical merge (1afe329) also left two breakages, fixed here:

  • Go TrackProducer.InsertDatagram called the removed Frame.ffi(); Frame is ffi.MoqFrame now, so it passes the frame straight through.
  • py/moq-rs/moq/video.py kept an unused timedelta import (ruff F401).

Following the maintainer review's coverage suggestion, timestamps_are_exact in rs/moq-ffi/src/test.rs also inserts a datagram at one 90 kHz tick and asserts the value and scale read back, and switches to kio::clock::timeout (the clippy clock lint denies tokio::time::timeout).

UniFFI outputs: just dart generate reproduces the committed moq.dart / uniffi_runtime.dart up to blank lines, which the Dart check's diff -B ignores. Kotlin, Go, Python and C++ bindings are regenerated by their checks.

Checks in the Nix dev shell, just ci origin/main (rs, go and python rerun on 17ce02a after their fixes): rs (check, and 446 moq-c/moq-ffi tests with 3 skips), py, kt, go, dart, c, cpp, obs, obs_compile, js (check and test), python, markdown, quest, shell, toml, nix, gh and carla pass. Swift is skipped: no Swift toolchain on this Linux host (swift-format ran). Interop was not rerun.

Review

  • Local review skipped for this merge under the simple-review rule (quest guide, moq-dev/quest#364), head 17ce02a. Since the last reviewed head, changes are: conflict resolutions that reapply this PR's edits to files main reformatted; formatter output; a four-line Go compile fix that keeps InsertDatagram's signature; an unused import removed; and one test leg. None of them changes a public API, the wire format, security code or unsafe.
  • No unresolved online-reviewer threads. Pullfrog's check is failing from a billing outage and was ignored, as the maintainer instructed.

Earlier records:

mechanical fix by codex / Codex: Merged base; preserved timestamp migration alongside clock rename, delay constant, interop assertions, and quest deletion. (head 1afe329)

mechanical fix by codex / Codex: Merged base; preserved timestamp/range migration alongside base defaults, clock timeout, and retired WASM/audit deletions. (head 8525d46)

  • Codex, gpt-6.1-sol, medium effort (quest review --base origin/main), head f0f03da: 0 findings.

Follow-ups

The existing moq-c JSON window parity gap is outside this timestamp change. moq_publish_json_window_push / _pop could take a trailing timestamp pointer like the JSON snapshot and stream writers.

(Written by GPT-6.1 Sol)

(Merge with main and Review written by Claude Opus 5.5)

kixelated and others added 4 commits October 27, 2026 16:47
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…indings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Exact timestamps in every binding feat(bindings)!: exact {value, scale} timestamps in every binding Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 01:19
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5631502a-8c62-46e7-a204-65c9681cbdfe
Commits

Reviewing files that changed from the base of the PR and between 88e6990 and 17ce02a.

Files selected for processing: 119
  • c/moq/test/probe.c
  • cpp/moq/bench/main.cpp
  • cpp/moq/test/probe.cpp
  • cpp/obs/src/moq-output.cpp
  • cpp/obs/src/moq-source.cpp
  • cpp/obs/test/moq-audio-encoder-test.cpp
  • cpp/obs/test/moq-output-test.cpp
  • cpp/obs/test/moq-source-test.cpp
  • dart/moq/README.md
  • dart/moq/bench/ffi.dart
  • dart/moq/lib/audio.dart
  • dart/moq/lib/json.dart
  • dart/moq/lib/media.dart
  • dart/moq/lib/moq.dart
  • dart/moq/lib/src/aliases.dart
  • dart/moq/lib/src/durations.dart
  • dart/moq/lib/src/timestamp.dart
  • dart/moq/lib/video.dart
  • dart/moq/test/docs/prelude.dart
  • dart/moq/test/moq_test.dart
  • dart/moq_ffi/lib/src/moq.dart
  • dart/moq_ffi/test/leak_test.dart
  • dart/moq_ffi/test/smoke_test.dart
  • demo/carla/sim/moq_carla_sim/bridge.py
  • demo/carla/sim/tests/conftest.py
  • demo/carla/sim/tests/test_bridge.py
  • doc/lib/c/index.md
  • doc/lib/cpp/index.md
  • doc/lib/dart/index.md
  • doc/lib/go/index.md
  • doc/lib/kt/index.md
  • doc/lib/py/index.md
  • doc/lib/swift/index.md
  • go/wrapper/README.md
  • go/wrapper/audio/audio.go
  • go/wrapper/bridge.go
  • go/wrapper/data_timestamps_test.go
  • go/wrapper/doc.go
  • go/wrapper/errors.go
  • go/wrapper/example_test.go
  • go/wrapper/internal/bridge/handles.go
  • go/wrapper/json/json.go
  • go/wrapper/json/json_test.go
  • go/wrapper/media/media.go
  • go/wrapper/media/read_test.go
  • go/wrapper/moq_test.go
  • go/wrapper/publish.go
  • go/wrapper/records.go
  • go/wrapper/records_internal_test.go
  • go/wrapper/runtime_bench_test.go
  • go/wrapper/subscribe.go
  • go/wrapper/types.go
  • go/wrapper/video/video.go
  • kt/README.md
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Durations.kt
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Timestamp.kt
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/audio/Audio.kt
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/json/Tracks.kt
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/media/Media.kt
  • kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt
  • kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/docs/Prelude.kt
  • kt/moq/src/jvmTest/kotlin/dev/moq/FfiBench.kt
  • py/moq-ffi/tests/exit_inside_callback.py
  • py/moq-rs/README.md
  • py/moq-rs/benches/ffi.py
  • py/moq-rs/docs/index.md
  • py/moq-rs/examples/clock.py
  • py/moq-rs/examples/serve_clock.py
  • py/moq-rs/moq/__init__.py
  • py/moq-rs/moq/_read.py
  • py/moq-rs/moq/_records.py
  • py/moq-rs/moq/audio.py
  • py/moq-rs/moq/json.py
  • py/moq-rs/moq/media.py
  • py/moq-rs/moq/publish.py
  • py/moq-rs/moq/types.py
  • py/moq-rs/moq/video.py
  • py/moq-rs/tests/test_data_timestamps.py
  • py/moq-rs/tests/test_local.py
  • py/moq-rs/tests/test_media_reads.py
  • py/moq-rs/tests/test_server.py
  • quest/m1/README.md
  • quest/m1/binding-timestamps.md
  • quest/m1/time-addressing/bindings.md
  • rs/moq-c/README.md
  • rs/moq-c/c-tests/audio-decode-output.c
  • rs/moq-c/c-tests/decode-output.c
  • rs/moq-c/c-tests/timestamps.c
  • rs/moq-c/src/api.rs
  • rs/moq-c/src/audio.rs
  • rs/moq-c/src/consume.rs
  • rs/moq-c/src/decode.rs
  • rs/moq-c/src/ffi.rs
  • rs/moq-c/src/publish.rs
  • rs/moq-c/src/test.rs
  • rs/moq-c/src/video.rs
  • rs/moq-ffi/src/audio.rs
  • rs/moq-ffi/src/bench.rs
  • rs/moq-ffi/src/consumer.rs
  • rs/moq-ffi/src/error.rs
  • rs/moq-ffi/src/json.rs
  • rs/moq-ffi/src/media.rs
  • rs/moq-ffi/src/producer.rs
  • rs/moq-ffi/src/test.rs
  • rs/moq-ffi/src/video.rs
  • swift/README.md
  • swift/Sources/Moq/Aliases.swift
  • swift/Sources/Moq/Json.swift
  • swift/Sources/Moq/Media.swift
  • swift/Sources/Moq/Timestamp.swift
  • swift/Sources/Moq/Track.swift
  • swift/Tests/MoqTests/Docs/Prelude.swift
  • swift/Tests/MoqTests/FfiBench.swift
  • swift/Tests/MoqTests/SmokeTests.swift
  • test/interop/clients/c/subscribe.c
  • test/interop/clients/cpp/main.cpp
  • test/interop/clients/go/main.go
  • test/interop/clients/python/interop.py
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T15:30:11.001711Z 3db0192 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)
Reviewed commit: 8547267

One actionable issue: the new Swift exact-timestamp test calls a macOS 13-only helper without an availability guard, breaking the macOS test build at the package's 12.3 deployment target (inline).

Overall direction is sound: carrying value/scale through the FFI removes lossy conversions, and boxing optional C read timestamps correctly preserves pointer stability as the slab grows. Duration rounding remains an explicit boundary rather than the storage representation. The documented Python raw-record exceptions and moq-c JSON timestamp-argument parity gap remain limitations, not additional new findings here.

Verification: static inspection of binding conversions, C pointer ownership, wrapper helpers, OBS timebase changes and regression tests. I did not run builds, tests, generated-binding checks or interoperability checks; the PR's reported passes were not independently reproduced.

Comment thread swift/Tests/MoqTests/SmokeTests.swift Resolved
kixelated and others added 3 commits October 27, 2026 18:47
…amp moq-c data writes

- Every wrapper's Timestamp-to-duration conversion rounds down to a whole
  unit, matching moq-net's own conversions and the C/C++ interop clients.
- Python wraps Anchor, Range, and VideoDecodedFrame so a time anchor takes
  and a decoded frame returns moq.Timestamp.
- moq-c's JSON and binary writes take a nullable const moq_timestamp *;
  NULL keeps stamping the broadcast clock.
- Swift's macOS 13-only duration assertion moves into the annotated test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cond

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)
Reviewed commit: 047432d

Reviewed the substantive update since 8547267, separating changes brought in from main.

Fixed: the prior Swift availability finding. The duration assertions now live in the macOS 13/iOS 16-annotated test; the raw timestamp test no longer calls the unavailable helper.

No new actionable bugs found in the reviewed delta. The consistent floor-at-conversion policy is simpler to reason about, Python's Anchor/Range/decoded-frame wrappers close the raw-record exceptions, and the C JSON/binary timestamp arguments close the parity gap while preserving NULL clock stamping. The added boundary and timestamp tests fit these changes.

Verification: static inspection of changed conversion helpers, wrapper adapters, C API forwarding and relevant regression tests. I did not run builds, tests, generated-binding checks or interoperability checks; this is not a test-pass claim.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 047432d3df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread go/wrapper/records.go Resolved
kixelated and others added 2 commits October 29, 2026 09:11
…stamps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) November 1, 2026 06:41
@moq-bot
moq-bot Bot disabled auto-merge November 1, 2026 12:11
@moq-bot moq-bot Bot removed the quest:merge label Nov 1, 2026
@moq-bot

moq-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

quest land removed this pull request from the queue: Conflicts with the base: quest/m1/binding-timestamps.md, rs/moq-c/src/test.rs, rs/moq-ffi/src/test.rs, test/interop/clients/c/subscribe.c, test/interop/clients/cpp/main.cpp
.

@moq-bot
moq-bot Bot enabled auto-merge (squash) November 1, 2026 12:27
@moq-bot moq-bot Bot added the quest:merge label Nov 1, 2026
@moq-bot
moq-bot Bot disabled auto-merge November 1, 2026 17:18
@moq-bot moq-bot Bot removed the quest:merge label Nov 1, 2026
@moq-bot

moq-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

quest land removed this pull request from the queue: Conflicts with the base: c/moq/test/probe.c, cpp/moq/bench/main.cpp, cpp/moq/test/probe.cpp, kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt, kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/audio/Audio.kt, kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/json/Tracks.kt, kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/media/Media.kt, kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt, kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/docs/Prelude.kt, kt/moq/src/jvmTest/kotlin/dev/moq/FfiBench.kt
.

@moq-bot

moq-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

codex / Codex land fixer: This maintainer pick already used its mechanical fi·x attempt. Run /quest-iterate to diagnose the repeated failure, then pick it again.

(written by codex / Codex)

kixelated and others added 4 commits November 1, 2026 15:38
Resolve the C, C++ and Kotlin conflicts from main's clang-format and ktfmt
reformatting by taking main's files and reapplying the exact timestamp
changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… test exact datagram ticks

The earlier mechanical merge left Go's InsertDatagram calling the
removed Frame.ffi() (Frame is now ffi.MoqFrame) and an unused timedelta
import in moq.video. timestamps_are_exact now also inserts a 90 kHz
datagram and reads the tick back exactly, and uses the ambient clock
timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed 17ce02ad803b7ded56c01707249c927574ad5956, focusing on changes since published review f0f03da387d36512214c64ad069d8ce0b273d9c8 and separating base merges and formatting.

No new actionable bugs found. The added timestamps_are_exact inserted-datagram case addresses the previous coverage suggestion: it inserts one 90 kHz tick, receives the datagram, and asserts both the sequence and the complete timestamp record. The existing conversion/validation path and direct Go forwarding remain consistent. Reusing this round-trip test is a simpler fit than a separate harness.

Verification: GitHub-only static comparison and inspection of the changed test and relevant forwarding/conversion paths. No builds, tests, generated-binding checks or interop runs were performed; reported results were not independently reproduced.

@kixelated
kixelated enabled auto-merge (squash) November 2, 2026 00:18
@moq-bot

moq-bot Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

quest land: this pull request conflicts with a pull request queued ahead of it, in these files:

  • #7540: dart/moq/test/moq_test.dart

Once one of them lands, this one conflicts with the base and is ejected when its turn comes. Resolve the overlap before then, such as by coordinating with that author, or by merging the base once it lands.

@moq-bot
moq-bot Bot merged commit d7f8841 into main Nov 2, 2026
1 check passed
@moq-bot
moq-bot Bot deleted the quest/m1/binding-timestamps branch November 2, 2026 01:48
kixelated added a commit that referenced this pull request Nov 2, 2026
- e2ee: the ietf properties block is variable (up to 64 KiB), so no fixed
  allowance covers it; choose between a forwarding cap or documenting the
  drop, and add a vector with non-empty relay properties.
- fmp4-refusal-framing: state the framing guarantee directly and fix the
  refusal-choice sentence.
- kotlin-js-flakes: SmokeTest.kt moved to :579 after #6946.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Nov 2, 2026
Main's rational timestamps (#6946) replace the Dart durations export
beside the retired epoch export. Main's moq-c epoch validation (#7246)
has nothing to validate on an instance, which is any non-empty text: its
malformed-epoch cases are dropped, and the instance parse keeps its
MOQ_ERROR_INVALID_CONFIG mapping in one helper. Main's new relay-hops C
test and resume-history bench are ported onto instances, and moqsrc's
refactored session (#7558) logs the instance escaped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant