Skip to content

docs: document 1:1 video calling - #408

Merged
jlucaso1 merged 11 commits into
mainfrom
claude/nifty-bohr-a3ngrl
Jul 16, 2026
Merged

jlucaso1 merged 11 commits into
mainfrom
claude/nifty-bohr-a3ngrl

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Updates the VoIP documentation for 1:1 video calling, added in oxidezap/whatsapp-rust#1024.

  • guides/voip-calls.mdx (main update):
    • Overview and feature-enabling notes now cover video alongside voice.
    • Incoming/outgoing call examples show the optional .video(source, sink) builder call.
    • New Video I/O section documenting the VideoSource/VideoSink traits, VideoFrame, and mid-call start_video/accept_video/stop_video on CallHandle.
    • New H.264 Video Plane section explaining the codec-neutral design (library owns RTP packetization/reassembly/keyframe recovery; consumer owns encode/decode).
    • Call Handle table gains the video control methods.
    • CLI subcommand table documents the --video flag and the v/q live-call keys.
    • Encryption/Validation sections extended to cover video's E2E-SRTP path.
    • Roadmap no longer lists video calls as a future item (now shipped).
  • introduction.mdx: "Voice calls" feature section renamed to "Voice & video calls" with new bullets for video capability and the codec-neutral design.
  • installation.mdx: voip feature-flag table row now mentions the H.264 video plane.

Test plan

  • Preview with mint dev and verify the new guides/voip-calls.mdx sections render correctly (code fences, tables, callouts)
  • mint broken-links

🤖 Generated with Claude Code


Generated by Claude Code


Summary by cubic

Documents 1:1 video calling in whatsapp-rust, including the codec‑neutral H.264 plane, .video(...), and mid‑call start/accept/stop flows. Clarifies crypto/signaling and I/O ownership, fixes the upgrade handshake and example comments, and notes loopback --video is video‑only and skips the RTP/SRTP plane.

  • New Features

    • guides/voip-calls.mdx: documents .video(source, sink), VideoSource/VideoSink, VideoFrame (incl. orientation) and rtp_timestamp_stride; CallHandle::{start_video,accept_video,stop_video} with from‑start and upgrade examples; handle.events() notes video state/RTCP; the Call Handle table states start_video returns immediately.
    • H.264 plane: RTP packetization/reassembly (STAP‑A, FU‑A) with PLI/FIR recovery and access‑unit backpressure; E2E‑SRTP for audio and video; CLI: --video, v toggles video and q hangs up; listen --video implies accept; loopback --video is a separate video‑only path that skips RTP/SRTP; intro/install pages updated.
  • Bug Fixes

    • Crypto/signaling docs: audio and video share per‑participant SRTP master keys; SFrame applies to audio only; start_video initiator flow clarified (subscribe to handle.events() first; peer acceptance auto‑sends Enabled; announce_video_enabled only for custom signaling); responder flow covers legacy and V2; handle.events() clones share one queue; incoming‑call local upgrade uses start_video.
    • Example polish: fixed contradictory comment placement in the incoming‑call example; minor punctuation fix.

Written for commit 84202a5. Summary will update on new commits.

Reflects the video calling feature added to the VoIP builder API:
VideoSource/VideoSink traits, CallHandle::start_video/accept_video/
stop_video, the codec-neutral H.264 Annex-B media plane, and the
CLI's --video flag and v/q live controls.
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The documentation expands VoIP coverage from 1:1 voice calls to end-to-end encrypted audio and video calls, including setup, examples, CLI behavior, video I/O APIs, H.264 media handling, encryption, architecture, and roadmap updates.

Changes

VoIP calling documentation

Layer / File(s) Summary
Calling scope and setup
guides/voip-calls.mdx, installation.mdx, introduction.mdx
Updates feature descriptions and setup guidance to cover 1:1 voice and video calls, including the async-channel requirement.
Call examples and CLI behavior
guides/voip-calls.mdx
Adds video-aware incoming and outgoing call examples and documents CLI video options and active-call commands.
Video I/O and call controls
guides/voip-calls.mdx
Documents VideoSource, VideoSink, VideoFrame, mid-call video transitions, event consumption, decoder behavior, and video-control methods.
Media architecture and encryption
guides/voip-calls.mdx
Expands H.264 video-plane, SRTP, encryption, architecture, validation, and roadmap documentation for audio and video calls.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Poem

