评论

收藏

[iOS开发] iOS 集成WebRTC相关知识点总结

移动开发 移动开发 发布于:2022-05-07 15:43 | 阅读数:476 | 评论:0

前言
本文主要是整理了使用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


   
   
   
        

关注下面的标签,发现更多相似文章