fix(M2): wire vc_client's real voice plane through the C ABI, not just raw sockets
test_m2_voice passed against raw BSD sockets, but vc_client::stream_start/stop, UDP binding, and capture/recv were still VC_ERR_NOT_IMPLEMENTED stubs -- meaning vccli and any GUI client still couldn't actually talk. Implements the real client-side UDP-binding handshake, media key derivation, capture->encode->seal-> send and recv->open->decode->playback paths, plus server-side StreamInfo broadcast so peers learn about each other's streams via sync_remote_streams(). Adds test_voice_client_abi (two real vc_client instances, not raw sockets) and vccli --voice/--mute/--text flags, manually verified live between two instances. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,14 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "audio/audio_engine.h"
|
||||
#include "codec/opus_codec.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "net/voice_frame.h"
|
||||
#include "protocol/envelope.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "session/session.h"
|
||||
@@ -103,6 +108,41 @@ struct vc_client {
|
||||
// Client-side session model
|
||||
voicecat::session::SessionModel session_model_;
|
||||
|
||||
// ── M2: UDP / media plane ────────────────────────────────────────────────────
|
||||
std::array<uint8_t, 16> udp_token_{};
|
||||
uint16_t server_udp_port_{0};
|
||||
std::string udp_host_;
|
||||
|
||||
std::atomic<int> udp_fd_{-1};
|
||||
std::thread udp_thread_;
|
||||
std::atomic<bool> udp_stop_{false};
|
||||
std::atomic<bool> udp_ready_{false};
|
||||
|
||||
std::unique_ptr<voicecat::crypto::SodiumMediaCrypto> media_send_crypto_;
|
||||
std::unique_ptr<voicecat::crypto::SodiumMediaCrypto> media_recv_crypto_;
|
||||
|
||||
voicecat::audio::AudioEngine audio_engine_;
|
||||
voicecat::codec::OpusEncoder local_encoder_;
|
||||
|
||||
std::atomic<bool> local_stream_active_{false};
|
||||
bool local_stream_pending_{false}; // announced, waiting for result
|
||||
uint32_t local_stream_id_{0};
|
||||
uint32_t local_ssrc_{0};
|
||||
uint32_t next_local_stream_id_{1};
|
||||
uint32_t local_timestamp_{0};
|
||||
uint16_t local_frame_samples_{960};
|
||||
|
||||
// ssrc → (user_id, stream_id) for remote streams already wired into audio_engine_.
|
||||
mutable std::mutex remote_streams_mu_;
|
||||
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> remote_streams_;
|
||||
|
||||
// Local UDP destination (server media endpoint), resolved once during binding.
|
||||
uint32_t udp_dest_addr_{0}; // network byte order
|
||||
uint16_t udp_dest_port_{0}; // network byte order
|
||||
|
||||
std::atomic<bool> self_mic_muted_{false};
|
||||
std::atomic<bool> self_deafened_{false};
|
||||
|
||||
// ── io_thread_ entry point ──────────────────────────────────────────────────
|
||||
void run_io(std::string host, uint16_t port);
|
||||
|
||||
@@ -114,6 +154,26 @@ struct vc_client {
|
||||
void handle_user_event(const voicecat::v1::UserEvent& ue);
|
||||
void handle_text_message(const voicecat::v1::TextMessage& msg);
|
||||
void handle_disconnect(const voicecat::v1::Disconnect& msg);
|
||||
void handle_udp_binding_ack(const voicecat::v1::UdpBinding& msg);
|
||||
void handle_stream_announce_result(const voicecat::v1::StreamAnnounceResult& msg);
|
||||
|
||||
// ── M2: UDP / media helpers ──────────────────────────────────────────────────
|
||||
// Kicks off TCP UdpBinding request; called once after a successful AuthResult.
|
||||
void start_udp_binding();
|
||||
// Opens the UDP socket, sends the plaintext bootstrap packet, starts udp_thread_.
|
||||
void finish_udp_binding();
|
||||
// udp_thread_ entry point: recv loop, AEAD-open, decode, push to audio_engine_.
|
||||
void run_udp_recv();
|
||||
// capture_cb passed to audio_engine_.start(): encode + seal + send one frame.
|
||||
void on_capture_frame(const int16_t* pcm, int samples);
|
||||
// Inspect a User proto's streams and wire up any new remote ssrc into audio_engine_,
|
||||
// emitting VC_EVENT_STREAM_STARTED/STOPPED as streams appear/disappear.
|
||||
void sync_remote_streams(const voicecat::v1::User& user);
|
||||
// Starts audio_engine_ (capture+playback) if not already running.
|
||||
void ensure_audio_running();
|
||||
// Joins udp_thread_, stops audio_engine_, clears media crypto/remote-stream state.
|
||||
// Safe to call multiple times. Called both from run_io()'s cleanup and disconnect().
|
||||
void teardown_voice();
|
||||
|
||||
// ── Helpers (io_thread_ and caller threads) ─────────────────────────────────
|
||||
// Queue an encoded envelope to be sent on io_thread_.
|
||||
|
||||
Reference in New Issue
Block a user