WebRTC working.

This commit is contained in:
Jared Goodwin 2020-01-16 16:32:20 -08:00
parent bc27bd77ea
commit 94b78488f1
37 changed files with 511 additions and 57 deletions

View File

@ -8,7 +8,6 @@
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\" />
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
@ -16,6 +15,10 @@
<SubType>Designer</SubType>
</AvaloniaResource>
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Remove="Models\**" />
<Compile Remove="Models\**" />
<EmbeddedResource Remove="Models\**" />
<None Remove="Models\**" />
<AvaloniaResource Remove="Controls\HostNamePrompt.xaml" />
<AvaloniaResource Remove="Controls\MessageBox.xaml" />
</ItemGroup>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<Platform>x64</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Server\wwwroot\Downloads</PublishDir>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>

View File

@ -10,7 +10,7 @@ using Remotely.ScreenCast.Core.Capture;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Models;
using Remotely.ScreenCast.Core.Services;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
using Remotely.ScreenCast.Linux.Capture;
using Remotely.ScreenCast.Linux.Services;
using Remotely.Shared.Models;

View File

@ -15,6 +15,7 @@
<Company>Translucency Software</Company>
<Product>Remotely Desktop</Product>
<PackageProjectUrl>https://remotely.lucency.co</PackageProjectUrl>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<Platform>x64</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Server\wwwroot\Downloads\Win-x64\</PublishDir>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<Platform>x86</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Server\wwwroot\Downloads\Win-x86\</PublishDir>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>

View File

