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:
@@ -14,8 +14,36 @@ const User* SessionModel::find_user(uint32_t id) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::pair<const User*, const Stream*> SessionModel::find_user_by_ssrc(uint32_t ssrc) const {
|
||||
for (auto& u : users_) {
|
||||
for (auto& s : u.streams) {
|
||||
if (s.ssrc == ssrc) return {&u, &s};
|
||||
}
|
||||
}
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
#ifdef VOICECAT_HAS_NET
|
||||
|
||||
namespace {
|
||||
std::vector<Stream> copy_streams(
|
||||
const google::protobuf::RepeatedPtrField<voicecat::v1::StreamInfo>& src) {
|
||||
std::vector<Stream> out;
|
||||
out.reserve(src.size());
|
||||
for (const auto& pb : src) {
|
||||
Stream s;
|
||||
s.stream_id = pb.stream_id();
|
||||
s.ssrc = pb.ssrc();
|
||||
s.kind = static_cast<int>(pb.kind());
|
||||
s.label = pb.label();
|
||||
s.sample_rate = pb.audio().sample_rate() ? pb.audio().sample_rate() : 48000;
|
||||
s.frame_ms = pb.audio().frame_ms() ? pb.audio().frame_ms() : 20;
|
||||
out.push_back(std::move(s));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SessionModel::apply_snapshot(const voicecat::v1::ServerStateSnapshot& snap) {
|
||||
channels_.clear();
|
||||
for (const auto& pb : snap.channels()) {
|
||||
@@ -32,6 +60,7 @@ void SessionModel::apply_snapshot(const voicecat::v1::ServerStateSnapshot& snap)
|
||||
u.nickname = pb.nickname();
|
||||
u.is_guest = pb.is_guest();
|
||||
u.channel_id = pb.channel_id();
|
||||
u.streams = copy_streams(pb.streams());
|
||||
users_.push_back(std::move(u));
|
||||
}
|
||||
}
|
||||
@@ -46,6 +75,7 @@ void SessionModel::apply_user_event(const voicecat::v1::UserEvent& ev) {
|
||||
u.nickname = pb.nickname();
|
||||
u.is_guest = pb.is_guest();
|
||||
u.channel_id = pb.channel_id();
|
||||
u.streams = copy_streams(pb.streams());
|
||||
|
||||
auto it = std::find_if(users_.begin(), users_.end(),
|
||||
[&](const User& x) { return x.id == u.id; });
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VOICECAT_HAS_NET
|
||||
@@ -30,6 +31,8 @@ struct Stream {
|
||||
uint32_t ssrc{0};
|
||||
int kind{0};
|
||||
std::string label;
|
||||
uint32_t sample_rate{48000};
|
||||
uint32_t frame_ms{20};
|
||||
};
|
||||
|
||||
struct User {
|
||||
@@ -48,6 +51,10 @@ class SessionModel {
|
||||
const Channel* find_channel(uint32_t id) const;
|
||||
const User* find_user(uint32_t id) const;
|
||||
|
||||
// Find the user that owns a given media ssrc, and the matching Stream entry.
|
||||
// Returns {nullptr, nullptr} if not found.
|
||||
std::pair<const User*, const Stream*> find_user_by_ssrc(uint32_t ssrc) const;
|
||||
|
||||
#ifdef VOICECAT_HAS_NET
|
||||
void apply_snapshot(const voicecat::v1::ServerStateSnapshot& snap);
|
||||
void apply_user_event(const voicecat::v1::UserEvent& ev);
|
||||
|
||||
Reference in New Issue
Block a user