#1827

Add live video streaming and video messages (LIVE_VIDEO_V1)

Opencallebtcwants to merge intofrom

Ports live video streaming (LIVE_VIDEO_V1) from bitchat-android #1009 to iOS and macOS. Stream from the camera over the mesh in public chat or a DM, then send the recording as a normal video message.

The spec is in docs/LIVE_VIDEO_V1.md.

Wire format

  • Outer type: MessageType.videoFrame is 0x2D. 0x2A/0x2B stay reserved for courier spray ack/decline (#1438). Android #1009 was updated to 0x2D to match.
  • DM type: NoisePayloadType.videoFrame is 0x0A.
  • Packet format: each packet is a VideoBurstPacket of at most 352 bytes, carrying H.264 Constrained Baseline (Annex B, SPS/PPS on every IDR) and optional raw AAC-LC at 16 kHz mono. Systematic Reed–Solomon parity lets the receiver recover lost packets. The test vectors are byte-identical to Android's.
  • Admission: public packets are admitted like live voice: they must be signed, fresh (≤ 30 s), broadcast-only and structurally valid. Rejected packets are not relayed. They are not gossip-synced and have a 4 KiB payload cap.
  • Single write: a full packet fits one 512-byte BLE write, both public and Noise-wrapped with a 4-hop route. A test checks both.

Transport

  • Relay policy: live voice and video share the fragment relay policy (isVoiceFrame → isLiveMedia).
  • Fanout: video is exempt from the per-packet fanout subset, so direct viewers get every packet. Relays skip links whose peer is already a confirmed neighbor of the stream's origin.
  • Late packets: live voice and video packets that have waited more than 1.5 s in a BLE link's outbound queue are dropped rather than sent late.
  • Sender inputs: the sender reads link backlog and a packet budget (MeshVideoStreaming). While a stream is sent or watched, the peripheral role asks subscribed centrals for low connection latency.

App

  • Receiving: ChatLiveVideoCoordinator assembles inbound streams with a 350 ms jitter buffer. After a gap it freezes until the next keyframe. When the stream goes quiet it shows "reconnecting" and keeps a 90 s reconnect window, joining the segments into one recording. When the sender's final video_<id>.mp4 arrives, it takes over the live bubble.
  • Sending: LiveVideoSendSession paces packets with a token bucket, adapts quality to the link backlog, drops stale packets, and records the stream locally. Recordings up to 100 KiB are sent as video/mp4 files afterwards.
  • UI:
    • The composer's camera button offers Image / Video; long-press still opens the camera.
    • The video studio has live and clip modes, the front/back camera, a mic toggle and gallery import, and uses a synthetic test card on the Simulator.
    • Live and file video bubbles have full-screen players.
  • Strings: 40 new strings, translated into all 30 languages.

Also included

  • bf1e1e70: PTT playback deadlock (existing bug). The buffer completion callback read engine.isRunning on the player node's completion queue. Meanwhile AVAudioPlayerNode.stop() on the main thread held the engine lock while waiting for that queue. Restarting the audio engine (category escalation) or tearing it down could therefore deadlock, and the watchdog killed the app. It was hit on device by opening the camera while a live stream's audio played. Talking over a live voice burst could hit it too.

Testing

  • Unit tests: the full macOS suite passes: 2263 Swift Testing and 214 XCTest tests, 0 failures. That includes the ported codec, FEC, packetizer and assembler tests, a coordinator loopback test from send session to receiver, and the frame-size tests.
  • iOS Simulator:
    • A live stream in #mesh ran at about 23 packets/s and 18 kbps.
    • Stopping sent the recording as a video message: 14 s, 144×192, H.264 + AAC, which ffprobe checks out.
    • A 3 s clip worked.
  • Android to iPhone over BLE: a live stream from an Android build of #1009 at 0x2D was received and recorded on the iPhone, and the sender's final file then replaced the live bubble. The file decodes (5 s, H.264 Baseline + AAC-LC).
  • Not yet tested on device: sending a live stream from the iPhone over BLE, iOS to iOS streaming, and DM live video.

Known issues / follow-ups

  • Testnet vs mainnet in Debug: iOS Debug builds advertise the testnet BLE service UUID, while Android uses mainnet. A Debug iOS build therefore can't see Android peers. That's existing behaviour, but it matters when testing interop.
  • Duplicate file copies (existing): after an app restart the in-memory packet dedupe is empty. A peer's gossip-sync replay of a public file transfer can then be stored again as name (n).ext.
  • Possible audio offset: received recordings may carry about 130 ms of AAC priming offset (unconfirmed).

Activity

6
chatgpt-codex-connector[bot]3d ago

Codex Review Summary

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

ReviewStatusCommitReview trigger
📝 Code Review✅ Completed 2026-10-27T20:55:41.251269Zd6015d9PR opened
ℹ️ 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.

chatgpt-codex-connector[bot]reviewed3d ago
chatgpt-codex-connector[bot]3d ago

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6015d91af

ℹ️ 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".