@ -18,7 +18,7 @@ using System.Security.Principal;
using System.Windows.Input;
using Remotely.ScreenCast.Win.Services;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
namespace Remotely.Desktop.Win.ViewModels
{

View File

@ -43,7 +43,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCast.Win", "ScreenCast.Win\ScreenCast.Win.csproj", "{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop.Win", "Desktop.Win\Desktop.Win.csproj", "{6B726FC4-A907-4813-BF38-3342E02AA8D2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Desktop.Win", "Desktop.Win\Desktop.Win.csproj", "{6B726FC4-A907-4813-BF38-3342E02AA8D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -129,28 +129,28 @@ Global
{FF7FD66A-B3E2-4D39-A457-A00C94EDE9E6}.Release|x86.Build.0 = Release|x86
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.ActiveCfg = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.Build.0 = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.ActiveCfg = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.Build.0 = Debug|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.Build.0 = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.ActiveCfg = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.Build.0 = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.ActiveCfg = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.Build.0 = Release|Any CPU
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.ActiveCfg = Debug|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.Build.0 = Debug|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.ActiveCfg = Debug|x86
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.Build.0 = Debug|x86
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.ActiveCfg = Release|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.Build.0 = Release|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.ActiveCfg = Release|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.Build.0 = Release|x64
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.ActiveCfg = Release|x86
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.Build.0 = Release|x86
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x64.ActiveCfg = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x64.Build.0 = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x86.ActiveCfg = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x86.Build.0 = Debug|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x64.ActiveCfg = Debug|x64
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x64.Build.0 = Debug|x64
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x86.ActiveCfg = Debug|x86
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x86.Build.0 = Debug|x86
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|Any CPU.Build.0 = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x64.ActiveCfg = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x64.Build.0 = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x86.ActiveCfg = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x86.Build.0 = Release|Any CPU
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x64.ActiveCfg = Release|x64
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x64.Build.0 = Release|x64
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x86.ActiveCfg = Release|x86
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.SignalR.Client;
using Remotely.ScreenCast.Core.Models;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
using Remotely.ScreenCast.Core.Services;
using System;
using System.Collections.Generic;
@ -25,9 +25,11 @@ namespace Remotely.ScreenCast.Core.Capture
string requesterName,
ICapturer capturer)
{
var conductor = Conductor.Current;
var viewers = Conductor.Current.Viewers;
var mode = Conductor.Current.Mode;
var casterSocket = Conductor.Current.CasterSocket;
Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}. App Mode: {conductor.Mode}");
Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}. App Mode: {mode}");
byte[] encodedImageBytes;
var fpsQueue = new Queue<DateTime>();
@ -41,26 +43,30 @@ namespace Remotely.ScreenCast.Core.Capture
HasControl = true
};
viewers.AddOrUpdate(viewerID, viewer, (id, v) => viewer);
conductor.Viewers.AddOrUpdate(viewerID, viewer, (id, v) => viewer);
if (conductor.Mode == Enums.AppMode.Normal)
if (mode == Enums.AppMode.Normal)
{
conductor.InvokeViewerAdded(viewer);
Conductor.Current.InvokeViewerAdded(viewer);
}
await conductor.CasterSocket.SendMachineName(Environment.MachineName, viewerID);
if (OSUtils.IsWindows)
{
await InitializeWebRtc(viewer, casterSocket);
}
await conductor.CasterSocket.SendScreenCount(
await casterSocket.SendMachineName(Environment.MachineName, viewerID);
await casterSocket.SendScreenCount(
capturer.SelectedScreen,
capturer.GetScreenCount(),
viewerID);
await conductor.CasterSocket.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);
await casterSocket.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);
capturer.ScreenChanged += async (sender, bounds) =>
{
await conductor.CasterSocket.SendScreenSize(bounds.Width, bounds.Height, viewerID);
await casterSocket.SendScreenSize(bounds.Width, bounds.Height, viewerID);
};
while (!viewer.DisconnectRequested)
@ -125,9 +131,17 @@ namespace Remotely.ScreenCast.Core.Capture
if (encodedImageBytes?.Length > 0)
{
await conductor.CasterSocket.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow);
viewer.Latency += 300;
viewer.OutputBuffer += encodedImageBytes.Length;
if (viewer.RtcSession.IsDataChannelOpen)
{
viewer.RtcSession.SendCaptureFrame(diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, encodedImageBytes);
}
else
{
await casterSocket.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow);
viewer.Latency += 300;
viewer.OutputBuffer += encodedImageBytes.Length;
}
}
}
}
@ -143,16 +157,37 @@ namespace Remotely.ScreenCast.Core.Capture
Logger.Write($"Ended screen cast. Requester: {requesterName}. Viewer ID: {viewerID}.");
conductor.Viewers.TryRemove(viewerID, out _);
viewers.TryRemove(viewerID, out _);
capturer.Dispose();
// Close if no one is viewing.
if (conductor.Viewers.Count == 0 && conductor.Mode == Enums.AppMode.Unattended)
if (viewers.Count == 0 && mode == Enums.AppMode.Unattended)
{
await conductor.CasterSocket.Disconnect();
await casterSocket.Disconnect();
Environment.Exit(0);
}
}
private async Task InitializeWebRtc(Viewer viewer, CasterSocket casterSocket)
{
try
{
viewer.RtcSession = new WebRtcSession();
viewer.RtcSession.LocalSdpReady += async (sender, sdp) =>
{
await casterSocket.SendRtcOfferToBrowser(sdp, viewer.ViewerConnectionID);
};
viewer.RtcSession.IceCandidateReady += async (sender, args) =>
{
await casterSocket.SendIceCandidateToBrowser(args.candidate, args.sdpMlineIndex, args.sdpMid, viewer.ViewerConnectionID);
};
await viewer.RtcSession.Init();
}
catch (Exception ex)
{
Logger.Write(ex);
}
}
}
}

View File

