feat(windows): wire screen-audio sharing into the WinForms client
The core already supported SCREEN_AUDIO capture on Windows (post-M3 WASAPI loopback via VOICECAT_HAS_LOOPBACK) and the C# Interop layer was complete (VcStreamKind.ScreenAudio, StartStream/StopStream/SetRemoteStream). Only the UI was missing -- no core, proto, or C ABI changes needed. Adds a 'Share Screen Audio' toggle to the voice panel, independent of mic voice (can share without joining voice). Disconnect/teardown now stops the screen stream cleanly. New smoke test exercises the full StartStream -> StreamStarted -> StopStream -> StreamStopped path through P/Invoke.
This commit is contained in:
@@ -246,4 +246,51 @@ public sealed class VoiceCatClientSmokeTests : IDisposable
|
||||
|
||||
client.Disconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Screen-audio (SCREEN_AUDIO) stream start/stop through the P/Invoke layer. The core's
|
||||
/// WASAPI loopback path (VOICECAT_HAS_LOOPBACK) captures the default render endpoint; the
|
||||
/// StreamAnnounce succeeds regardless of whether the loopback device actually initializes
|
||||
/// on a headless box, so this test passes in CI while still exercising the full
|
||||
/// StartStream -> StreamStarted -> StopStream -> StreamStopped path through P/Invoke.
|
||||
/// See docs/voice.md §9 and MainForm's BtnScreenShareToggle_Click.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ScreenAudioStream_Starts_And_Stops()
|
||||
{
|
||||
var events = new List<VoiceCatEvent>();
|
||||
using var client = new VoiceCatClient("vc-csharp-screen", "0.1", VcLogLevel.Off,
|
||||
tofuStorePath: Path.Combine(_tempDir, "tofu_pins_screen.txt"));
|
||||
client.EventReceived += events.Add;
|
||||
|
||||
Assert.Equal(VcResult.Ok, client.Connect("127.0.0.1", _port));
|
||||
Assert.Equal(VcResult.Ok, client.AuthenticateGuest("CSharpScreen"));
|
||||
|
||||
Assert.True(PumpUntil(client, () => events.Any(e => e.Type == VcEventType.ServerIdentity), 5000));
|
||||
Assert.Equal(VcResult.Ok, client.ConfirmServerIdentity(accept: true));
|
||||
Assert.True(PumpUntil(client, () => events.Any(e => e.Type == VcEventType.AuthResult), 5000));
|
||||
Assert.Equal(VcResult.Ok, events.First(e => e.Type == VcEventType.AuthResult).Result);
|
||||
Assert.True(PumpUntil(client, () => events.Any(e => e.Type == VcEventType.ChannelList), 3000));
|
||||
|
||||
// Give the async UDP binding handshake a moment to land before announcing a stream
|
||||
// (mirrors vccli's 500ms sleep after auth).
|
||||
Thread.Sleep(500);
|
||||
|
||||
var (startResult, streamId) = client.StartStream(VcStreamKind.ScreenAudio, "Desktop audio");
|
||||
Assert.Equal(VcResult.Ok, startResult);
|
||||
Assert.True(streamId != 0, "StreamId should be non-zero on success");
|
||||
|
||||
// The core emits VC_EVENT_STREAM_STARTED for the local client too (client.cpp
|
||||
// handle_stream_announce_result), so we see our own screen-audio stream start.
|
||||
Assert.True(PumpUntil(client,
|
||||
() => events.Any(e => e.Type == VcEventType.StreamStarted && e.StreamId == streamId), 5000),
|
||||
"did not receive VC_EVENT_STREAM_STARTED for screen-audio stream");
|
||||
|
||||
Assert.Equal(VcResult.Ok, client.StopStream(streamId));
|
||||
Assert.True(PumpUntil(client,
|
||||
() => events.Any(e => e.Type == VcEventType.StreamStopped && e.StreamId == streamId), 5000),
|
||||
"did not receive VC_EVENT_STREAM_STOPPED for screen-audio stream");
|
||||
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user