跳转至

RTCPeerConnection传输视频流(三)

你会学到什么

在这一步中,您将了解如何:

  • 使用WebRTC Shim(adapter.js)兼容浏览器差异。
  • 使用RTCPeerConnection API来传输视频流。
  • 控制媒体捕捉和流媒体。

此步骤的完整版本位于step-2文件夹中。

什么是RTCPeerConnection?

RTCPeerConnection是一个用于使WebRTC调用视频和音频流并交换数据的API。

本示例在同一页上设置两个RTCPeerConnection对象(称为对等)之间的连接。

没有多少实际用途,但可以很好理解RTCPeerConnection的工作原理。

添加video元素和控制按钮

在index.html中用两个视频元素和三个按钮替换单个视频元素:

<video id="localVideo" autoplay></video>
<video id="remoteVideo" autoplay></video>

<div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
</div>
一个video元素将显示来自getUserMedia()的流,另一个将显示通过RTCPeerconnection传输的到本地相同的视频流。 (在现实世界的应用程序中,一个video元素将显示本地流,而另一个video元素则显示远程流。)

添加 adapter.js shim

在main.js文件的上方添加一个adapter.js的链接:

<script src="js/lib/adapter.js"></script>

adapter.js是一个垫片,以隔离应用程序的规格变化和前缀差异。
事实上,用于WebRTC实施的标准和协议是高度稳定的,并且只有少数前缀名称。
在这一步中,我们链接到最新版本的adapter.js的本地副本 - 适用于本例子,但不适用于生产应用程序。 adapter.js GitHub repo解释了确保您的应用程序总是访问最新版本的技术。
有关完整的WebRTC互操作信息,请参阅webrtc.org/web-apis/interop。

Index.html文件代码如下所示:

<!DOCTYPE html>
<html>

<head>

    <title>Realtime communication with WebRTC</title>

    <link rel="stylesheet" href="css/main.css" />

</head>

<body>

    <h1>Realtime communication with WebRTC</h1>

    <video id="localVideo" autoplay></video>
    <video id="remoteVideo" autoplay></video>

    <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
    </div>

    <script src="js/lib/adapter.js"></script>
    <script src="js/main.js"></script>

</body>

</html>

安装RTCPeerConnection代码

将main.js替换为step-02文件夹中的版本。

调用call

打开index.html,单击“开始”按钮从网络摄像机获取视频,然后单击“call”以建立对等连接。 您应该在两个视频元素中看到相同的视频(来自网络摄像头)。 查看浏览器控制台以显示WebRTC日志记录。

工作原理:

这一步做了很多...

如果你想跳过下面的解释,那很好。
你仍然可以继续使用示例代码!

WebRTC使用RTCPeerConnection API建立连接,以在WebRTC客户端(称为对等点)之间传输视频流。

在这个例子中,两个RTCPeerConnection对象在同一个页面上:pc1和pc2。 没有太多的实际用途,但很好的展示了API的工作原理。

在WebRTC对等设备之间建立呼叫涉及三个任务:

  • 为呼叫的每一端创建一个RTCPeerConnection,并在每一端添加来自getUserMedia()的本地流。
  • 获取和共享网络信息:潜在的连接终端被称为ICE候选人
  • 获取及共享本地和远程描述:关于SDP格式的本地媒体的元数据。

想象一下Alice和Bob想使用RTCPeerConnection来建立一个视频聊天。

首先,Alice和Bob交换网络信息。 “查找候选者”这个表达是指使用ICE框架查找网络接口和端口的过程。

  • Alice使用onicecandidate处理程序创建一个RTCPeerConnection对象。 这对应于main.js中的以下代码:
    pc1 = new RTCPeerConnection(servers);
    trace('Created local peer connection object pc1');
    pc1.onicecandidate = function(e) {
        onIceCandidate(pc1, e);
    }; 
     ```   
    
    *   Alice调用getUserMedia()并添加传递给它的流:
    ```javascript
    pc1.addStream(localStream);
     ```   
    
    *   网络候选变得可用时,调用步骤1中的onicecandidate处理函数。
    *   Alice将序列化的候选数据发送给Bob。 在一个真正的应用程序中,这个过程(称为信令)通过消息传递服务进行 - 您将在后面的步骤中学习如何做到这一点。 当然,在这个步骤中,两个RTCPeerConnection对象位于同一页面上,可以直接进行通信而不需要外部消息传递。
    *   当Bob从Alice获得候选消息时,他调用addIceCandidate(),将候选添加到远程对等描述中:
    ```javascript
    function onIceCandidate(pc, event) {
        if (event.candidate) {
        getOtherPc(pc).addIceCandidate(
            new RTCIceCandidate(event.candidate)
        ).then(
            function() {
            onAddIceCandidateSuccess(pc);
            },
            function(err) {
            onAddIceCandidateError(pc, err);
            }
        );
        trace(getName(pc) + ' ICE candidate: \n' + event.candidate.candidate);
        }
    } 
    