@ -14,10 +14,9 @@ using System.Net;
using Remotely.ScreenCast.Core.Services;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.Shared.Win32;
using Remotely.ScreenCast.Core.Interfaces;
using Microsoft.Extensions.DependencyInjection;
namespace Remotely.ScreenCast.Core.Sockets
namespace Remotely.ScreenCast.Core.Communication
{
public class CasterSocket
{
@ -113,6 +112,11 @@ namespace Remotely.ScreenCast.Core.Sockets
await Connection.SendAsync("SendMachineName", machineName, viewerID);
}
public async Task SendRtcOfferToBrowser(string sdp, string viewerID)
{
await Connection.SendAsync("SendRtcOfferToBrowser", sdp, viewerID);
}
public async Task SendScreenCapture(byte[] captureBytes, string viewerID, int left, int top, int width, int height, DateTime captureTime)
{
await Connection.SendAsync("SendScreenCapture", captureBytes, viewerID, left, top, width, height, captureTime);
@ -133,6 +137,10 @@ namespace Remotely.ScreenCast.Core.Sockets
await Connection.SendAsync("SendViewerRemoved", viewerID);
}
public async Task SendIceCandidateToBrowser(string candidate, int sdpMlineIndex, string sdpMid, string viewerConnectionID)
{
await Connection.SendAsync("SendIceCandidateToBrowser", candidate, sdpMlineIndex, sdpMid, viewerConnectionID);
}
private void ApplyConnectionHandlers()
{
var conductor = Conductor.Current;
@ -142,6 +150,37 @@ namespace Remotely.ScreenCast.Core.Sockets
return Task.CompletedTask;
};
Connection.On("ReceiveIceCandidate", (string candidate, int sdpMlineIndex, string sdpMid, string viewerID) =>
{
try
{
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
viewer.RtcSession.AddIceCandidate(sdpMid, sdpMlineIndex, candidate);
}
}
catch (Exception ex)
{
Logger.Write(ex);
}
});
Connection.On("ReceiveRtcAnswer", (string sdp, string viewerID) =>
{
try
{
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
viewer.RtcSession.SetRemoteDescription("answer", sdp);
}
}
catch (Exception ex)
{
Logger.Write(ex);
}
});
Connection.On("ClipboardTransfer", (string transferText, bool typeText, string viewerID) =>
{
try

View File

@ -0,0 +1,111 @@
using MessagePack;
using Microsoft.MixedReality.WebRTC;
using Remotely.ScreenCast.Core.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.ScreenCast.Core.Communication
{
public class WebRtcSession
{
public event EventHandler<(string candidate, int sdpMlineIndex, string sdpMid)> IceCandidateReady;
public event EventHandler<string> LocalSdpReady;
public bool IsDataChannelOpen => CaptureChannel?.State == DataChannel.ChannelState.Open;
private DataChannel CaptureChannel { get; set; }
private PeerConnection PeerConnection { get; set; }
public void AddIceCandidate(string sdpMid, int sdpMlineIndex, string candidate)
{
PeerConnection.AddIceCandidate(sdpMid, sdpMlineIndex, candidate);
}
public async Task Init()
{
PeerConnection = new PeerConnection();
var config = new PeerConnectionConfiguration()
{
IceServers = new List<IceServer>
{
new IceServer{ Urls = { "stun:stun.l.google.com:19302" } },
new IceServer{ Urls = { "stun:stun4.l.google.com:19302" } }
}
};
await PeerConnection.InitializeAsync(config);
PeerConnection.LocalSdpReadytoSend += PeerConnection_LocalSdpReadytoSend;
PeerConnection.Connected += PeerConnection_Connected;
PeerConnection.IceStateChanged += PeerConnection_IceStateChanged;
PeerConnection.IceCandidateReadytoSend += PeerConnection_IceCandidateReadytoSend;
CaptureChannel = await PeerConnection.AddDataChannelAsync("ScreenCapture", true, true);
CaptureChannel.BufferingChanged += DataChannel_BufferingChanged;
CaptureChannel.MessageReceived += CaptureChannel_MessageReceived;
CaptureChannel.StateChanged += CaptureChannel_StateChanged;
PeerConnection.CreateOffer();
}
public void SendCaptureFrame(int left, int top, int width, int height, byte[] imageBytes)
{
CaptureChannel.SendMessage(MessagePackSerializer.Serialize(new FrameInfo()
{
Left = left,
Top = top,
Width = width,
Height = height,
ImageBytes = imageBytes
}));
}
public void SetRemoteDescription(string type, string sdp)
{
PeerConnection.SetRemoteDescription(type, sdp);
if (type == "offer")
{
PeerConnection.CreateAnswer();
}
}
private void CaptureChannel_MessageReceived(byte[] obj)
{
Debug.WriteLine($"DataChannel message received. Size: {obj.Length}");
}
private void CaptureChannel_StateChanged()
{
Debug.WriteLine($"DataChannel state changed. New State: {CaptureChannel.State}");
}
private void DataChannel_BufferingChanged(ulong previous, ulong current, ulong limit)
{
Debug.WriteLine($"DataChannel buffering changed. Previous: {previous}. Current: {current}. Limit: {limit}.");
}
private void PeerConnection_Connected()
{
Debug.WriteLine("PeerConnection connected.");
}
private void PeerConnection_IceCandidateReadytoSend(string candidate, int sdpMlineindex, string sdpMid)
{
Debug.WriteLine("Ice candidate ready to send.");
IceCandidateReady?.Invoke(this, (candidate, sdpMlineindex, sdpMid));
}
private void PeerConnection_IceStateChanged(IceConnectionState newState)
{
Debug.WriteLine($"Ice state changed to {newState}.");
}
private void PeerConnection_LocalSdpReadytoSend(string type, string sdp)
{
Debug.WriteLine($"Local SDP ready.");
LocalSdpReady?.Invoke(this, sdp);
}
}
}

View File

@ -4,7 +4,7 @@ using Remotely.Shared.Models;
using Remotely.ScreenCast.Core.Enums;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Models;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
using Remotely.ScreenCast.Core.Services;
using System;
using System.Collections.Concurrent;

View File

@ -0,0 +1,22 @@
using MessagePack;
using System;
using System.Collections.Generic;
using System.Text;
namespace Remotely.ScreenCast.Core.Models
{
[MessagePackObject]
public class FrameInfo
{
[Key("Left")]
public int Left { get; set; }
[Key("Top")]
public int Top { get; set; }
[Key("Width")]
public int Width { get; set; }
[Key("Height")]
public int Height { get; set; }
[Key("ImageBytes")]
public byte[] ImageBytes { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using Remotely.ScreenCast.Core.Capture;
using Remotely.ScreenCast.Core.Communication;
using Remotely.ScreenCast.Core.Interfaces;
using System;
using System.Collections.Generic;
@ -54,6 +55,7 @@ namespace Remotely.ScreenCast.Core.Models
public double Latency { get; set; } = 1;
public string Name { get; set; }
public int OutputBuffer { get; set; }
public WebRtcSession RtcSession { get; set; }
public string ViewerConnectionID { get; set; }
}
}

View File

@ -34,6 +34,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.1" />
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.2" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</ItemGroup>

View File

@ -4,7 +4,7 @@ using System;
using System.Threading;
using Remotely.ScreenCast.Linux.Services;
using Remotely.ScreenCast.Linux.Capture;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
namespace Remotely.ScreenCast.Linux
{

View File

@ -10,7 +10,7 @@ using System.Threading;
using Remotely.ScreenCast.Win.Services;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Win.Capture;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
namespace Remotely.ScreenCast.Win
{

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<Platform>x64</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x64\publish\ScreenCast</PublishDir>
<SelfContained>false</SelfContained>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<Platform>x86</Platform>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x86\publish\ScreenCast</PublishDir>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>

View File

@ -13,6 +13,7 @@
<Description>Allows unattended remote control via the Remotely server.</Description>
<Copyright>Copyright © 2020 Translucency Software</Copyright>
<PackageProjectUrl>https://remotely.lucency.co</PackageProjectUrl>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>
<ItemGroup>
@ -33,12 +34,9 @@
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="favicon.ico" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="if $(ConfigurationName) == Debug (&#xD;&#xA; if $(PlatformName) == Any CPU (&#xD;&#xA; md &quot;$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\&quot;&#xD;&#xA; copy /y &quot;$(TargetDir)\*&quot; &quot;$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\&quot;&#xD;&#xA; )&#xD;&#xA;)" />
<Exec Command="if $(ConfigurationName) == Debug (&#xD;&#xA; if $(PlatformName) == Any CPU (&#xD;&#xA; md &quot;$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\&quot;&#xD;&#xA; xcopy &quot;$(TargetDir)*&quot; &quot;$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\&quot; /y /e /i&#xD;&#xA; )&#xD;&#xA; if $(PlatformName) == x64 (&#xD;&#xA; md &quot;$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\&quot;&#xD;&#xA; xcopy &quot;$(TargetDir)*&quot; &quot;$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\&quot; /y /e /i&#xD;&#xA; )&#xD;&#xA; if $(PlatformName) == x86 (&#xD;&#xA; md &quot;$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\&quot;&#xD;&#xA; xcopy &quot;$(TargetDir)*&quot; &quot;$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\&quot; /y /e /i&#xD;&#xA; )&#xD;&#xA;)" />
</Target>
</Project>

View File

@ -1,6 +1,6 @@
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Services;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Core.Communication;
using Remotely.Shared.Win32;
using System;
using System.Collections.Generic;

View File

@ -40,12 +40,14 @@
<Content Remove="wwwroot\scripts\Models\CursorInfo.ts" />
<Content Remove="wwwroot\scripts\Models\Device.ts" />
<Content Remove="wwwroot\scripts\Models\DevicePermissionLink.ts" />
<Content Remove="wwwroot\scripts\Models\FrameInfo.ts" />
<Content Remove="wwwroot\scripts\Models\GenericCommandResult.ts" />
<Content Remove="wwwroot\scripts\Models\Parameter.ts" />
<Content Remove="wwwroot\scripts\Models\Point.ts" />
<Content Remove="wwwroot\scripts\Models\UserOptions.ts" />
<Content Remove="wwwroot\scripts\Pages\OrganizationManagement.ts" />
<Content Remove="wwwroot\scripts\RemoteControl\Main.ts" />
<Content Remove="wwwroot\scripts\RemoteControl\RtcSession.ts" />
<Content Remove="wwwroot\scripts\RemoteControl\UI.ts" />
<Content Remove="wwwroot\scripts\ResultsParser.ts" />
<Content Remove="wwwroot\scripts\RTC.ts" />
@ -142,12 +144,14 @@
<TypeScriptCompile Include="wwwroot\scripts\CommandProcessor.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\CommandLineParameter.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\ConsoleCommand.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\FrameInfo.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\Point.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\UserOptions.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\Device.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Models\Parameter.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Pages\OrganizationManagement.ts" />
<TypeScriptCompile Include="wwwroot\scripts\RemoteControl\Main.ts" />
<TypeScriptCompile Include="wwwroot\scripts\RemoteControl\RtcSession.ts" />
<TypeScriptCompile Include="wwwroot\scripts\RemoteControl\UI.ts" />
<TypeScriptCompile Include="wwwroot\scripts\ResultsParser.ts" />
<TypeScriptCompile Include="wwwroot\scripts\RemoteControl\RCBrowserSockets.ts" />

View File

@ -247,5 +247,15 @@ namespace Remotely.Server.Services
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("TouchUp", Context.ConnectionId);
}
public async Task SendIceCandidateToAgent(string candidate, int sdpMlineIndex, string sdpMid)
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ReceiveIceCandidate", candidate, sdpMlineIndex, sdpMid, Context.ConnectionId);
}
public async Task SendRtcAnswerToAgent(string sdp)
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ReceiveRtcAnswer", sdp, Context.ConnectionId);
}
}
}