A rabbit hops through calls so bright,
With audio left and video right.
H.264 frames bounce and flow,
Secure signals tell them where to go.
“One-to-one!” the bunny sings,
As encrypted media spreads its wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: documenting 1:1 video calling support.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

Comment @coderabbitai help to get the list of available commands.

@mintlify

mintlify Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
whatsapp-rust 🟢 Ready View Preview Jul 16, 2026, 12:38 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates the VoIP documentation to cover 1:1 video calling added in whatsapp-rust#1024, alongside the existing audio call support. It touches three files: the main guides/voip-calls.mdx guide (bulk of the changes), introduction.mdx, and installation.mdx.

  • guides/voip-calls.mdx: Adds a Video I/O section documenting VideoSource/VideoSink/VideoFrame, mid-call upgrade flows (start_video/accept_video/stop_video), H.264 video plane architecture, CLI --video flag and v/q keyboard controls, and extends the Encryption/Validation/Roadmap sections to cover video.
  • introduction.mdx: Renames "Voice calls" to "Voice & video calls" and adds bullets for codec-neutral H.264 video and the consumer-owns-encode/decode model.
  • installation.mdx: Updates the voip feature flag table row to mention the H.264 video plane.

Confidence Score: 5/5

Documentation-only PR; no runtime code is changed, making this safe to merge.