WebRTC对等体还需要查找并交换本地和远程音频和视频媒体信息,例如分辨率和编解码器功能。 交换媒体配置信息的信令通过使用被称为SDP的会话描述协议格式来交换,被称为提议和应答的元数据块:

  • Alice运行RTCPeerConnection createOffer()方法。 返回的promise提供了一个RTCSessionDescription:Alice的本地会话描述:

    pc1.createOffer(
        offerOptions
    ).then(
        onCreateOfferSuccess,
        onCreateSessionDescriptionError
    );
    

  • 如果成功,Alice使用setLocalDescription()设置本地描述,然后通过它的信令通道将这个会话描述发送给Bob。

  • Bob使用setRemoteDescription()将Alice发送的描述设置为远程描述。
  • Bob运行RTCPeerConnection createAnswer()方法,并将其从Alice获得的远程描述传递给它,以便可以生成与她相兼容的本地会话。 createAnswer() promise传递RTCSessionDescription:Bob将其设置为本地描述并发送给Alice。
  • 当Alice获得Bob的会话描述时,她将其设置为具有setRemoteDescription()的远程描述。

    function onCreateOfferSuccess(desc) {
        pc1.setLocalDescription(desc).then(
        function() {
            onSetLocalSuccess(pc1);
        },
        onSetSessionDescriptionError
        );
        pc2.setRemoteDescription(desc).then(
        function() {
            onSetRemoteSuccess(pc2);
        },
        onSetSessionDescriptionError
        );
        // Since the 'remote' side has no media stream you need
        // to pass in the right constraints in order for it to
        // accept the incoming offer of audio and video.
        pc2.createAnswer().then(
        onCreateAnswerSuccess,
        onCreateSessionDescriptionError
        );
    }
    
    function onCreateAnswerSuccess(desc) {
        pc2.setLocalDescription(desc).then(
        function() {
            onSetLocalSuccess(pc2);
        },
        onSetSessionDescriptionError
        );
        pc1.setRemoteDescription(desc).then(
        function() {
            onSetRemoteSuccess(pc1);
        },
        onSetSessionDescriptionError
        );
    } 
    

  • Ping!

知识扩展

  • 打开 chrome://webrtc-internals , 这里提供了WebRTC统计和调试数据。 (Chrome网址的完整列表位于chrome://about)
  • 使用CSS设置页面样式:

把视频并排放置。
使按钮的宽度相同,文字更大。
确保布局在移动设备上运行。

  • 在Chrome开发工具控制台中,查看pc1和pc2的localStream
  • 从控制台查看 pc1.localDescription的SDP格式是什么样?

你学到了什么

在这一步你学会了如何:

  • 使用WebRTC Shim adapter.js shim 兼容浏览器差异。
  • 使用RTCPeerConnection API来传输视频流。
  • 捕捉和控制流媒体。
  • 在对等点之间启用WebRTC共享媒体和网络信息。

此步骤的完整版本位于step-2文件夹中。

Tips

这一步有很多东西要学习! 要更详细的内容参考RTCPeerConnection的其他资源,请查看webrtc.org/start。 本页面包含JavaScript框架的建议 - 如果您想使用WebRTC,但不想围绕API。

从adapter.js GitHub仓库中找到更多关于adapter.js shim 程序的信息。

想看看世界上最好的视频聊天应用程序是什么样子? 看看AppRTC,WebRTC项目的WebRTC调用规范应用程序:appcode。 呼叫建立时间小于500毫秒。

最佳实践

为了使您的代码具有前瞻性,请使用新的基于Promise的API,并通过使用adapter.js启用不支持它们(webrtc)的浏览器。

下一步

这一步展示了如何使用WebRTC在对等体之间传输视频 - 但是这个实验也是关于数据的!

在下一步中,将了解如何使用RTCDataChannel传输任意数据流。