View File

@ -154,6 +154,7 @@ namespace Remotely.Server.Services
SessionInfo.MachineName = machineName;
SessionInfo.DeviceID = deviceID;
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("AudioSample", buffer);
@ -163,6 +164,7 @@ namespace Remotely.Server.Services
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ClipboardTextChanged", clipboardText);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
@ -177,6 +179,16 @@ namespace Remotely.Server.Services
{
await RCBrowserHub.Clients.Client(viewerID).SendAsync("ReceiveMachineName", machineName);
}
public async Task SendRtcOfferToBrowser(string sdp, string viewerID)
{
await RCBrowserHub.Clients.Client(viewerID).SendAsync("ReceiveRtcOffer", sdp);
}
public async Task SendIceCandidateToBrowser(string candidate, int sdpMlineIndex, string sdpMid, string viewerID)
{
await RCBrowserHub.Clients.Client(viewerID).SendAsync("ReceiveIceCandidate", candidate, sdpMlineIndex, sdpMid);
}
public Task SendScreenCapture(byte[] captureBytes, string rcBrowserHubConnectionID, int left, int top, int width, int height, DateTime captureTime)
{
if (AppConfig.RecordRemoteControlSessions)

View File

@ -25,6 +25,11 @@
"msgpack5": "^4.0.2"
}
},
"@msgpack/msgpack": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-1.11.0.tgz",
"integrity": "sha512-gR/NgUeQGARNabGVAjS59lagyxsCgD0zd4F5oKf9uKt8XNLu4awFBNO/Nstr2q1Iu1UPC5EeJeLZSNdP30m7zQ=="
},
"@types/bootstrap": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-4.3.1.tgz",