All three changed files are .mdx documentation pages. The technical claims in the new video sections are internally consistent — the codec-neutral H.264 model, the VideoSource/VideoSink channel-based design, and the mid-call upgrade signaling rules all align across the Overview, Video I/O, Call Handle table, Encryption, and Validation sections. Issues raised in prior review rounds (the async-channel comment, the overview's mic capture ownership claim) have been addressed in this PR. No new inaccuracies were found.

No files require special attention.

Important Files Changed

Filename Overview
guides/voip-calls.mdx Main documentation file updated with Video I/O section, mid-call upgrade flows, H.264 video plane description, CLI flags, and extended Encryption/Validation/Roadmap sections. Previously-flagged issues are now addressed or carry forward from the existing state. No new technical inaccuracies found.
introduction.mdx Feature section renamed from 'Voice calls' to 'Voice & video calls' with new bullets for video capability and codec-neutral design. Changes are accurate and consistent with the voip-calls.mdx guide.
installation.mdx Single-row update to the voip feature flag table to mention H.264 video plane. Change is minimal and consistent with the rest of the documentation.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant A as Our Client
    participant R as WhatsApp Relay
    participant B as Peer Client

    Note over A,B: Audio-only call already established

    rect rgb(200, 230, 255)
        Note over A,B: We initiate video upgrade
        A->>R: start_video() sends video-upgrade offer
        R->>B: video upgrade request
        B->>R: accept_video() sends accept + Enabled stanza
        R->>A: UpgradeAccept event (auto-enables media plane)
        Note over A: whatsapp-rust automatically sends Enabled stanza
    end

    rect rgb(230, 255, 200)
        Note over A,B: Peer initiates video upgrade
        B->>R: video upgrade request (UpgradeRequest/V2)
        R->>A: "VideoStateChanged { state: UpgradeRequest }"
        A->>R: accept_video() sends accept + Enabled stanza
        R->>B: video enabled
    end

    rect rgb(255, 230, 200)
        Note over A,B: Downgrade back to audio-only
        A->>R: stop_video() [idempotent]
        R->>B: video disabled
    end

    rect rgb(240, 220, 255)
        Note over A,B: Video from call start
        A->>R: .call().audio().video().start()
        R->>B: "offer with is_video=true"
        B->>R: .accept().audio().video().start()
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant A as Our Client
    participant R as WhatsApp Relay
    participant B as Peer Client

    Note over A,B: Audio-only call already established

    rect rgb(200, 230, 255)
        Note over A,B: We initiate video upgrade
        A->>R: start_video() sends video-upgrade offer
        R->>B: video upgrade request
        B->>R: accept_video() sends accept + Enabled stanza
        R->>A: UpgradeAccept event (auto-enables media plane)
        Note over A: whatsapp-rust automatically sends Enabled stanza
    end

    rect rgb(230, 255, 200)
        Note over A,B: Peer initiates video upgrade
        B->>R: video upgrade request (UpgradeRequest/V2)
        R->>A: "VideoStateChanged { state: UpgradeRequest }"
        A->>R: accept_video() sends accept + Enabled stanza
        R->>B: video enabled
    end

    rect rgb(255, 230, 200)
        Note over A,B: Downgrade back to audio-only
        A->>R: stop_video() [idempotent]
        R->>B: video disabled
    end

    rect rgb(240, 220, 255)
        Note over A,B: Video from call start
        A->>R: .call().audio().video().start()
        R->>B: "offer with is_video=true"
        B->>R: .accept().audio().video().start()
    end
Loading

Reviews (10): Last reviewed commit: "docs: fix contradictory comment placemen..." | Re-trigger Greptile

Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Addresses Greptile review on #408: mic/speaker capture-playout is
consumer-owned (was contradicting the Audio I/O section and the new
video bullet), the async-channel dependency comment omitted
VideoSource/VideoSink, and the mid-call upgrade example was missing
the initiator's announce_video_enabled step that completes the
UpgradeRequestV2/UpgradeAccept handshake.

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — pushed fixes for issues 1–3 (mic-capture ownership contradiction, missing announce_video_enabled step in the mid-call upgrade example, and the async-channel comment).

Issue 4 (Opus → MLow in the loopback row) I checked against examples/voip-cli/src/main.rs and left as-is: run_loopback() genuinely uses WaOpusEncoder/WaOpusDecoder (standard Opus), not MLow — it's a local-only harness that exercises the SRTP protect/unprotect path independently of the real CallEngine/MLow wire path used for actual calls. The original wording was accurate.


Generated by Claude Code

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026

@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: 2f98d84384

ℹ️ 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 guides/voip-calls.mdx Outdated
Comment thread introduction.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx
Addresses Codex review on #408, verified against wacore source:
- VideoPipeline and MediaPipeline derive_e2e_keys() with the same
  call_key + participant LID, so audio/video share master SRTP keys
  per participant (only SSRC/sequence/ROC differ) — was documented as
  independent per-stream keys.
- SframeSession lives only in the audio MediaState (engine.rs) and
  decrypts inbound foreign-Opus payloads; VideoPipeline has no sframe
  field, so the encryption steps no longer imply a video SFrame layer.
- introduction.mdx's "full media plane in Rust" bullet no longer
  claims video encode/decode run in Rust (only audio/MLow does; H.264
  stays consumer-owned per the Video I/O section).
- The incoming-call example pointed a later local upgrade at
  `accept_video` (peer-upgrade-response only); corrected to
  `start_video`.
- CLI table now notes `listen --video` implies `accept` (confirmed by
  the `listen_video_implies_accept` test in the example).

Also splits the mid-call video upgrade snippet into separate
initiator/responder/downgrade blocks per Greptile's review.
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 12:51

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

Copy link
Copy Markdown
Collaborator Author

Thanks @chatgpt-codex-connector — all 5 findings checked out against wacore/the CLI source and are fixed:

  • accept_videostart_video in the incoming-call example (an audio-only accept adding video later is an initiator flow, not a peer-upgrade response).
  • introduction.mdx's "full media plane in Rust" bullet no longer implies video encode/decode run in Rust — only MLow (audio) does; H.264 stays consumer-owned.
  • Encryption section: confirmed VideoPipeline::new and MediaPipeline::new both call derive_e2e_keys(call_key, participant_lid) with no media-type distinction, so audio and video share the same per-participant master keys — only SSRC/sequence/ROC differ. Fixed the "independently derived" claim.
  • Confirmed SframeSession lives only in the audio MediaState (engine.rs) for inbound foreign-Opus decode; VideoPipeline has no sframe field. Scoped that step to audio-only.
  • CLI table now notes listen --video implies accept (matches the listen_video_implies_accept test in the example).

Also split the mid-call video-upgrade snippet into separate initiator/responder/downgrade blocks per Greptile's suggestion.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@mintlify

mintlify Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
whatsapp-rust 🟡 Building Jul 16, 2026, 12:37 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

…ideo_enabled

Greptile flagged the initiator example as two back-to-back awaits with
no indication that announce_video_enabled must wait for the peer's
UpgradeAccept event, which arrives asynchronously on handle.events().
Rewrite the snippet as an explicit event loop so it can't be copied
into a signaling protocol violation.
greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026
Two minor clarity nits from Greptile's review: subscribe to
handle.events() before calling start_video (avoids a theoretical
race with a fast peer response), and clarify that --video replaces
the audio loopback with a separate video-only one rather than
running alongside it.
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 13:01

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026

@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: 5b610adf4e

ℹ️ 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 guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Codex caught the real defect: src/handlers/call.rs's CallHandler
already sends the standalone Enabled stanza automatically when the
peer's UpgradeAccept arrives (before dispatching CallEvent to the
app), so the previous "call announce_video_enabled from your event
loop" example taught a double-send. Rewrite to describe the event as
a notification only, and scope announce_video_enabled to consumers
driving signaling outside the standard handler.

