mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
WebRTC working.
This commit is contained in:
parent
bc27bd77ea
commit
94b78488f1
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
38
Remotely.sln
38
Remotely.sln
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
111
ScreenCast.Core/Communication/WebRtcSession.cs
Normal file
111
ScreenCast.Core/Communication/WebRtcSession.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
22
ScreenCast.Core/Models/FrameInfo.cs
Normal file
22
ScreenCast.Core/Models/FrameInfo.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 (
 if $(PlatformName) == Any CPU (
 md "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\"
 copy /y "$(TargetDir)\*" "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\"
 )
)" />
|
||||
<Exec Command="if $(ConfigurationName) == Debug (
 if $(PlatformName) == Any CPU (
 md "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
 if $(PlatformName) == x64 (
 md "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
 if $(PlatformName) == x86 (
 md "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
)" />
|
||||
</Target>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
5
Server/package-lock.json
generated
5
Server/package-lock.json
generated
@ -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",
|
||||
|
||||
1
Server/wwwroot/scripts/Models/FrameInfo.js
Normal file
1
Server/wwwroot/scripts/Models/FrameInfo.js
Normal file
@ -0,0 +1 @@
|
||||
//# sourceMappingURL=FrameInfo.js.map
|
||||
1
Server/wwwroot/scripts/Models/FrameInfo.js.map
Normal file
1
Server/wwwroot/scripts/Models/FrameInfo.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"FrameInfo.js","sourceRoot":"","sources":["FrameInfo.ts"],"names":[],"mappings":""}
|
||||
7
Server/wwwroot/scripts/Models/FrameInfo.ts
Normal file
7
Server/wwwroot/scripts/Models/FrameInfo.ts
Normal file
@ -0,0 +1,7 @@
|
||||
declare interface FrameInfo {
|
||||
Left: number;
|
||||
Top: number;
|
||||
Width: number;
|
||||
Height: number;
|
||||
ImageBytes: Uint8Array;
|
||||
}
|
||||
@ -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"]) : "",
|
||||
|
||||
@ -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"}
|
||||
@ -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"]) : "",
|
||||
|
||||
@ -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
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
73
Server/wwwroot/scripts/RemoteControl/RtcSession.js
Normal file
73
Server/wwwroot/scripts/RemoteControl/RtcSession.js
Normal 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
|
||||
1
Server/wwwroot/scripts/RemoteControl/RtcSession.js.map
Normal file
1
Server/wwwroot/scripts/RemoteControl/RtcSession.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"RtcSession.js","sourceRoot":"","sources":["RtcSession.ts"],"names":[],"mappings":"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"}
|
||||
77
Server/wwwroot/scripts/RemoteControl/RtcSession.ts
Normal file
77
Server/wwwroot/scripts/RemoteControl/RtcSession.ts
Normal 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.");
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user