View File

@ -0,0 +1 @@
//# sourceMappingURL=FrameInfo.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"FrameInfo.js","sourceRoot":"","sources":["FrameInfo.ts"],"names":[],"mappings":""}

View File

@ -0,0 +1,7 @@
declare interface FrameInfo {
Left: number;
Top: number;
Width: number;
Height: number;
ImageBytes: Uint8Array;
}

View File

@ -1,10 +1,12 @@
import * as Utilities from "../Utilities.js";
import { RCBrowserSockets } from "./RCBrowserSockets.js";
import { RtcSession } from "./RtcSession.js";
import * as UI from "./UI.js";
import { RemoteControlMode } from "../Enums/RemoteControlMode.js";
var queryString = Utilities.ParseSearchString();
export const RemoteControl = {
RCBrowserSockets: new RCBrowserSockets(),
RtcSession: new RtcSession(),
ClientID: queryString["clientID"] ? decodeURIComponent(queryString["clientID"]) : "",
ServiceID: queryString["serviceID"] ? decodeURIComponent(queryString["serviceID"]) : "",
RequesterName: queryString["requesterName"] ? decodeURIComponent(queryString["requesterName"]) : "",

View File

@ -1 +1 @@
{"version":3,"file":"Main.js","sourceRoot":"","sources":["Main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE,IAAI,WAAW,GAAG,SAAS,CAAC,iBAAiB,EAAE,CAAC;AAEhD,MAAM,CAAC,MAAM,aAAa,GAAG;IAEzB,gBAAgB,EAAE,IAAI,gBAAgB,EAAE;IACxC,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACpF,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACvF,aAAa,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACnG,IAAI,EAAE,iBAAiB,CAAC,IAAI;IAE5B,IAAI,EAAE,GAAG,EAAE;QACP,EAAE,CAAC,kBAAkB,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAEtD,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE;YACzB,aAAa,CAAC,IAAI,GAAG,iBAAiB,CAAC,UAAU,CAAC;YAClD,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;YACrC,aAAa,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;SAC5C;aACI,IAAI,WAAW,CAAC,WAAW,CAAC,EAAE;YAC/B,EAAE,CAAC,cAAc,CAAC,KAAK,GAAG,kBAAkB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;YACvE,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE;gBAC9B,EAAE,CAAC,kBAAkB,CAAC,KAAK,GAAG,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;gBAC/E,IAAI,CAAC,eAAe,EAAE,CAAC;aAC1B;SACJ;IACL,CAAC;IACD,eAAe,EAAE,GAAG,EAAE;QAClB,EAAE,CAAC,aAAa,CAAC,QAAQ,GAAG,IAAI,CAAC;QACjC,aAAa,CAAC,QAAQ,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrE,aAAa,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC;QAC1D,aAAa,CAAC,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC;QAC9C,aAAa,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QACzC,EAAE,CAAC,aAAa,CAAC,SAAS,GAAG,+BAA+B,CAAC;IACjE,CAAC;CACJ,CAAA;AAED,MAAM,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC"}
{"version":3,"file":"Main.js","sourceRoot":"","sources":["Main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE,IAAI,WAAW,GAAG,SAAS,CAAC,iBAAiB,EAAE,CAAC;AAEhD,MAAM,CAAC,MAAM,aAAa,GAAG;IAEzB,gBAAgB,EAAE,IAAI,gBAAgB,EAAE;IACxC,UAAU,EAAE,IAAI,UAAU,EAAE;IAC5B,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACpF,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACvF,aAAa,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACnG,IAAI,EAAE,iBAAiB,CAAC,IAAI;IAE5B,IAAI,EAAE,GAAG,EAAE;QACP,EAAE,CAAC,kBAAkB,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAEtD,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE;YACzB,aAAa,CAAC,IAAI,GAAG,iBAAiB,CAAC,UAAU,CAAC;YAClD,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;YACrC,aAAa,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;SAC5C;aACI,IAAI,WAAW,CAAC,WAAW,CAAC,EAAE;YAC/B,EAAE,CAAC,cAAc,CAAC,KAAK,GAAG,kBAAkB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;YACvE,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE;gBAC9B,EAAE,CAAC,kBAAkB,CAAC,KAAK,GAAG,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;gBAC/E,IAAI,CAAC,eAAe,EAAE,CAAC;aAC1B;SACJ;IACL,CAAC;IACD,eAAe,EAAE,GAAG,EAAE;QAClB,EAAE,CAAC,aAAa,CAAC,QAAQ,GAAG,IAAI,CAAC;QACjC,aAAa,CAAC,QAAQ,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrE,aAAa,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC;QAC1D,aAAa,CAAC,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC;QAC9C,aAAa,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QACzC,EAAE,CAAC,aAAa,CAAC,SAAS,GAAG,+BAA+B,CAAC;IACjE,CAAC;CACJ,CAAA;AAED,MAAM,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC"}

View File

@ -1,5 +1,6 @@
import * as Utilities from "../Utilities.js";
import { RCBrowserSockets } from "./RCBrowserSockets.js";
import { RtcSession } from "./RtcSession.js";
import * as UI from "./UI.js";
import { RemoteControlMode } from "../Enums/RemoteControlMode.js";
@ -9,6 +10,7 @@ var queryString = Utilities.ParseSearchString();
export const RemoteControl = {
RCBrowserSockets: new RCBrowserSockets(),
RtcSession: new RtcSession(),
ClientID: queryString["clientID"] ? decodeURIComponent(queryString["clientID"]) : "",
ServiceID: queryString["serviceID"] ? decodeURIComponent(queryString["serviceID"]) : "",
RequesterName: queryString["requesterName"] ? decodeURIComponent(queryString["requesterName"]) : "",

View File

@ -33,6 +33,14 @@ export class RCBrowserSockets {
});
}
;
SendIceCandidate(candidate) {
if (candidate) {
this.Connection.invoke("SendIceCandidateToAgent", candidate.candidate, candidate.sdpMLineIndex, candidate.sdpMid);
}
}
SendRtcAnswer(sessionDescription) {
this.Connection.invoke("SendRtcAnswerToAgent", sessionDescription.sdp);
}
SendScreenCastRequestToDevice() {
this.Connection.invoke("SendScreenCastRequestToDevice", RemoteControl.ClientID, RemoteControl.RequesterName, RemoteControl.Mode);
}
@ -187,6 +195,19 @@ export class RCBrowserSockets {
hubConnection.on("RequestingScreenCast", () => {
UI.ShowMessage("Requesting remote control...");
});
hubConnection.on("ReceiveRtcOffer", async (sdp) => {
console.log("Rtc offer SDP received.");
RemoteControl.RtcSession.Init();
await RemoteControl.RtcSession.ReceiveRtcOffer(sdp);
});
hubConnection.on("ReceiveIceCandidate", (candidate, sdpMlineIndex, sdpMid) => {
console.log("Ice candidate received.");
RemoteControl.RtcSession.ReceiveCandidate({
candidate: candidate,
sdpMLineIndex: sdpMlineIndex,
sdpMid: sdpMid
});
});
}
}
//# sourceMappingURL=RCBrowserSockets.js.map

File diff suppressed because one or more lines are too long

View File

@ -46,6 +46,17 @@ export class RCBrowserSockets {
UI.ConnectBox.style.removeProperty("display");
});
};
SendIceCandidate(candidate: RTCIceCandidate) {
if (candidate) {
this.Connection.invoke("SendIceCandidateToAgent", candidate.candidate, candidate.sdpMLineIndex, candidate.sdpMid);
}
}
SendRtcAnswer(sessionDescription: RTCSessionDescription) {
this.Connection.invoke("SendRtcAnswerToAgent", sessionDescription.sdp);
}
SendScreenCastRequestToDevice() {
this.Connection.invoke("SendScreenCastRequestToDevice", RemoteControl.ClientID, RemoteControl.RequesterName, RemoteControl.Mode);
}
@ -205,5 +216,20 @@ export class RCBrowserSockets {
hubConnection.on("RequestingScreenCast", () => {
UI.ShowMessage("Requesting remote control...");
});
hubConnection.on("ReceiveRtcOffer", async (sdp: string) => {
console.log("Rtc offer SDP received.");
RemoteControl.RtcSession.Init();
await RemoteControl.RtcSession.ReceiveRtcOffer(sdp);
});
hubConnection.on("ReceiveIceCandidate", (candidate: string, sdpMlineIndex: number, sdpMid: string) => {
console.log("Ice candidate received.");
RemoteControl.RtcSession.ReceiveCandidate({
candidate: candidate,
sdpMLineIndex: sdpMlineIndex,
sdpMid: sdpMid
} as any);
});
}
}

