WebRTC ON iOS

WebRTC 在 iOS中的使用

原文地址

WebRTC是一个面向iOS和安卓的高层级的API的开源项目.

在这篇文章我们将研究如何开始建立WebRTC到你的iOS程序. 我们建议使用本地的WebRTC库,但你可以看看OpenWebRTC项目. 我们不会讲述如何建立使用signaling ,而是突出在iOS浏览器实现的异同. 正如你将看到的,这个API是在iOS和web上是平行的.如果你正在寻找一个更基本的WebRTC概论,我们推荐Sam Dutton’s Getting started with WebRTC. 本文中的代码并不完全,我们建议可以看看WebRTC团队的AppRTCDemo实现.

开始

你可以自行下载源码进行十数小时的编译, 也可以直接pod libjingle_peerconnection到你的项目.

理解API

建议你花些时间熟悉他们的头文件,下面的代码基于WebRTC 9103版本.

1. 为RTCPeerConnection做准备

创建ICE servers 组

如果你想在外网使用,则需要一个STUN 或 TURNserver把两方连接起来. 你也可以忽略,当你在内网进行测试的时候.

RTCIceServer *iceServer = [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:YOUR_SERVER]
    username:USERNAME_OR_EMPTY_STRING
    password:PASSWORD_OR_EMPTY_STRING]];

2. 回调和代理

iOS的库使用代理模式进行JavaScript API的回调. 它提供了几个代理为你实现一个完整的WebRTC程序. 如果仅仅是演示和运行 ,则需要 RTCPeerConnectionDelegateRTCSessionDescriptionDelegate

RTCPeerConnectionDelegate: 是web的 RTCPeerConnection.onNN 回调在ObjC中的实现:它们中许多方法你并不是必须的,RTCSessionDescriptionDelegate我们列举必要的两条出来:

- (void)peerConnection:(RTCPeerConnection *)peerConnection
    didCreateSessionDescription:(RTCSessionDescription *)sdp
                          error:(NSError *)error;

- (void)peerConnection:(RTCPeerConnection *)peerConnection
    didSetSessionDescriptionWithError:(NSError *)error;


即对应JavaScript中的
peerConnection.createOffer(function didCreateSessionDescription(sdp) {
    peerConnection.setLocalDescription(sdp, function didSetSessionDescription() {

    });
});
// 简单表示就是创建offer 后, 第一个方法进行localDescription设置. 第二个 发送sdp.

3. 创建RTCPeerConnection

你们都知道,RTCPeerConnection是webRTC中用来控制媒体会话的.iOS也尽力模仿让他来管理你的会话.
使用RTCPeerConnectionFactory:来创建RTCPeerConnection

// Enable SSL globally for WebRTC in our app
[RTCPeerConnectionFactory initializeSSL];
RTCPeerConnectionFactory *pcFactory = [[RTCPeerConnectionFactory alloc] init];

// Create the peer connection using the ICE server list and the current class as the delegate
RTCPeerConnection *peerConnection = [pcFactory peerConnectionWithICEServers:iceServers
                                                constraints:nil delegate:self];

你可以看到, 创建RTCPeerConnection非常类似web中的方式,所以,你也应该禁用SSL,当你完成了你的WebRTC电话,或者在应用程序终止时:

[RTCPeerConnectionFactory deinitializeSSL];

4. 摄像头和麦克的许可

在这个示例中,我们要用RTCMediaStream包装音频和视频,不像在web中,我们有getUserMedia ,我们现在需要自己创建stream对象. 这并不是很困难.

请注意,下面的代码假定用户授予访问摄像头和麦克风,在实际中这可能并非如此。

Media stream

RTCMediaStream包括了视频和音频的track. 我们可以添加每个不同的type,也可以不添加,直接创建stream然后添加一个one audio track和one video track.

RTCMediaStream *localStream = [factory mediaStreamWithLabel:@”someUniqueStreamLabel”];
Audio

获取audio track是容易的,只有一个麦克风来源,会自动获取,当你使用下面代码时:

RTCAudioTrack *audioTrack = [factory audioTrackWithID:@”audio0”];
[localStream addAudioTrack:audioTrack];
Video

