Skip to content

fix(moq-video): make decode Frame/Consumer Send on macOS#2162

Merged
kixelated merged 1 commit into
devfrom
claude/fix-libmoq-send
Jul 10, 2026
Merged

fix(moq-video): make decode Frame/Consumer Send on macOS#2162
kixelated merged 1 commit into
devfrom
claude/fix-libmoq-send

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

The NVDEC merge (#2145) broke cargo check -p libmoq on macOS with "future cannot be sent between threads safely". Linux-only CI didn't catch it.

That PR reshaped the public decode::Frame to hold the internal frame::Frame enum so a GPU-decoded frame can flow zero-copy into NVENC. On macOS that enum has a Surface variant wrapping CFRetained<CVPixelBuffer>, which objc2 conservatively leaves !Send. The non-Send-ness propagated up through decode::Consumer, and libmoq's tokio::spawn of its decode loop requires a Send future, so the crate stopped compiling on macOS.

Changes

  • frame.rs: unsafe impl Send for macos::Surface, with the safety justification the crate already relied on (CVPixelBuffer is a refcounted IOSurface wrapper; retain/release are thread-safe; pixels are only touched under CVPixelBufferLockBaseAddress).
  • capture/channel.rs: removed the SendFrame newtype that asserted the same fact locally at the capture boundary. The unsafe now lives once, on the type itself, and both the capture and decode paths inherit it.
  • decode/mod.rs: added a compile-time Send assertion test for decode::Frame and decode::Consumer, so a future non-Send variant fails cargo test on the affected platform instead of only surfacing downstream.

Public API changes

None. macos::Surface and SendFrame are private (pub(crate) / private); no exported signatures change. Not a breaking change.

Test plan

  • cargo check -p libmoq (macOS)
  • cargo check -p moq-video --all-features (macOS)
  • new decode::tests::frame_and_consumer_are_send passes
  • nix develop --command just check (exit 0)

Targets dev because it sits on top of the dev-only NVDEC work.

(Written by Claude Fable 5)

The NVDEC merge (#2145) reshaped the public `decode::Frame` to hold the
internal `frame::Frame` enum so a GPU-decoded frame can flow zero-copy
into NVENC. On macOS that enum includes a `Surface` variant wrapping
`CFRetained<CVPixelBuffer>`, which objc2 leaves `!Send`. That propagated
up through `decode::Consumer` and broke `libmoq`, whose `tokio::spawn` of
the decode loop requires a Send future. Linux-only CI never exercised the
`Surface` variant so it slipped through.

Move the `Send` assertion onto `macos::Surface` itself (the capture path
already relied on this via a local `SendFrame` newtype, now removed) and
add a compile-time Send check for the decode surface so a future
regression fails per-platform in `cargo test` rather than downstream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai 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.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@kixelated kixelated enabled auto-merge (squash) July 10, 2026 20:41
@kixelated kixelated merged commit eb7d286 into dev Jul 10, 2026
3 checks passed
@kixelated kixelated deleted the claude/fix-libmoq-send branch July 10, 2026 21:01
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.

1 participant