feat(calls): implement Linux LAN peer calls over SRTP
Reuse existing ChatHub consent and Podcast LibVLC capture for direct bidirectional H.264/AAC media, with embedded call controls and cleanup. Require HTTPS key exchange and reject IP-masking policies for this opt-in LAN/VPN transport. Add native SRTP, signaling and lifecycle tests; document setup and remaining physical-device validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
5e48831d6d
commit
cc28ee8b29
20 changed files with 1394 additions and 29 deletions
|
|
@ -5,6 +5,7 @@ namespace Yavsc.Abstract.Chat.Calls;
|
|||
public static class CallHubMethods
|
||||
{
|
||||
public const string Invite = "InviteCall";
|
||||
public const string InviteLan = "InviteLanCall";
|
||||
public const string Accept = "AcceptCall";
|
||||
public const string End = "EndCall";
|
||||
public const string Signal = "SendCallSignal";
|
||||
|
|
@ -16,7 +17,13 @@ public static class CallHubMethods
|
|||
|
||||
public sealed record CallSession(
|
||||
Guid Id, string RoomName, string Caller, string Callee, bool RelayOnly,
|
||||
DateTimeOffset ExpiresAt);
|
||||
DateTimeOffset ExpiresAt, string Transport = CallTransports.WebRtc);
|
||||
|
||||
public static class CallTransports
|
||||
{
|
||||
public const string WebRtc = "webrtc";
|
||||
public const string LanSrtp = "lan-srtp";
|
||||
}
|
||||
|
||||
public sealed record CallIceServer(string[] Urls, string? Username = null, string? Credential = null);
|
||||
|
||||
|
|
@ -26,4 +33,29 @@ public sealed record CallEnded(Guid CallId, string Reason);
|
|||
|
||||
public sealed record CallSignal(
|
||||
string Kind, string? Sdp = null, string? Candidate = null,
|
||||
string? SdpMid = null, int? SdpMLineIndex = null);
|
||||
string? SdpMid = null, int? SdpMLineIndex = null, LanCallEndpoint? Endpoint = null);
|
||||
|
||||
public sealed record LanCallEndpoint(string Address, int Port, string Key, string Salt)
|
||||
{
|
||||
public override string ToString() => "SRTP endpoint (credentials redacted)";
|
||||
|
||||
public bool IsValid() =>
|
||||
LanCallNetwork.IsPrivateIPv4(Address) && Port is >= 1024 and <= 65534 && Port % 2 == 0 &&
|
||||
Key is { Length: 32 } && Key.All(Uri.IsHexDigit) &&
|
||||
Salt is { Length: 28 } && Salt.All(Uri.IsHexDigit);
|
||||
}
|
||||
|
||||
public static class LanCallNetwork
|
||||
{
|
||||
public static bool IsPrivateIPv4(string address)
|
||||
{
|
||||
if (!System.Net.IPAddress.TryParse(address, out var ip) ||
|
||||
ip.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork ||
|
||||
ip.ToString() != address)
|
||||
return false;
|
||||
var bytes = ip.GetAddressBytes();
|
||||
return bytes[0] == 10 || bytes[0] == 127 ||
|
||||
(bytes[0] == 172 && bytes[1] is >= 16 and <= 31) ||
|
||||
(bytes[0] == 192 && bytes[1] == 168);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue