WebRTC 远程视频流不工作

3

我添加了一个简单的WebRTC应用程序,它将连接一个浏览器窗口到自身,从我的摄像机流式传输视频数据。最终目标是在页面上获得两个视频流,一个直接来自相机,另一个来自浏览器本地建立的WebRTC连接。

不幸的是,远程视频流没有显示出来。有任何想法为什么吗?

<video id="yours" autoplay></video>
<video id="theirs" autoplay></video>

这里是javascript代码

function hasUserMedia() {
     navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;

     return !!navigator.getUserMedia;
    }

    function hasRTCPeerConnection() {
     window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection 
        || window.mozRTCPeerConnection;

     return !!window.RTCPeerConnection;
    }

    var yourVideo = document.querySelector('#yours'),
     theirVideo = document.querySelector('#theirs'),
     yourConnection, theirConnection;


    if (hasUserMedia()) {
        navigator.getUserMedia({ video: true, audio: false }, function(stream) {
         yourVideo.src = window.URL.createObjectURL(stream);
         if (hasRTCPeerConnection()) {
            startPeerConnection(stream);
         } else {
         alert("Sorry, your browser does not support WebRTC.");
         }
         }, function (error) {
         console.log(error);
         });
        }else{
            alert("Sorry, your browser does not support WebRTC.");
        }


    function startPeerConnection(stream){
        var configuration = {
            "iceServers": [{ "url": "stun:stun.1.google.com:19302"
            }]
        };

        yourConnection = new RTCPeerConnection(configuration);
        theirConnection = new RTCPeerConnection(configuration);



         // Setup stream listening
         yourConnection.addStream(stream);

         theirConnection.onaddstream = function (event) {
            theirVideo.src = window.URL.createObjectURL(event.stream);
            console.log('stream added');
         };

         // console.log(yourConnection);
          //console.log(theirConnection);

         // Setup ice handling
         yourConnection.onicecandidate = function (event) {
         if (event.candidate) {
                 theirConnection.addIceCandidate(new RTCIceCandidate(event.
                candidate));
             }
         };
         theirConnection.onicecandidate = function (event) {
             if (event.candidate) {
                 yourConnection.addIceCandidate(new RTCIceCandidate(event.
                candidate));
             }
         };

          // Begin the offer
         yourConnection.createOffer(function (offer) {
            yourConnection.setLocalDescription(offer);
            theirConnection.setRemoteDescription(offer);

            theirConnection.createAnswer(function (offer) {
                theirConnection.setLocalDescription(offer);
                yourConnection.setRemoteDescription(offer);
            });
         });
    };

我正在跟随Dan Ristic的WebRTC书籍,并理解他所编写的代码。不幸的是,远程视频没有显示出来。

1个回答

8
添加“失败回调”以使其正常工作。否则不仅看不到错误,而且这样做实际上会使其工作,因为有一个非常奇怪的原因:
你成了WebIDL重载的受害者。发生的情况是WebRTC API有两个版本,并且你混合使用它们。
有一个现代的承诺API,例如: (这里是链接)
pc.createOffer(options).then(successCallback, failureCallback);

还有一个不建议使用的回调函数版本,例如:

pc.createOffer(successCallback, failureCallback, options);

换句话说,有两个不同参数数量的 createOffer 函数。
不幸的是,你只传递了一个参数,所以你正在调用第一个 createOffer !第一个 createOffer 需要一个选项对象,但在 WebIDL 中,这个对象和一个函数无法区分。因此,它被视为有效的参数(一个空的选项对象)。即使这导致了一个 TypeError,它也不会引发异常,因为 promise API 拒绝返回的承诺而不是抛出异常:
pc.createOffer(3).catch(e => console.log("Here: "+ e.name)); // Here: TypeError

你没有检查返回的Promise,因此错误被忽略了。

这是一个可行的版本(Chrome fiddle):

navigator.getUserMedia = navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia;
window.RTCPeerConnection = window.RTCPeerConnection ||
                           window.webkitRTCPeerConnection;

var yourConnection, theirConnection;

navigator.getUserMedia({ video: true, audio: false }, function(stream) {
    yourVideo.src = window.URL.createObjectURL(stream);

    var config = { "iceServers": [{ "urls": "stun:stun.1.google.com:19302"}] };
    yourConnection = new RTCPeerConnection(config);
    theirConnection = new RTCPeerConnection(config);

    yourConnection.addStream(stream);

    theirConnection.onaddstream = function (event) {
        theirVideo.src = window.URL.createObjectURL(event.stream);
    };

    yourConnection.onicecandidate = function (e) {
        if (e.candidate) {
            theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate),
                                            success, failure);
         }
     };
     theirConnection.onicecandidate = function (e) {
         if (e.candidate) {
             yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate),
                                            success, failure);
         }
     };

     yourConnection.createOffer(function (offer) {
         yourConnection.setLocalDescription(offer, success, failure);
         theirConnection.setRemoteDescription(offer, success, failure);
         theirConnection.createAnswer(function (offer) {
             theirConnection.setLocalDescription(offer, success, failure);
             yourConnection.setRemoteDescription(offer, success, failure);
         }, failure);
     }, failure);
}, failure);

function success() {};
function failure(e) { console.log(e); };
<video id="yourVideo" width="160" height="120" autoplay></video>
<video id="theirVideo" width="160" height="120" autoplay></video>

但是回调函数很繁琐。我强烈推荐使用新的 Promise API 代替 (https 在 Chrome 中可用):

var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();

navigator.mediaDevices.getUserMedia({video: true, audio: true})
  .then(stream => pc1.addStream(video1.srcObject = stream))
  .catch(log);

var add = (pc, can) => pc.addIceCandidate(can).catch(log);
pc1.onicecandidate = e => add(pc2, e.candidate);
pc2.onicecandidate = e => add(pc1, e.candidate);

pc2.ontrack = e => video2.srcObject = e.streams[0];
pc1.oniceconnectionstatechange = e => log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
  pc1.createOffer().then(d => pc1.setLocalDescription(d))
  .then(() => pc2.setRemoteDescription(pc1.localDescription))
  .then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
  .then(() => pc1.setRemoteDescription(pc2.localDescription))
  .catch(log);

var log = msg => console.log(msg);
<video id="video1" height="120" width="160" autoplay muted></video>
<video id="video2" height="120" width="160" autoplay></video><br>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>


2
哇哦。这个回答让我的一天都变得美好了。我是WebRTC的新手,将来会经常来打扰你的! - marukobotto
我不明白。我理解了很多,但仍然不明白。这个例子应该与同行建立连接。所以在第二个“视频”中,我应该看到别人,而不是我,对吗? - Damian Hetman
@DamianHetman 你期望看到谁? :) 这只是一个本地环回演示。要进行真正的通话,您首先需要知道要呼叫谁,并使用一些信令通道交换提供/应答以建立呼叫。 - jib
@jib 那么如何与其他通话者通话呢?我在网上搜索了一些示例,几乎每个示例都是本地循环。我找到了一个可以呼叫其他人的示例,但是代码太旧无法工作。我正在尝试进行一对一视频通话,但不知道自己做错了什么... - Damian Hetman
@DamianHetman 这是因为做更多的事情需要一个(websocket)服务器进行信令传递。请参阅我的其他答案,包括cross-tab democut'n'paste signalinglocal websocket server。第二个答案可以用来连接到任何地方的任何机器(只要你手动获取并交换offer和answer)。 - jib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接