View File

@ -0,0 +1,73 @@
import * as UI from "./UI.js";
import * as Utilities from "../Utilities.js";
import { RemoteControl } from "./Main.js";
export class RtcSession {
constructor() {
this.MsgPack5 = new window['msgpack5']();
}
Init() {
this.PeerConnection = new RTCPeerConnection({
iceServers: [
{ urls: "stun: stun.l.google.com:19302" },
{ urls: "stun:stun4.l.google.com:19302" }
]
});
this.PeerConnection.ondatachannel = (ev) => {
console.log("Data channel received.");
this.DataChannel = ev.channel;
this.DataChannel.onbufferedamountlow = (ev) => {
console.log("Buffer amount low.");
};
this.DataChannel.onclose = (ev) => {
console.log("Data channel closed.");
};
this.DataChannel.onerror = (ev) => {
console.log("Data channel error.", ev.error);
};
this.DataChannel.onmessage = (ev) => {
var frameInfo = this.MsgPack5.decode(ev.data);
console.debug("Data channel message.");
var url = window.URL.createObjectURL(new Blob([frameInfo.ImageBytes]));
var img = document.createElement("img");
img.onload = () => {
UI.Screen2DContext.drawImage(img, frameInfo.Left, frameInfo.Top, frameInfo.Width, frameInfo.Height);
window.URL.revokeObjectURL(url);
};
img.src = url;
};
this.DataChannel.onopen = (ev) => {
console.log("Data channel opened.");
};
};
this.PeerConnection.onconnectionstatechange = function (ev) {
console.log("Connection state changed to " + this.connectionState);
};
this.PeerConnection.oniceconnectionstatechange = function (ev) {
console.log("Connection state changed to " + this.iceConnectionState);
};
this.PeerConnection.onicecandidate = async (ev) => {
await RemoteControl.RCBrowserSockets.SendIceCandidate(ev.candidate);
};
}
Disconnect() {
this.PeerConnection.close();
}
async ReceiveRtcOffer(sdp) {
await this.PeerConnection.setRemoteDescription({ type: "offer", sdp: sdp });
Utilities.When(() => {
return this.PeerConnection.remoteDescription.sdp.length > 0;
}).then(async () => {
await this.PeerConnection.setLocalDescription(await this.PeerConnection.createAnswer());
await RemoteControl.RCBrowserSockets.SendRtcAnswer(this.PeerConnection.localDescription);
});
}
async ReceiveCandidate(candidate) {
Utilities.When(() => {
return this.PeerConnection.remoteDescription.sdp.length > 0;
}).then(async () => {
await this.PeerConnection.addIceCandidate(candidate);
console.log("Set ICE candidate.");
});
}
}
//# sourceMappingURL=RtcSession.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"RtcSession.js","sourceRoot":"","sources":["RtcSession.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,77 @@
import * as UI from "./UI.js";
import * as Utilities from "../Utilities.js";
import { RemoteControl } from "./Main.js";
export class RtcSession {
PeerConnection: RTCPeerConnection;
DataChannel: RTCDataChannel;
MsgPack5: any = new window['msgpack5']();
Init() {
this.PeerConnection = new RTCPeerConnection({
iceServers: [
{ urls: "stun: stun.l.google.com:19302" },
{ urls: "stun:stun4.l.google.com:19302" }
]
});
this.PeerConnection.ondatachannel = (ev) => {
console.log("Data channel received.");
this.DataChannel = ev.channel;
this.DataChannel.onbufferedamountlow = (ev) => {
console.log("Buffer amount low.");
};
this.DataChannel.onclose = (ev) => {
console.log("Data channel closed.");
};
this.DataChannel.onerror = (ev) => {
console.log("Data channel error.", ev.error);
};
this.DataChannel.onmessage = (ev) => {
var frameInfo = this.MsgPack5.decode(ev.data) as FrameInfo;
console.debug("Data channel message.");
var url = window.URL.createObjectURL(new Blob([frameInfo.ImageBytes]));
var img = document.createElement("img");
img.onload = () => {
UI.Screen2DContext.drawImage(img, frameInfo.Left, frameInfo.Top, frameInfo.Width, frameInfo.Height);
window.URL.revokeObjectURL(url);
};
img.src = url;
};
this.DataChannel.onopen = (ev) => {
console.log("Data channel opened.");
};
};
this.PeerConnection.onconnectionstatechange = function (ev) {
console.log("Connection state changed to " + this.connectionState);
}
this.PeerConnection.oniceconnectionstatechange = function (ev) {
console.log("Connection state changed to " + this.iceConnectionState);
}
this.PeerConnection.onicecandidate = async (ev) => {
await RemoteControl.RCBrowserSockets.SendIceCandidate(ev.candidate);
};
}
Disconnect() {
this.PeerConnection.close();
}
async ReceiveRtcOffer(sdp: string) {
await this.PeerConnection.setRemoteDescription({ type: "offer", sdp: sdp });
Utilities.When(() => {
return this.PeerConnection.remoteDescription.sdp.length > 0;
}).then(async () => {
await this.PeerConnection.setLocalDescription(await this.PeerConnection.createAnswer());
await RemoteControl.RCBrowserSockets.SendRtcAnswer(this.PeerConnection.localDescription);
})
}
async ReceiveCandidate(candidate: RTCIceCandidate) {
Utilities.When(() => {
return this.PeerConnection.remoteDescription.sdp.length > 0;
}).then(async () => {
await this.PeerConnection.addIceCandidate(candidate);
console.log("Set ICE candidate.");
})
}
}