mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
173 lines
5.6 KiB
C#
173 lines
5.6 KiB
C#
using MessagePack;
|
|
using Remotely.Shared.Utilities;
|
|
using Remotely.Shared.Models;
|
|
using Remotely.Shared.Models.RemoteControlDtos;
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using SIPSorcery.Net;
|
|
using System.Timers;
|
|
using System.Collections.Generic;
|
|
using System.Text.Json;
|
|
|
|
namespace Remotely.Desktop.Core.Services
|
|
{
|
|
public class WebRtcSession : IDisposable
|
|
{
|
|
public WebRtcSession(Viewer viewer, IDtoMessageHandler rtcMessageHandler)
|
|
{
|
|
Viewer = viewer;
|
|
RtcMessageHandler = rtcMessageHandler;
|
|
}
|
|
|
|
|
|
|
|
public event EventHandler<RTCIceCandidate> IceCandidateReady;
|
|
|
|
public event EventHandler<RTCSessionDescriptionInit> LocalSdpReady;
|
|
|
|
public bool IsDataChannelOpen => CaptureChannel?.readyState == RTCDataChannelState.open;
|
|
public bool IsPeerConnected => PeerSession?.connectionState == RTCPeerConnectionState.connected;
|
|
|
|
private RTCDataChannel CaptureChannel { get; set; }
|
|
private IceServerModel[] IceServers { get; set; }
|
|
private RTCPeerConnection PeerSession { get; set; }
|
|
private IDtoMessageHandler RtcMessageHandler { get; }
|
|
private Viewer Viewer { get; }
|
|
public void AddIceCandidate(string candidate, string sdpMid, ushort sdpMLineIndex, string usernameFragment)
|
|
{
|
|
var rtcCandidate = new RTCIceCandidateInit()
|
|
{
|
|
candidate = candidate,
|
|
sdpMid = sdpMid,
|
|
sdpMLineIndex = sdpMLineIndex,
|
|
usernameFragment = usernameFragment
|
|
};
|
|
|
|
PeerSession.addIceCandidate(rtcCandidate);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
PeerSession?.DataChannels?.Clear();
|
|
CaptureChannel?.close();
|
|
}
|
|
catch { }
|
|
|
|
PeerSession?.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public async Task Init(IceServerModel[] iceServers)
|
|
{
|
|
Logger.Write("Starting WebRTC connection.");
|
|
|
|
IceServers = iceServers;
|
|
|
|
var config = new RTCConfiguration()
|
|
{
|
|
iceServers = iceServers.Select(x => new RTCIceServer()
|
|
{
|
|
credential = x.Credential,
|
|
credentialType = RTCIceCredentialType.password,
|
|
urls = x.Urls,
|
|
username = x.Username
|
|
|
|
}).ToList()
|
|
};
|
|
|
|
PeerSession = new RTCPeerConnection(config);
|
|
|
|
PeerSession.oniceconnectionstatechange += PeerSession_oniceconnectionstatechange;
|
|
PeerSession.onicecandidate += PeerSession_onicecandidate;
|
|
|
|
var dataChannelInit = new RTCDataChannelInit();
|
|
CaptureChannel = await PeerSession.createDataChannel("RemoteControl", dataChannelInit);
|
|
|
|
CaptureChannel.onmessage += CaptureChannel_onmessage; ;
|
|
CaptureChannel.onclose += CaptureChannel_onclose;
|
|
CaptureChannel.onopen += CaptureChannel_onopen;
|
|
CaptureChannel.onerror += CaptureChannel_onerror;
|
|
|
|
var offer = PeerSession.createOffer(new RTCOfferOptions());
|
|
LocalSdpReady?.Invoke(this, offer);
|
|
}
|
|
|
|
|
|
|
|
public async Task SendDto<T>(T dto) where T : BaseDto
|
|
{
|
|
CaptureChannel.send(MessagePackSerializer.Serialize(dto));
|
|
|
|
await TaskHelper.DelayUntilAsync(() => CaptureChannel.bufferedAmount < 64_000, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
public Task SetRemoteDescription(string type, string sdp)
|
|
{
|
|
if (!Enum.TryParse<RTCSdpType>(type, true, out var sdpMessageType))
|
|
{
|
|
Logger.Write("Unable to parse remote WebRTC description type.");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
PeerSession.setRemoteDescription(new RTCSessionDescriptionInit()
|
|
{
|
|
type = sdpMessageType,
|
|
sdp = sdp
|
|
});
|
|
|
|
|
|
if (sdpMessageType == RTCSdpType.offer)
|
|
{
|
|
PeerSession.createAnswer(new RTCAnswerOptions());
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async void CaptureChannel_onmessage(RTCDataChannel dc, DataChannelPayloadProtocols protocol, byte[] data)
|
|
{
|
|
await RtcMessageHandler.ParseMessage(Viewer, data);
|
|
}
|
|
|
|
private async void CaptureChannel_onerror(string obj)
|
|
{
|
|
// Clear the queue when WebRTC state changes.
|
|
Viewer.PendingSentFrames.Clear();
|
|
Logger.Write($"DataChannel error: {obj}");
|
|
await Init(IceServers);
|
|
}
|
|
|
|
private void CaptureChannel_onopen()
|
|
{
|
|
// Clear the queue when WebRTC state changes.
|
|
Viewer.PendingSentFrames.Clear();
|
|
Logger.Write("DataChannel opened.");
|
|
}
|
|
|
|
private async void CaptureChannel_onclose()
|
|
{
|
|
// Clear the queue when WebRTC state changes.
|
|
Viewer.PendingSentFrames.Clear();
|
|
Logger.Write("DataChannel closed.");
|
|
await Init(IceServers);
|
|
}
|
|
|
|
private void PeerSession_oniceconnectionstatechange(RTCIceConnectionState newState)
|
|
{
|
|
// Clear the queue when WebRTC state changes.
|
|
Viewer.PendingSentFrames.Clear();
|
|
Logger.Write($"Ice state changed to {newState}.");
|
|
}
|
|
|
|
private void PeerSession_onicecandidate(RTCIceCandidate candidate)
|
|
{
|
|
Logger.Write("Ice candidate ready to send.");
|
|
IceCandidateReady?.Invoke(this, candidate);
|
|
}
|
|
}
|
|
}
|