WebRTC.Net庫開發進階,教你實現屏幕共享和多路復用!
WebRTC.Net庫:讓你的應用更親民友好,實現視頻通話無痛接入! 除了基本用法外,還有一些進階用法可以更好地利用該庫。
自定義 STUN/TURN 服務器配置
WebRTC.Net 默認使用 Google 的 STUN 服務器和 Coturn 的 TURN 服務器。如果你需要使用其他 STUN/TURN 服務器,則可以在初始化 PeerConnectionFactory 和 PeerConnection 時設置自定義配置。
例如,以下代碼設置了使用 coturn 服務器的 PeerConnectionFactory:
var config = new PeerConnectionConfiguration
{
IceServers = new List<IceServer>
{
new IceServer{ Urls = new[] { "stun:stun.l.google.com:19302" }},
new IceServer{ Urls = new[] { "turn:my-turn-server.com" }, Username="myusername", Credential="mypassword" }
}
};
var factory = new PeerConnectionFactory(config);
在不同線程中創建和使用 PeerConnectionFactory 和 PeerConnection 對象:
WebRTC.Net 庫本質上是基于線程的,因此它的對象通常在單獨的線程中創建和使用。這樣可以避免在主線程中對 UI 線程造成大量負擔。
以下代碼在后臺線程中創建并使用 PeerConnection 對象:
Task.Run(() =>
{
var config = new PeerConnectionConfiguration { IceServers = new List<IceServer> { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } } };
var factory = new PeerConnectionFactory(config);
var pc = factory.CreatePeerConnection(config);
// 在這里使用 PeerConnection 對象,不會阻塞主線程
}).Wait();
選擇視頻和音頻設備
在創建 PeerConnectionFactory 對象時,可以設置 defaultAudioDevice 和 defaultVideoDevice 參數以選擇默認的音頻和視頻設備。
例如,以下如何通過設備名稱選擇視頻和音頻設備:
var config = new PeerConnectionConfiguration
{
IceServers = new List<IceServer> { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } },
DefaultVideoDevice = VideoCaptureDevice.GetDevices().FirstOrDefault(x => x.Name == "MyCameraName"),
DefaultAudioDevice = AudioCaptureDevice.GetDevices().FirstOrDefault(x => x.Name == "MyMicrophoneName")
};
var factory = new PeerConnectionFactory(config);
實現數據通道
WebRTC.Net 庫不僅支持音視頻傳輸,還支持實現數據通道(DataChannel)。使用數據通道,應用程序可以在客戶端之間傳輸任意類型的數據,例如聊天消息、游戲狀態等。
以下代碼如何創建數據通道:
// 創建 PeerConnection 對象
var config = new PeerConnectionConfiguration { IceServers = new List<IceServer> { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } } };
var factory = new PeerConnectionFactory(config);
var pc = factory.CreatePeerConnection(config);
// 創建數據通道
var dcConfig = new DataChannelInit { Ordered = true };
var dc = pc.CreateDataChannel("mydatachannel", dcConfig);
// 監聽數據通道事件
dc.MessageReceived += (sender, e) =>
{
// 處理接收到的數據
};
實現屏幕共享
除了音視頻傳輸和數據通道,WebRTC.Net 還支持屏幕共享。這意味著應用程序可以捕獲屏幕上的內容并將其共享給其他客戶端。
以下是使用 WinForm 技術棧和 WebRTC.Net 庫實現桌面共享的示例代碼。
using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX.Direct3D11;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using Org.WebRtc;
namespace DesktopStreaming
{
public partial class MainForm : Form
{
private PeerConnection _peerConnection;
private DataChannel _dataChannel;
private Direct3D11CaptureFramePool _framePool;
private GraphicsCaptureSession _session;
private VideoTrack _videoTrack;
public MainForm()
{
InitializeComponent();
// 初始化 WebRTC
WebRTC.Initialize(new WebRTCInitializationOptions { EnableAudioBufferLog = false });
// 創建 PeerConnectionFactory 對象
var config = new PeerConnectionConfiguration { IceServers = new[] { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } } };
var factory = new PeerConnectionFactory(config);
// 創建 PeerConnection 對象
_peerConnection = factory.CreatePeerConnection();
// 創建數據通道
_dataChannel = _peerConnection.CreateDataChannel("mychannel");
// 訂閱數據通道的消息事件
_dataChannel.MessageReceived += (sender, args) =>
{
// 處理收到的消息
};
// 創建 Direct3D11CaptureFramePool 對象
var device = Direct3D11Helpers.CreateDevice();
var size = new Size(800, 600);
_framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(
device,
Direct3DPixelFormat.B8G8R8A8UIntNormalized,
1,
size);
// 訂閱 FrameArrived 事件
_framePool.FrameArrived += (sender, args) =>
{
// 獲取最新的桌面幀
using var frame = sender.TryGetNextFrame();
if (frame == null) return;
// 將桌面幀轉換為 RTCVideoFrame 對象
var videoFrame = new RTCVideoFrame(frame.ContentSize.Width, frame.ContentSize.Height, RTCVideoFrameType.RTCVideoFrameTypeI420);
videoFrame.ConvertFromArgb32(frame.Surface.Direct3D11Device, frame.Surface);
// 將 RTCVideoFrame 對象轉換為 VideoTrack 對象并發送
if (_videoTrack != null)
_videoTrack.PushFrame(videoFrame);
};
// 創建 GraphicsCaptureItem 對象
var item = ScreenCapture.GetDefault();
// 創建 GraphicsCaptureSession 對象
_session = _framePool.CreateCaptureSession(item);
}
private async void btnStart_Click(object sender, EventArgs e)
{
// 開始共享桌面
await _session.StartAsync();
// 創建視頻軌道
_videoTrack = await PeerConnectionFactory.GetVideoTrackSourceAsync(_framePool);
// 添加視頻軌道到 PeerConnection 對象
await _peerConnection.AddTrack(_videoTrack);
// 創建 Offer SDP 并設置本地描述符
var offerSdp = await _peerConnection.CreateOffer();
await _peerConnection.SetLocalDescription(offerSdp);
// 發送 Offer SDP 到遠端
SendSdp(offerSdp);
}
private void SendSdp(RTCSessionDescription sdp)
{
// 將 SDP 轉換為 JSON 格式并發送到遠端
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { type = sdp.Type, sdp = sdp.Sdp });
_dataChannel.Send(json);
}
private async void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// 關閉 PeerConnection 和 GraphicsCaptureSession 對象
await _peerConnection.CloseAsync();
_session.Dispose();
}
}
}
上述代碼中,我們使用了 ScreenCapture 類來獲取默認的桌面捕獲項目,然后創建了 GraphicsCaptureSession 對象來捕獲桌面幀。我們還使用了
Direct3D11CaptureFramePool 類來創建一個 Direct3D 11 幀池,并訂閱了 FrameArrived 事件以獲取最新的桌面幀。在每次收到桌面幀時,我們將其轉換為 RTCVideoFrame 對象,再將其發送到 WebRTC 連接中。通過這種方式,我們就實現了桌面共享的功能。
需要注意的是,由于 WebRTC 是基于 p2p 的實時通信協議,因此本示例代碼中僅演示了如何將桌面共享的數據發送給遠端客戶端,而沒有涉及如何在遠端客戶端上解析和顯示收到的數據。
處理 ICE 連接狀態
WebRTC.Net 使用 ICE(Interactive Connectivity Establishment)協議來建立和維護客戶端之間的連接。ICE 協議涉及多個狀態和事件,例如 gathering、connected、disconnected 等等。應用程序可以訂閱 PeerConnection 對象上的各種事件來處理這些狀態。
以下代碼如何訂閱 PeerConnection 對象上的連接狀態:
// 創建 PeerConnection 對象
var config = new PeerConnectionConfiguration { IceServers = new List<IceServer> { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } } };
var factory = new PeerConnectionFactory(config);
var pc = factory.CreatePeerConnection(config);
// 訂閱 PeerConnection 對象上的連接狀態
pc.IceStateChanged += (sender, iceState) =>
{
if (iceState == IceConnectionState.Connected)
{
// 客戶端已成功連接
}
else if (iceState == IceConnectionState.Disconnected)
{
// 客戶端已斷開連接
}
};
實現多路復用
WebRTC.Net 支持實現多路復用(Multiplexing),這意味著應用程序可以在同一個數據通道上同時傳輸多種類型的數據,例如音頻、視頻、文件等。
下面是使用 WinForm 技術棧和 WebRTC.Net 庫實現多路復用的示例代碼。
Copy Codeusing System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using Org.WebRtc;
namespace WebRTC_Multiplexing
{
public partial class Form1 : Form
{
private PeerConnection _peerConnection;
private List<DataChannel> _dataChannels = new List<DataChannel>();
public Form1()
{
InitializeComponent();
// 初始化 WebRTC
WebRTC.Initialize(new WebRTCInitializationOptions { EnableAudioBufferLog = false });
// 創建 PeerConnectionFactory 對象
var config = new PeerConnectionConfiguration { IceServers = new[] { new IceServer { Urls = new[] { "stun:stun.l.google.com:19302" } } } };
var factory = new PeerConnectionFactory(config);
// 創建 PeerConnection 對象
_peerConnection = factory.CreatePeerConnection();
// 訂閱 PeerConnection 的連接狀態改變事件
_peerConnection.ConnectionStateChanged += (sender, args) =>
{
// 處理連接狀態改變事件
BeginInvoke(new Action(() => txtOutput.AppendText($"連接狀態:{args.NewState.ToString()}\r\n")));
};
// 訂閱 PeerConnection 的數據通道回調事件
_peerConnection.DataChannelAdded += (sender, args) =>
{
// 處理數據通道回調事件
var dataChannel = args.Channel;
dataChannel.MessageReceived += DataChannel_MessageReceived;
_dataChannels.Add(dataChannel);
BeginInvoke(new Action(() => txtOutput.AppendText($"收到數據通道:{dataChannel.Label}\r\n")));
};
}
private async void btnCreateOffer_Click(object sender, EventArgs e)
{
// 創建 Offer SDP 并設置本地描述符
var offerSdp = await _peerConnection.CreateOffer();
await _peerConnection.SetLocalDescription(offerSdp);
// 發送 Offer SDP 到對端
SendSdp(offerSdp);
}
private void SendSdp(RTCSessionDescription sdp)
{
// 將 SDP 轉換為 JSON 格式并發送到對端
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { type = sdp.Type, sdp = sdp.Sdp });
_dataChannels.ForEach(dc => dc.Send(json));
}
private async void DataChannel_MessageReceived(object sender, DataChannelMessageEventArgs e)
{
// 收到數據通道消息后將其轉換為 RTCSessionDescription 對象
if (e.MessageType == DataMessageType.Text)
{
var text = e.Data;
var sdp = Newtonsoft.Json.JsonConvert.DeserializeObject<RTCSessionDescription>(text);
// 設置遠端描述符并完成連接
await _peerConnection.SetRemoteDescription(sdp);
if (sdp.Type == RTCSessionDescriptionType.Offer) await _peerConnection.CreateAnswer();
}
}
}
}
上述代碼中,我們創建了一個 PeerConnectionFactory 對象和一個 PeerConnection 對象,用于建立 WebRTC 連接。我們還創建了一個 _dataChannels 列表來保存所有的數據通道對象,每當 PeerConnection 對象添加一個新的數據通道時,我們就將其添加到 _dataChannels 列表中。
在 btnCreateOffer_Click 事件處理方法中,我們創建了一個 Offer SDP 并設置本地描述符,然后將其發送到所有的數據通道對象中。當收到對端發送過來的 SDP 消息時,我們將其轉換為 RTCSessionDescription 對象,并調用 SetRemoteDescription 方法設置遠端描述符。如果收到來自對端的 Offer SDP,則執行 CreateAnswer 方法創建 Answer SDP 并將其發送回對端。
通過這種方式,我們就可以使用同一個 PeerConnection 對象來支持多路復用。每當需要發送數據時,只需要將數據發送到指定的數據通道對象即可。需要注意的是,在使用多路復用時,我們需要為不同的數據通道設置不同的標簽(Label),以便在接收端識別不同的通道。