mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Replace MR WebRTC with SIPSorcery.
This commit is contained in:
parent
a1943365c6
commit
3a5471b553
@ -46,8 +46,10 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="2.0.2" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.80.2" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
<PackageReference Include="SIPSorcery" Version="5.1.1" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ namespace Remotely.Desktop.Core.Services
|
||||
Task SendCtrlAltDelToAgent();
|
||||
Task SendDeviceInfo(string serviceID, string machineName, string deviceID);
|
||||
Task SendDtoToViewer<T>(T baseDto, string viewerId);
|
||||
Task SendIceCandidateToBrowser(string candidate, int sdpMlineIndex, string sdpMid, string viewerConnectionID);
|
||||
Task SendIceCandidateToBrowser(string candidateJson, string viewerConnectionID);
|
||||
Task SendMessageToViewer(string viewerID, string message);
|
||||
Task SendRtcOfferToBrowser(string sdp, string viewerID, IceServerModel[] iceServers);
|
||||
Task SendViewerConnected(string viewerConnectionId);
|
||||
@ -164,9 +164,9 @@ namespace Remotely.Desktop.Core.Services
|
||||
var serializedDto = MessagePack.MessagePackSerializer.Serialize(baseDto);
|
||||
return Connection.SendAsync("SendDtoToBrowser", serializedDto, viewerId);
|
||||
}
|
||||
public async Task SendIceCandidateToBrowser(string candidate, int sdpMlineIndex, string sdpMid, string viewerConnectionID)
|
||||
public async Task SendIceCandidateToBrowser(string candidateJson, string viewerConnectionID)
|
||||
{
|
||||
await Connection.SendAsync("SendIceCandidateToBrowser", candidate, sdpMlineIndex, sdpMid, viewerConnectionID);
|
||||
await Connection.SendAsync("SendIceCandidateToBrowser", candidateJson, viewerConnectionID);
|
||||
}
|
||||
|
||||
public async Task SendRtcOfferToBrowser(string sdp, string viewerID, IceServerModel[] iceServers)
|
||||
@ -237,13 +237,13 @@ namespace Remotely.Desktop.Core.Services
|
||||
});
|
||||
|
||||
|
||||
Connection.On("ReceiveIceCandidate", (string candidate, int sdpMlineIndex, string sdpMid, string viewerID) =>
|
||||
Connection.On("ReceiveIceCandidate", (string candidateJson, string viewerID) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.RtcSession.AddIceCandidate(sdpMid, sdpMlineIndex, candidate);
|
||||
viewer.RtcSession.AddIceCandidate(candidateJson);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -97,9 +97,6 @@ namespace Remotely.Desktop.Core.Services
|
||||
case BaseDtoType.ToggleBlockInput:
|
||||
ToggleBlockInput(message);
|
||||
break;
|
||||
case BaseDtoType.ToggleWebRtcVideo:
|
||||
ToggleWebRtcVideo(message, viewer);
|
||||
break;
|
||||
case BaseDtoType.ClipboardTransfer:
|
||||
await ClipboardTransfer(message);
|
||||
break;
|
||||
@ -250,11 +247,5 @@ namespace Remotely.Desktop.Core.Services
|
||||
var dto = MessagePackSerializer.Deserialize<ToggleBlockInputDto>(message);
|
||||
KeyboardMouseInput.ToggleBlockInput(dto.ToggleOn);
|
||||
}
|
||||
|
||||
private void ToggleWebRtcVideo(byte[] message, Viewer viewer)
|
||||
{
|
||||
var dto = MessagePackSerializer.Deserialize<ToggleWebRtcVideoDto>(message);
|
||||
viewer.ToggleWebRtcVideo(dto.ToggleOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ namespace Remotely.Desktop.Core.Services
|
||||
}
|
||||
|
||||
|
||||
if (EnvironmentHelper.IsWindows && screenCastRequest.UseWebRtc)
|
||||
if (screenCastRequest.UseWebRtc)
|
||||
{
|
||||
await viewer.InitializeWebRtc();
|
||||
}
|
||||
@ -118,11 +118,6 @@ namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
if (viewer.IsUsingWebRtcVideo)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
continue;
|
||||
}
|
||||
if (viewer.IsStalled)
|
||||
{
|
||||
// Viewer isn't responding. Abort sending.
|
||||
|
||||
@ -11,8 +11,7 @@ using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
@ -59,14 +58,6 @@ namespace Remotely.Desktop.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUsingWebRtcVideo
|
||||
{
|
||||
get
|
||||
{
|
||||
return RtcSession?.IsPeerConnected == true && RtcSession?.IsVideoTrackConnected == true;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public double AverageBytesPerSecond { get; set; }
|
||||
@ -109,11 +100,12 @@ namespace Remotely.Desktop.Core.Services
|
||||
|
||||
RtcSession.LocalSdpReady += async (sender, sdp) =>
|
||||
{
|
||||
await CasterSocket.SendRtcOfferToBrowser(sdp.Content, ViewerConnectionID, iceServers);
|
||||
await CasterSocket.SendRtcOfferToBrowser(sdp.sdp, ViewerConnectionID, iceServers);
|
||||
};
|
||||
RtcSession.IceCandidateReady += async (sender, candidate) =>
|
||||
{
|
||||
await CasterSocket.SendIceCandidateToBrowser(candidate.Content, candidate.SdpMlineIndex, candidate.SdpMid, ViewerConnectionID);
|
||||
var candidateJson = JsonSerializer.Serialize(candidate);
|
||||
await CasterSocket.SendIceCandidateToBrowser(candidateJson, ViewerConnectionID);
|
||||
};
|
||||
|
||||
await RtcSession.Init(iceServers);
|
||||
@ -306,11 +298,6 @@ namespace Remotely.Desktop.Core.Services
|
||||
TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
public void ToggleWebRtcVideo(bool toggleOn)
|
||||
{
|
||||
RtcSession.ToggleWebRtcVideo(toggleOn);
|
||||
}
|
||||
|
||||
private async void AudioCapturer_AudioSampleReady(object sender, byte[] sample)
|
||||
{
|
||||
await SendAudioSample(sample);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using MessagePack;
|
||||
using Microsoft.MixedReality.WebRTC;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Models.RemoteControlDtos;
|
||||
@ -7,240 +6,175 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
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(Services.Viewer viewer, IDtoMessageHandler rtcMessageHandler)
|
||||
public WebRtcSession(Viewer viewer, IDtoMessageHandler rtcMessageHandler)
|
||||
{
|
||||
Viewer = viewer;
|
||||
RtcMessageHandler = rtcMessageHandler;
|
||||
DataChannelBufferMonitor = new Timer(500);
|
||||
DataChannelBufferMonitor.Elapsed += DataChannelBufferMonitor_Elapsed;
|
||||
DataChannelBufferMonitor.Start();
|
||||
}
|
||||
|
||||
public event EventHandler<IceCandidate> IceCandidateReady;
|
||||
|
||||
public event EventHandler<SdpMessage> LocalSdpReady;
|
||||
|
||||
public event EventHandler<RTCIceCandidate> IceCandidateReady;
|
||||
|
||||
public event EventHandler<RTCSessionDescriptionInit> LocalSdpReady;
|
||||
|
||||
public ulong CurrentBuffer { get; private set; }
|
||||
public bool IsDataChannelOpen => CaptureChannel?.State == DataChannel.ChannelState.Open;
|
||||
public bool IsPeerConnected => PeerSession?.IsConnected == true;
|
||||
public bool IsVideoTrackConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return Transceiver?.LocalVideoTrack?.Enabled == true;
|
||||
}
|
||||
}
|
||||
public bool IsDataChannelOpen => CaptureChannel?.readyState == RTCDataChannelState.open;
|
||||
public bool IsPeerConnected => PeerSession?.connectionState == RTCPeerConnectionState.connected;
|
||||
|
||||
private DataChannel CaptureChannel { get; set; }
|
||||
private RTCDataChannel CaptureChannel { get; set; }
|
||||
private IceServerModel[] IceServers { get; set; }
|
||||
private PeerConnection PeerSession { get; set; }
|
||||
private RTCPeerConnection PeerSession { get; set; }
|
||||
private IDtoMessageHandler RtcMessageHandler { get; }
|
||||
private Transceiver Transceiver { get; set; }
|
||||
private ExternalVideoTrackSource VideoSource { get; set; }
|
||||
private Services.Viewer Viewer { get; }
|
||||
public void AddIceCandidate(string sdpMid, int sdpMlineIndex, string candidate)
|
||||
private Timer DataChannelBufferMonitor { get; }
|
||||
private Viewer Viewer { get; }
|
||||
public void AddIceCandidate(string candidateJson)
|
||||
{
|
||||
PeerSession.AddIceCandidate(new IceCandidate()
|
||||
{
|
||||
Content = candidate,
|
||||
SdpMid = sdpMid,
|
||||
SdpMlineIndex = sdpMlineIndex
|
||||
});
|
||||
var candidate = JsonSerializer.Deserialize<RTCIceCandidateInit>(candidateJson);
|
||||
PeerSession.addIceCandidate(candidate);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Unable to exit process until DataChannel is removed/disposed,
|
||||
// and this throws internally (at least in 2.0 version).
|
||||
PeerSession?.RemoveDataChannel(CaptureChannel);
|
||||
PeerSession?.DataChannels?.Clear();
|
||||
CaptureChannel?.close();
|
||||
DataChannelBufferMonitor?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
PeerSession.Transceivers.RemoveAll(x => true);
|
||||
Disposer.TryDisposeAll(new IDisposable[]
|
||||
{
|
||||
Transceiver?.LocalVideoTrack,
|
||||
VideoSource,
|
||||
PeerSession
|
||||
});
|
||||
|
||||
PeerSession?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task Init(IceServerModel[] iceServers)
|
||||
public Task Init(IceServerModel[] iceServers)
|
||||
{
|
||||
Logger.Write("Starting WebRTC connection.");
|
||||
|
||||
IceServers = iceServers;
|
||||
|
||||
PeerSession = new PeerConnection();
|
||||
|
||||
var iceList = IceServers.Select(x => new IceServer()
|
||||
var config = new RTCConfiguration()
|
||||
{
|
||||
Urls = { x.Url },
|
||||
TurnPassword = x.TurnPassword ?? string.Empty,
|
||||
TurnUserName = x.TurnUsername ?? string.Empty
|
||||
}).ToList();
|
||||
iceServers = iceServers.Select(x => new RTCIceServer()
|
||||
{
|
||||
credential = x.Credential,
|
||||
credentialType = RTCIceCredentialType.password,
|
||||
urls = x.Urls,
|
||||
username = x.Username
|
||||
|
||||
var config = new PeerConnectionConfiguration()
|
||||
{
|
||||
IceServers = iceList
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
await PeerSession.InitializeAsync(config);
|
||||
PeerSession = new RTCPeerConnection(config);
|
||||
|
||||
PeerSession.LocalSdpReadytoSend += PeerSession_LocalSdpReadytoSend;
|
||||
PeerSession.Connected += PeerConnection_Connected;
|
||||
PeerSession.IceStateChanged += PeerConnection_IceStateChanged;
|
||||
PeerSession.IceCandidateReadytoSend += PeerSession_IceCandidateReadytoSend;
|
||||
PeerSession.oniceconnectionstatechange += PeerSession_oniceconnectionstatechange;
|
||||
PeerSession.onicecandidate += PeerSession_onicecandidate;
|
||||
|
||||
CaptureChannel = await PeerSession.AddDataChannelAsync("ScreenCapture", true, true);
|
||||
CaptureChannel.BufferingChanged += DataChannel_BufferingChanged;
|
||||
CaptureChannel.MessageReceived += CaptureChannel_MessageReceived;
|
||||
CaptureChannel.StateChanged += CaptureChannel_StateChanged;
|
||||
var dataChannelInit = new RTCDataChannelInit();
|
||||
CaptureChannel = PeerSession.createDataChannel("RemoteControl", dataChannelInit);
|
||||
|
||||
VideoSource = ExternalVideoTrackSource.CreateFromArgb32Callback(GetCaptureFrame);
|
||||
Transceiver = PeerSession.AddTransceiver(MediaKind.Video);
|
||||
CaptureChannel.onDatamessage += CaptureChannel_onDatamessage;
|
||||
CaptureChannel.onclose += CaptureChannel_onclose;
|
||||
CaptureChannel.onopen += CaptureChannel_onopen;
|
||||
CaptureChannel.onerror += CaptureChannel_onerror;
|
||||
|
||||
PeerSession.CreateOffer();
|
||||
var offer = PeerSession.createOffer(new RTCOfferOptions());
|
||||
LocalSdpReady?.Invoke(this, offer);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendDto<T>(T dto) where T : BaseDto
|
||||
|
||||
public async Task SendDto<T>(T dto) where T : BaseDto
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
CaptureChannel.SendMessage(MessagePackSerializer.Serialize(dto));
|
||||
while (CurrentBuffer > 64_000)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
});
|
||||
await CaptureChannel.sendasync(MessagePackSerializer.Serialize(dto));
|
||||
}
|
||||
|
||||
public async Task SetRemoteDescription(string type, string sdp)
|
||||
public Task SetRemoteDescription(string type, string sdp)
|
||||
{
|
||||
if (!Enum.TryParse<SdpMessageType>(type, true, out var sdpMessageType))
|
||||
if (!Enum.TryParse<RTCSdpType>(type, true, out var sdpMessageType))
|
||||
{
|
||||
Logger.Write("Unable to parse remote WebRTC description type.");
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
await PeerSession.SetRemoteDescriptionAsync(new SdpMessage()
|
||||
PeerSession.setRemoteDescription(new RTCSessionDescriptionInit()
|
||||
{
|
||||
Content = sdp,
|
||||
Type = sdpMessageType
|
||||
type = sdpMessageType,
|
||||
sdp = sdp
|
||||
});
|
||||
|
||||
if (sdpMessageType == SdpMessageType.Offer)
|
||||
|
||||
if (sdpMessageType == RTCSdpType.offer)
|
||||
{
|
||||
PeerSession.CreateAnswer();
|
||||
PeerSession.createAnswer(new RTCAnswerOptions());
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void ToggleWebRtcVideo(bool toggleOn)
|
||||
{
|
||||
if (Transceiver?.LocalVideoTrack != null)
|
||||
{
|
||||
Transceiver.LocalVideoTrack.Dispose();
|
||||
Transceiver.LocalVideoTrack = null;
|
||||
}
|
||||
|
||||
if (toggleOn)
|
||||
{
|
||||
Transceiver.LocalVideoTrack = LocalVideoTrack.CreateFromSource(VideoSource, new LocalVideoTrackInitConfig()
|
||||
{
|
||||
trackName = "ScreenCapture"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void CaptureChannel_MessageReceived(byte[] obj)
|
||||
private async void CaptureChannel_onDatamessage(byte[] obj)
|
||||
{
|
||||
await RtcMessageHandler.ParseMessage(Viewer, obj);
|
||||
}
|
||||
private async void CaptureChannel_StateChanged()
|
||||
|
||||
|
||||
|
||||
private async void CaptureChannel_onerror(string obj)
|
||||
{
|
||||
// Clear the queue when WebRTC state changes.
|
||||
Viewer.PendingSentFrames.Clear();
|
||||
Logger.Write($"DataChannel state changed. New State: {CaptureChannel.State}");
|
||||
if (CaptureChannel.State == DataChannel.ChannelState.Closed)
|
||||
{
|
||||
await Init(IceServers);
|
||||
}
|
||||
Logger.Write($"DataChannel error: {obj}");
|
||||
await Init(IceServers);
|
||||
}
|
||||
|
||||
private void DataChannel_BufferingChanged(ulong previous, ulong current, ulong limit)
|
||||
private void CaptureChannel_onopen()
|
||||
{
|
||||
CurrentBuffer = current;
|
||||
// Clear the queue when WebRTC state changes.
|
||||
Viewer.PendingSentFrames.Clear();
|
||||
Logger.Write("DataChannel opened.");
|
||||
}
|
||||
|
||||
private void GetCaptureFrame(in FrameRequest request)
|
||||
private async void CaptureChannel_onclose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsVideoTrackConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var currentFrame = Viewer.Capturer.GetNextFrame();
|
||||
if (currentFrame == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bitmapData = currentFrame.LockBits(
|
||||
new Rectangle(Point.Empty, Viewer.Capturer.CurrentScreenBounds.Size),
|
||||
System.Drawing.Imaging.ImageLockMode.ReadOnly,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
|
||||
try
|
||||
{
|
||||
var frame = new Argb32VideoFrame()
|
||||
{
|
||||
data = bitmapData.Scan0,
|
||||
height = (uint)currentFrame.Height,
|
||||
width = (uint)currentFrame.Width,
|
||||
stride = bitmapData.Stride
|
||||
};
|
||||
request.CompleteRequest(in frame);
|
||||
}
|
||||
finally
|
||||
{
|
||||
currentFrame.UnlockBits(bitmapData);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
|
||||
// Clear the queue when WebRTC state changes.
|
||||
Viewer.PendingSentFrames.Clear();
|
||||
Logger.Write("DataChannel closed.");
|
||||
await Init(IceServers);
|
||||
}
|
||||
|
||||
private void PeerConnection_Connected()
|
||||
{
|
||||
Logger.Write("PeerConnection connected.");
|
||||
}
|
||||
|
||||
private void PeerConnection_IceStateChanged(IceConnectionState newState)
|
||||
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_IceCandidateReadytoSend(IceCandidate candidate)
|
||||
private void PeerSession_onicecandidate(RTCIceCandidate candidate)
|
||||
{
|
||||
Logger.Write("Ice candidate ready to send.");
|
||||
IceCandidateReady?.Invoke(this, candidate);
|
||||
}
|
||||
private void PeerSession_LocalSdpReadytoSend(SdpMessage message)
|
||||
|
||||
private void DataChannelBufferMonitor_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
Logger.Write($"Local SDP ready.");
|
||||
LocalSdpReady?.Invoke(this, message);
|
||||
if (CaptureChannel is not null)
|
||||
{
|
||||
CurrentBuffer = CaptureChannel.bufferedAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="2.0.2" />
|
||||
<PackageReference Include="NAudio" Version="2.0.0" />
|
||||
<PackageReference Include="SharpDX" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
|
||||
|
||||
@ -168,11 +168,11 @@ namespace Remotely.Server.Hubs
|
||||
return ViewerHubContext.Clients.Client(viewerId).SendAsync("SendDtoToBrowser", dto);
|
||||
}
|
||||
|
||||
public Task SendIceCandidateToBrowser(string candidate, int sdpMlineIndex, string sdpMid, string viewerID)
|
||||
public Task SendIceCandidateToBrowser(string candidateJson, string viewerID)
|
||||
{
|
||||
if (_appConfig.UseWebRtc)
|
||||
{
|
||||
return ViewerHubContext.Clients.Client(viewerID).SendAsync("ReceiveIceCandidate", candidate, sdpMlineIndex, sdpMid);
|
||||
return ViewerHubContext.Clients.Client(viewerID).SendAsync("ReceiveIceCandidate", candidateJson);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@ -122,9 +122,9 @@ namespace Remotely.Server.Hubs
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
public Task SendIceCandidateToAgent(string candidate, int sdpMlineIndex, string sdpMid)
|
||||
public Task SendIceCandidateToAgent(string candidateJson)
|
||||
{
|
||||
return CasterHubContext.Clients.Client(ScreenCasterID).SendAsync("ReceiveIceCandidate", candidate, sdpMlineIndex, sdpMid, Context.ConnectionId);
|
||||
return CasterHubContext.Clients.Client(ScreenCasterID).SendAsync("ReceiveIceCandidate", candidateJson, Context.ConnectionId);
|
||||
}
|
||||
|
||||
public Task SendRtcAnswerToAgent(string sdp)
|
||||
|
||||
@ -134,10 +134,6 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="streamVideoButton" class="option-button" hidden title="Reduce bandwidth and increase FPS, but increase input delay.">
|
||||
Stream Mode <i class="fas fa-video"></i>
|
||||
</button>
|
||||
|
||||
<button id="changeScreenButton" class="option-button" title="Switch monitors on remote multi-monitor setups.">
|
||||
Monitors <i class="fas fa-desktop"></i>
|
||||
</button>
|
||||
|
||||
@ -40,8 +40,8 @@ namespace Remotely.Server.Services
|
||||
{
|
||||
private readonly IceServerModel[] fallbackIceServers = new IceServerModel[]
|
||||
{
|
||||
new IceServerModel() { Url = "stun:stun.l.google.com:19302"},
|
||||
new IceServerModel() { Url = "stun:stun4.l.google.com:19302"}
|
||||
new IceServerModel() { Urls = "stun:stun.l.google.com:19302"},
|
||||
new IceServerModel() { Urls = "stun:stun4.l.google.com:19302"}
|
||||
};
|
||||
|
||||
public ApplicationConfig(IConfiguration config)
|
||||
|
||||
@ -18,14 +18,14 @@
|
||||
"EnforceAttendedAccess": false,
|
||||
"IceServers": [
|
||||
{
|
||||
"Url": "stun:stun.l.google.com:19302",
|
||||
"TurnPassword": "",
|
||||
"TurnUsername": ""
|
||||
"Urls": "stun:stun.l.google.com:19302",
|
||||
"Credential": null,
|
||||
"Username": null
|
||||
},
|
||||
{
|
||||
"Url": "stun:stun4.l.google.com:19302",
|
||||
"TurnPassword": "",
|
||||
"TurnUsername": ""
|
||||
"Urls": "stun:stun4.l.google.com:19302",
|
||||
"Credential": null,
|
||||
"Username": null
|
||||
}
|
||||
],
|
||||
"KnownProxies": [],
|
||||
|
||||
@ -23,6 +23,5 @@
|
||||
WindowsSessions = 23,
|
||||
SetKeyStatesUp = 24,
|
||||
FrameReceived = 25,
|
||||
ToggleWebRtcVideo = 26,
|
||||
OpenFileTransferWindow = 27
|
||||
}
|
||||
@ -24,11 +24,9 @@
|
||||
RecordSessionButton,
|
||||
DownloadRecordingButton,
|
||||
VideoScreenViewer,
|
||||
StreamVideoButton,
|
||||
FileTransferBar,
|
||||
FileUploadButtton,
|
||||
FileDownloadButton,
|
||||
UpdateStreamingToggled,
|
||||
ViewOnlyButton,
|
||||
FullScreenButton
|
||||
} from "./UI.js";
|
||||
@ -215,15 +213,6 @@ export function ApplyInputHandlers() {
|
||||
ev.stopPropagation();
|
||||
MenuButton.style.top = `${ev.touches[0].clientY}px`;
|
||||
});
|
||||
StreamVideoButton.addEventListener("click", (ev) => {
|
||||
var toggleOn = !StreamVideoButton.classList.contains("toggled");
|
||||
|
||||
ViewerApp.Settings.streamModeEnabled = toggleOn;
|
||||
SetSettings(ViewerApp.Settings);
|
||||
|
||||
UpdateStreamingToggled(toggleOn);
|
||||
ViewerApp.MessageSender.SendToggleWebRtcVideo(toggleOn);
|
||||
});
|
||||
|
||||
[ ScreenViewer, VideoScreenViewer ].forEach(viewer => {
|
||||
viewer.addEventListener("pointermove", function (e: PointerEvent) {
|
||||
|
||||
@ -201,16 +201,6 @@ export class ToggleBlockInputDto implements BaseDto {
|
||||
DtoType: BaseDtoType = BaseDtoType.ToggleBlockInput;
|
||||
}
|
||||
|
||||
export class ToggleWebRtcVideoDto implements BaseDto {
|
||||
constructor(toggleOn: boolean) {
|
||||
this.ToggleOn = toggleOn;
|
||||
}
|
||||
|
||||
ToggleOn: boolean;
|
||||
DtoType: BaseDtoType = BaseDtoType.ToggleWebRtcVideo;
|
||||
}
|
||||
|
||||
|
||||
export class WindowsSessionsDto implements BaseDto {
|
||||
|
||||
WindowsSessions: Array<WindowsSession>;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export interface Settings {
|
||||
streamModeEnabled: boolean;
|
||||
displayName: string;
|
||||
|
||||
}
|
||||
@ -15,8 +15,7 @@ import {
|
||||
ClipboardTransferDto,
|
||||
FileDto,
|
||||
WindowsSessionsDto,
|
||||
GenericDto,
|
||||
ToggleWebRtcVideoDto
|
||||
GenericDto
|
||||
} from "./Interfaces/Dtos.js";
|
||||
import { CreateGUID, When } from "./Utilities.js";
|
||||
import { FileTransferProgress } from "./UI.js";
|
||||
@ -139,11 +138,6 @@ export class MessageSender {
|
||||
this.SendToAgent(() => ViewerApp.RtcSession.SendDto(dto),
|
||||
() => ViewerApp.ViewerHubConnection.SendDtoToClient(dto));
|
||||
}
|
||||
SendToggleWebRtcVideo(toggleOn: boolean) {
|
||||
var dto = new ToggleWebRtcVideoDto(toggleOn);
|
||||
this.SendToAgent(() => ViewerApp.RtcSession.SendDto(dto),
|
||||
() => ViewerApp.ViewerHubConnection.SendDtoToClient(dto));
|
||||
}
|
||||
SendClipboardTransfer(text: string, typeText: boolean) {
|
||||
var dto = new ClipboardTransferDto(text, typeText);
|
||||
this.SendToAgent(() => ViewerApp.RtcSession.SendDto(dto),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export class IceServerModel {
|
||||
Url: string;
|
||||
TurnPassword: string;
|
||||
TurnUsername: string;
|
||||
Urls: string;
|
||||
Credential: string;
|
||||
Username: string;
|
||||
}
|
||||
@ -1,23 +1,26 @@
|
||||
import * as UI from "./UI.js";
|
||||
import * as Utilities from "./Utilities.js";
|
||||
import { ViewerApp } from "./App.js";
|
||||
import { IceServerModel } from "./Models/IceServerModel.js";
|
||||
import { IceServerModel } from "../Shared/Models/IceServerModel.js";
|
||||
import { When } from "../Shared/Utilities.js";
|
||||
|
||||
export class RtcSession {
|
||||
PeerConnection: RTCPeerConnection;
|
||||
DataChannel: RTCDataChannel;
|
||||
MessagePack: any = window['MessagePack'];
|
||||
Init(iceServers: IceServerModel[]) {
|
||||
|
||||
|
||||
var servers = iceServers.map(x => {
|
||||
return {
|
||||
urls: x.Urls,
|
||||
username: x.Username,
|
||||
credential: x.Credential,
|
||||
credentialType: "password"
|
||||
|
||||
} as RTCIceServer
|
||||
});
|
||||
|
||||
this.PeerConnection = new RTCPeerConnection({
|
||||
iceServers: iceServers.map(x => {
|
||||
return {
|
||||
urls: x.Url,
|
||||
username: x.TurnUsername,
|
||||
credential: x.TurnPassword,
|
||||
credentialType: "password"
|
||||
}
|
||||
})
|
||||
iceServers: servers
|
||||
});
|
||||
|
||||
this.PeerConnection.ondatachannel = (ev) => {
|
||||
@ -29,7 +32,6 @@ export class RtcSession {
|
||||
UI.ConnectionP2PIcon.style.display = "none";
|
||||
UI.ConnectionRelayedIcon.style.display = "unset";
|
||||
|
||||
UI.StreamVideoButton.setAttribute("hidden", "hidden");
|
||||
UI.ScreenViewer.removeAttribute("hidden");
|
||||
UI.VideoScreenViewer.setAttribute("hidden", "hidden");
|
||||
};
|
||||
@ -38,7 +40,6 @@ export class RtcSession {
|
||||
UI.ConnectionP2PIcon.style.display = "none";
|
||||
UI.ConnectionRelayedIcon.style.display = "unset";
|
||||
|
||||
UI.StreamVideoButton.setAttribute("hidden", "hidden");
|
||||
UI.ScreenViewer.removeAttribute("hidden");
|
||||
UI.VideoScreenViewer.setAttribute("hidden", "hidden");
|
||||
};
|
||||
@ -51,13 +52,6 @@ export class RtcSession {
|
||||
console.log("Data channel opened.");
|
||||
UI.ConnectionP2PIcon.style.display = "unset";
|
||||
UI.ConnectionRelayedIcon.style.display = "none";
|
||||
|
||||
UI.StreamVideoButton.removeAttribute("hidden");
|
||||
|
||||
if (ViewerApp.Settings.streamModeEnabled) {
|
||||
UI.UpdateStreamingToggled(true);
|
||||
ViewerApp.MessageSender.SendToggleWebRtcVideo(true);
|
||||
}
|
||||
};
|
||||
};
|
||||
this.PeerConnection.onconnectionstatechange = function (ev) {
|
||||
@ -91,8 +85,10 @@ export class RtcSession {
|
||||
console.log("Set RTC offer.");
|
||||
}
|
||||
async ReceiveCandidate(candidate: RTCIceCandidate) {
|
||||
await this.PeerConnection.addIceCandidate(candidate);
|
||||
console.log("Set ICE candidate.");
|
||||
When(() => !!this.PeerConnection).then(async () => {
|
||||
await this.PeerConnection.addIceCandidate(candidate);
|
||||
console.log("Set ICE candidate.", candidate);
|
||||
});
|
||||
}
|
||||
|
||||
SendDto(dto: any) {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { Settings } from "./Interfaces/Settings.js";
|
||||
|
||||
const defaultSettings = {
|
||||
streamModeEnabled: false,
|
||||
displayName: ""
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ export var ScreenSelectBar = document.getElementById("screenSelectBar") as HTMLD
|
||||
export var ActionsBar = document.getElementById("actionsBar") as HTMLDivElement;
|
||||
export var ViewBar = document.getElementById("viewBar") as HTMLDivElement;
|
||||
export var ChangeScreenButton = document.getElementById("changeScreenButton") as HTMLButtonElement;
|
||||
export var StreamVideoButton = document.getElementById("streamVideoButton") as HTMLButtonElement;
|
||||
export var FitToScreenButton = document.getElementById("fitToScreenButton") as HTMLButtonElement;
|
||||
export var BlockInputButton = document.getElementById("blockInputButton") as HTMLButtonElement;
|
||||
export var DisconnectButton = document.getElementById("disconnectButton") as HTMLButtonElement;
|
||||
@ -118,13 +117,7 @@ export function ToggleConnectUI(shown: boolean) {
|
||||
Screen2DContext.clearRect(0, 0, ScreenViewer.width, ScreenViewer.height);
|
||||
ScreenViewer.setAttribute("hidden", "hidden");
|
||||
VideoScreenViewer.setAttribute("hidden", "hidden");
|
||||
if (ViewerApp.Mode == RemoteControlMode.Normal) {
|
||||
ConnectBox.style.removeProperty("display");
|
||||
}
|
||||
else {
|
||||
DisconnectedBox.style.removeProperty("display");
|
||||
}
|
||||
StreamVideoButton.classList.remove("toggled");
|
||||
ConnectBox.style.removeProperty("display");
|
||||
BlockInputButton.classList.remove("toggled");
|
||||
AudioButton.classList.remove("toggled");
|
||||
}
|
||||
@ -172,19 +165,6 @@ export function UpdateDisplays(selectedDisplay: string, displayNames: string[])
|
||||
}
|
||||
}
|
||||
|
||||
export function UpdateStreamingToggled(toggleOn: boolean) {
|
||||
if (toggleOn) {
|
||||
StreamVideoButton.classList.add("toggled");
|
||||
VideoScreenViewer.removeAttribute("hidden");
|
||||
ScreenViewer.setAttribute("hidden", "hidden");
|
||||
}
|
||||
else {
|
||||
StreamVideoButton.classList.remove("toggled");
|
||||
ScreenViewer.removeAttribute("hidden");
|
||||
VideoScreenViewer.setAttribute("hidden", "hidden");
|
||||
}
|
||||
}
|
||||
|
||||
export function UpdateWindowsSessions(windowsSessions: Array<WindowsSession>) {
|
||||
while (WindowsSessionSelect.options.length > 0) {
|
||||
WindowsSessionSelect.options.remove(0);
|
||||
|
||||
@ -55,10 +55,10 @@ export class ViewerHubConnection {
|
||||
|
||||
SendIceCandidate(candidate: RTCIceCandidate) {
|
||||
if (candidate) {
|
||||
this.Connection.invoke("SendIceCandidateToAgent", candidate.candidate, candidate.sdpMLineIndex, candidate.sdpMid);
|
||||
this.Connection.invoke("SendIceCandidateToAgent", JSON.stringify(candidate));
|
||||
}
|
||||
else {
|
||||
this.Connection.invoke("SendIceCandidateToAgent", "", 0, "");
|
||||
this.Connection.invoke("SendIceCandidateToAgent", "{}");
|
||||
}
|
||||
}
|
||||
SendRtcAnswer(sessionDescription: RTCSessionDescription) {
|
||||
@ -147,13 +147,10 @@ export class ViewerHubConnection {
|
||||
await ViewerApp.RtcSession.ReceiveRtcOffer(sdp);
|
||||
|
||||
});
|
||||
hubConnection.on("ReceiveIceCandidate", (candidate: string, sdpMlineIndex: number, sdpMid: string) => {
|
||||
console.log("Ice candidate received.");
|
||||
ViewerApp.RtcSession.ReceiveCandidate({
|
||||
candidate: candidate,
|
||||
sdpMLineIndex: sdpMlineIndex,
|
||||
sdpMid: sdpMid
|
||||
} as any);
|
||||
hubConnection.on("ReceiveIceCandidate", async (candidateJson: string) => {
|
||||
var candidate = JSON.parse(candidateJson);
|
||||
console.log("Ice candidate received.", candidate);
|
||||
await ViewerApp.RtcSession.ReceiveCandidate(candidate);
|
||||
});
|
||||
hubConnection.on("ShowMessage", (message: string) => {
|
||||
ShowMessage(message);
|
||||
|
||||
@ -53,8 +53,6 @@ namespace Remotely.Shared.Enums
|
||||
SetKeyStatesUp = 24,
|
||||
[EnumMember(Value = "FrameReceived")]
|
||||
FrameReceived = 25,
|
||||
[EnumMember(Value = "ToggleWebRtcVideo")]
|
||||
ToggleWebRtcVideo = 26,
|
||||
[EnumMember(Value = "OpenFileTransferWindow")]
|
||||
OpenFileTransferWindow = 27
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
{
|
||||
public class IceServerModel
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string TurnPassword { get; set; }
|
||||
public string TurnUsername { get; set; }
|
||||
public string Urls { get; set; }
|
||||
public string Credential { get; set; }
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
using Remotely.Shared.Enums;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Remotely.Shared.Models.RemoteControlDtos
|
||||
{
|
||||
[DataContract]
|
||||
public class ToggleWebRtcVideoDto : BaseDto
|
||||
{
|
||||
[DataMember(Name = "ToggleOn")]
|
||||
public bool ToggleOn { get; set; }
|
||||
|
||||
[DataMember(Name = "DtoType")]
|
||||
public new BaseDtoType DtoType { get; } = BaseDtoType.ToggleWebRtcVideo;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user