Also: the "peer initiates" flow now covers the legacy
VideoState::UpgradeRequest alongside UpgradeRequestV2 (the CLI's own
handler matches both), and documents that handle.events() clones are
competing consumers sharing one queue, not a broadcast.
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 13:21

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

Copy link
Copy Markdown
Collaborator Author

Thanks, this round caught a real bug in the docs. Verified against src/handlers/call.rs:

  • Double-send confirmed: CallHandler::handle already sends the standalone Enabled stanza automatically when it processes the peer's UpgradeAccept (before dispatching the CallEvent to the app) — my "call announce_video_enabled from your event loop" example would have made a real app send a redundant second Enabled. Rewrote the initiator flow to describe the event as a notification only, and re-scoped announce_video_enabled to consumers driving signaling outside the standard handler.
  • Legacy UpgradeRequest: confirmed the CLI's own handler matches VideoState::UpgradeRequest | VideoState::UpgradeRequestV2 — docs now cover both.
  • Single-consumer event queue: confirmed CallHandle::events() is self.events.clone() on an async_channel::Receiver (competing consumers, not broadcast) — added a note against spawning a second consumer.

On the sentence-case heading nit: every existing heading in this file (## MLow Codec, ## Multi-Device Behavior, ## Architecture: CallEngine, etc.) is already title case, so I kept ## H.264 Video Plane/## Video I/O matching that established in-file convention rather than introducing a mixed-case file for the sake of the general style guide.


Generated by Claude Code

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 13:36

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@guides/voip-calls.mdx`:
- Line 16: Update the headings in guides/voip-calls.mdx at lines 16-16 and
281-281 to use sentence case: change “Enabling the Feature” to “Enabling the
feature” and “H.264 Video Plane” to “H.264 video plane”.
- Line 201: Rewrite the paragraph describing peer acceptance and the <call>
stanza handler into concise, single-idea sentences or ordered steps. Separately
state that the handler automatically sends Enabled, that handle.events() emits
VideoState::UpgradeAccept as a notification, that consumers must not call
handle.announce_video_enabled() in response, and that the method is only for
consumers managing signaling outside the standard handler.

In `@introduction.mdx`:
- Line 60: Update the media-plane bullet in introduction.mdx to remove the “Full
media plane in Rust” claim, rename it to accurately reflect Rust-owned
responsibilities, and split capture/playout, audio processing and
encryption/relay, and consumer-owned H.264 encoding/decoding into concise
one-idea sentences.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c9116fc9-cb97-485f-8e28-252ae1013388

📥 Commits

Reviewing files that changed from the base of the PR and between cb61edc and ff22427.

📒 Files selected for processing (3)
  • guides/voip-calls.mdx
  • installation.mdx
  • introduction.mdx

Comment thread guides/voip-calls.mdx Outdated
Comment thread guides/voip-calls.mdx Outdated
Comment thread introduction.mdx Outdated
Sentence case for the two new headings per this repo's documented
"use sentence case for headings" guideline (previously left as title
case to match the file's existing headings, but a house style rule
takes precedence over matching now-nonconforming older content).
Split the video-upgrade Note and the introduction.mdx media-plane
bullet into one-idea-per-sentence prose per CodeRabbit's suggestions.
Confirmed in examples/voip-cli/src/video.rs::run_video_loopback: it's
src.recv() -> VideoFrame::new() -> sink.send(), no protect/unprotect
call at all, unlike the audio loopback's explicit E2E-SRTP round trip.
Greptile flagged that a reader could assume parity with the audio row
directly above it.
greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 13:47

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 16, 2026
The 'audio-only accept can still add video later' note lived inside
the if is_video block, reading as a contradiction with the 'required'
comment right above it. Moved it after the if so it clearly describes
the other branch.
@greptile-apps
greptile-apps Bot dismissed their stale review July 16, 2026 13:53

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@jlucaso1
jlucaso1 merged commit 7c97c9c into main Jul 16, 2026
4 checks passed
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