前言 本文主要是整理了使用WebRTC做音視頻通訊時的各知識點及問題點。有理解不足和不到位的地方也歡迎指正。 對於你感興趣的部分可以選擇性觀看。 WebRTC的初始化 在使用WebRTC的庫之前,需要對WebRTC進行初始化, 用到的代碼如下: RTCInitializeSSL(); 轉定義後可以看 ...
前言
本文主要是整理了使用WebRTC做音視頻通訊時的各知識點及問題點。有理解不足和不到位的地方也歡迎指正。 對於你感興趣的部分可以選擇性觀看。
WebRTC的初始化
在使用WebRTC的庫之前,需要對WebRTC進行初始化, 用到的代碼如下:
RTCInitializeSSL();
轉定義後可以看到方法的聲明:
/**
* Initialize and clean up the SSL library. Failure is fatal. These call the
* corresponding functions in webrtc/rtc_base/ssladapter.h.
*/
RTC_EXTERN BOOL RTCInitializeSSL(void);
RTC_EXTERN BOOL RTCCleanupSSL(void);
Initialize and clean up the SSL library. Failure is fatal. 初始化SSL庫,失敗是致命的。
函數返回的是一個布爾類型
, 表示初始化的結果。 如果失敗,則不能繼續使用其他特性。這是使用WebRTC的前提
PeerConnection工廠的創建
在 WebRTC Native 層,factory 可以說是 “萬物的根源”,像 RTCVideoSource
、RTCVideoTrack
、RTCPeerConnection
這些類型的對象,都需要通過 factory 來創建
[RTCPeerConnectionFactory initialize];
//如果點對點工廠為空
if (!factory)
{
RTCDefaultVideoDecoderFactory* decoderFactory = [[RTCDefaultVideoDecoderFactory alloc] init];
RTCDefaultVideoEncoderFactory* encoderFactory = [[RTCDefaultVideoEncoderFactory alloc] init];
NSArray* codecs = [encoderFactory supportedCodecs];
[encoderFactory setPreferredCodec:codecs[2]];
factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory: encoderFactory
decoderFactory: decoderFactory];
}
- 首先要調用
RTCPeerConnectionFactory
類的initialize
方法進行初始化;- 然後創建 factory 對象。需要註意的是,在創建 factory 對象時,傳入了兩個參數:一個是預設的
編碼器
;一個是預設的解碼器
。我們可以通過修改這兩個參數來達到使用不同編解碼器的目的。
獲取本地視頻流
在獲取視頻之前,我們首先要選擇使用哪個視頻設備採集數據。在WebRTC中,我們可以通過RTCCameraVideoCapture
類獲取所有的視頻設備。如下所示:
NSArray<AVCaptureDevice*>* devices = [RTCCameraVideoCapture captureDevices];
AVCaptureDevice* device = devices[0];
通過上面兩行代碼,我們就拿到了視頻設備中的第一個設備。當然,光有設備還不行。我們還要清楚從設備中採集的數據放到哪裡了,這樣我們才能將其展示出來。WebRTC 為我們提供了一個專門的類,即 RTCVideoSource
, 它有兩層含義:
- 一是表明它是一個
視頻源
。當我們要展示視頻的時候,就從這裡獲取數據; - 另一方面,它也是一個終點。即,當我們從視頻設備採集到視頻數據時,要交給它暫存起來。
RTCVideoSource* videoSource = [factory videoSource];
除此之外,為了能更方便的控制視頻設備,WebRTC 提供了一個專門用於操作設備的類,即 RTCCameraVideoCapture
。通過它,我們就可以自如的控制視頻設備
了。
RTCVideoSource* videoSource = [factory videoSource];
capture = [[RTCCameraVideoCapturer alloc] initWithDelegate:videoSource];
[capture startCaptureWithDevice:device
format:format
fps:fps];
現在已經可以通過RTCCameraVideoCapture
類控制視頻設備來採集視頻了, 那如何獲取採集的視頻流呢 ? 上面的代碼我們已經將視頻採集到視頻源RTCVideoSource
了, 那RTCVideoSource
就是我們的視頻流
嗎 ?顯然不是。 這裡要提到的是WebRTC三大對象中的其中一個對象RTCMediaStream
,它才是我們說的視頻流。那它和RTCVideoSource之間是什麼關係呢,之間是如何建立關聯的呢?
//創建本地流
_localStream = [_factory mediaStreamWithStreamId:@"ARDAMS"];
//獲取數據源
_localVideoSource = [_factory videoSource];
//音頻
RTCAudioTrack * audioTrack = [_factory audioTrackWithTrackId:@"ARDAMSa0"];
//視頻
RTCVideoTrack *videoTrack = [_factory videoTrackWithSource:_localVideoSource trackId:@"ARDAMSv0"];
//將audioTrack、videoTrack添加到流
[_localStream addAudioTrack:audioTrack];
[_localStream addVideoTrack:videoTrack];
//拿到capture對象
RTCCameraVideoCapturer * capture = [[RTCCameraVideoCapturer alloc] initWithDelegate:_localVideoSource];
原來是通過一個中間對象RTCVideoTrack
建立的關聯。
RTCCameraVideoCapturer
將採集的視頻數據
交給RTCVideoSource
- 通過
RTCVideoSource
創建RTCVideoTrack
RTCMediaStream
添加視頻軌 videoTrack。
獲取本地流完整的代碼如下:
if (!_localStream) {
NSArray<AVCaptureDevice *> *captureDevices = [RTCCameraVideoCapturer captureDevices];
AVCaptureDevice * device = captureDevices[0];
//檢測攝像頭許可權
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)
{
NSLog(@"相機訪問受限");
//TODO:
if ([self.delegate respondsToSelector:@selector(webRTCClient:setLocalStream:)]) {
[self.delegate webRTCClient:self setLocalStream:nil];
}
} else {
if (device) {
//創建本地流
_localStream = [_factory mediaStreamWithStreamId:@"ARDAMS"];
//獲取數據源
_localVideoSource = [_factory videoSource];
//音頻
RTCAudioTrack * audioTrack = [_factory audioTrackWithTrackId:@"ARDAMSa0"];
//視頻
RTCVideoTrack *videoTrack = [_factory videoTrackWithSource:_localVideoSource trackId:@"ARDAMSv0"];
//將audioTrack、videoTrack添加到流
[_localStream addAudioTrack:audioTrack];
[_localStream addVideoTrack:videoTrack];
//拿到capture對象
RTCCameraVideoCapturer * capture = [[RTCCameraVideoCapturer alloc] initWithDelegate:_localVideoSource];
//format , fps
AVCaptureDeviceFormat * format = [[RTCCameraVideoCapturer supportedFormatsForDevice:device] lastObject];
CGFloat fps = [[format videoSupportedFrameRateRanges] firstObject].maxFrameRate;
//開始採集
_videoCapture = capture;
[capture startCaptureWithDevice:device format:format fps:fps completionHandler:^(NSError * error) {
NSLog(@"startCaptureWithDevice---:%@",error);
dispatch_async(dispatch_get_main_queue(), ^{
//展示預覽
if ([self.delegate respondsToSelector:@selector(webRTCClient:setLocalStream:)]) {
[self.delegate webRTCClient:self setLocalStream:self.localStream];
}
});
}];
}
else
{
NSLog(@"該設備不能打開攝像頭");
if ([self.delegate respondsToSelector:@selector(webRTCClient:setLocalStream:)]) {
[self.delegate webRTCClient:self setLocalStream:nil];
}
} //end device
}//end auth
}
PeerConnection對象的創建
RTCPeerConnection
是WebRTC用於構建點對點之間穩定、高效的流傳輸
的組件,是WebRTC三大核心組件之一。 使用它我們可以建立一條與遠端通話的音視頻數據傳輸通道
。
上面提到了PeerConnection工廠 RTCPeerConnectionFactory
, RTCPeerConnection
的實例就是通過此工廠來創建.
if (!ICEServers) {
ICEServers = [NSMutableArray array];
[ICEServers addObject:[self defaultSTUNServer]];
}
RTCConfiguration* configuration = [[RTCConfiguration alloc] init];
[configuration setIceServers:ICEServers];
RTCPeerConnection* conn = [factory
peerConnectionWithConfiguration:configuration
constraints:[self defaultPeerConnContraints]
delegate:self];
- (RTCMediaConstraints *)defaultPeerConnContraints
{
RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{kRTCMediaConstraintsOfferToReceiveAudio:kRTCMediaConstraintsValueTrue,kRTCMediaConstraintsOfferToReceiveVideo:kRTCMediaConstraintsValueTrue} optionalConstraints:nil];
return constraints;
}
對於 iOS 的 RTCPeerConnection
對象有三個參數:
- 第一個,是
RTCConfiguration
類型的對象,該對象中最重要的一個欄位是iceservers
。它裡邊存放了stun/turn
伺服器地址。其主要作用是用於NAT穿越
。 - 第二個參數,是
RTCMediaConstraints
類型對象,也就是對RTCPeerConnection
的限制
。如,是否接收視頻數據?是否接收音頻數據?如果要與瀏覽器互通還要開啟DtlsSrtpKeyAgreement
選項。 - 第三個參數,是
委托
類型。相當於給RTCPeerConnection
設置一個觀察者
。這樣RTCPeerConnection
可以將一個狀態/信息
通過它通知給觀察者。但它並不屬於觀察者模式,這一點大家一定要清楚。
更多內容
- PeerConnection對象添加媒體流
- PeerConnection對象的信令狀態
- PeerConnection對象獲取sdp並設置
- 獲取Candidate並添加到PeerConnection對象
- PeerConnection對象的幾種狀態
- 多點連接建立的流程
詳見: https://zhanglei.blog.csdn.net/article/details/122539459
本文來自博客園,作者:reyzhang,轉載請註明原文鏈接:https://www.cnblogs.com/reyzhang/p/16198230.html