Video track需要知道我们想使用的AVCaptureDevice,在大多数iOS设备上你可以选择前后摄像头.下面让我们使用前置摄像头.

记住,你没有访问摄像头在模拟器上,所以下面的代码不会找到一个AVCaptureDevice除非你在设备上运行它。

// Find the device that is the front facing camera
AVCaptureDevice *device;
for (AVCaptureDevice *captureDevice in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] ) {
    if (captureDevice.position == AVCaptureDevicePositionFront) {
        device = captureDevice;
        break;
    }
}

// Create a video track and add it to the media stream
if (device) {
    RTCVideoSource *videoSource;
    RTCVideoCapturer *capturer = [RTCVideoCapturer capturerWithDeviceName:device.localizedName];
    videoSource = [factory videoSourceWithCapturer:capturer constraints:nil];
    RTCVideoTrack *videoTrack = [factory videoTrackWithID:videoId source:videoSource];
    [localStream addVideoTrack:videoTrack];
}

最后 ,我们有了一个RTCMediaStream 的视频和音频轨道,现在是则需要添加到peer connection.

[peerConnection addStream:localStream];

你可以发送或接受一个Offer,这是使用signaling机制的逻辑. 现在假设你已经向对方成功发送了一个offer.

5. Offer/Answer

创建Offer
RTCMediaConstraints *constraints = [RTCMediaConstraints alloc] initWithMandatoryConstraints:
    @[
        [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
        [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
    ]
    optionalConstraints: nil];

[peerConnection createOfferWithConstraints:constraints];

创建offer之后,就会用代理实现回调机制 ,让我们进行 localDescription设置:

- (void)peerConnection:(RTCPeerConnection *)peerConnection
    didCreateSessionDescription:(RTCSessionDescription *)sdp error:(NSError *)error
{
    [peerConnection setLocalDescription:sdp]
}

同样,之后是让我们进行发送offer(sdp)的回调.

- (void)peerConnection:(RTCPeerConnection *)peerConnection
    didSetSessionDescriptionWithError:(NSError *)error
{
    if (peerConnection.signalingState == RTCSignalingHaveLocalOffer) {
        // Send offer through the signaling channel of our application

    }
}

之后,你应当从对方获得一个answer.来进行RemoteDescription设置.

RTCSessionDescription *remoteDesc = [[RTCSessionDescription alloc] initWithType:@"answer" sdp:sdp];
[peerConnection setRemoteDescription:remoteDesc];

但是目前只有处理设置本地描述。让我们扩展它来处理设置远程描述:

- (void)peerConnection:(RTCPeerConnection *)peerConnection
    didSetSessionDescriptionWithError:(NSError *)error
{
  // If we have a local offer OR answer we should signal it
  if (peerConnection.signalingState == RTCSignalingHaveLocalOffer | RTCSignalingHaveLocalAnswer ) {
    // Send offer/answer through the signaling channel of our application
  } else if (peerConnection.signalingState == RTCSignalingHaveRemoteOffer) {
    // If we have a remote offer we should add it to the peer connection
    [peerConnection createAnswerWithConstraints:constraints];
  }
}

6. RTCIceCandidate

一旦你调用了setLocalDescription,ICE engine 就会开始穿透,通过RTCPeerConnectionDelegategotICECandidate,这些都是本地的iceCandidates,必须发送到其他远端.

- (void)peerConnection:(RTCPeerConnection *)peerConnection
     gotICECandidate:(RTCICECandidate *)candidate
{
    // Send candidate through the signaling channel
}

当你收到一个ICE candidate时,你可以简便的直接连接远端.

7. 接收video stream

如果一切正确执行或是你网络玩的还凑合,那么你现在应该收到addedSteam 代理方法, 在其中处理显示接收到的steam即可.

- (void)peerConnection:(RTCPeerConnection *)peerConnection addedStream:(RTCMediaStream *)stream
{
    // Create a new render view with a size of your choice
    RTCEAGLVideoView *renderView = [[RTCEAGLVideoView alloc] initWithFrame:CGRectMake(100, 100)];
    [stream.videoTracks.lastObject addRenderer:self.renderView];

    // RTCEAGLVideoView is a subclass of UIView, so renderView
    // can be inserted into your view hierarchy where it suits your application.
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值