WebRTC 问题视频流有时不会显示,除非我强制显示

问题描述 投票:0回答:1

我有一个网站,我尝试使用 WebRTC https://www.camspark.com/

由于某种原因,我必须没有正确实现它,它不会 100% 地显示视频流,这是某种计时问题,任何 1 毫秒的延迟都会迫使它不显示视频流。

这是我的代码,您可以在 https://www.camspark.com/static/camspark.js

加载 JavaScript
var ws;

var peerConnection, localStream;  
//STUN = (Session Traversal Utilities for NAT)  
var rtc_server = {
  iceServers: [
                {urls: "stun:stun.l.google.com:19302"},
                {urls: "stun:stun.services.mozilla.com"},
                {urls: "stun:stun.stunprotocol.org:3478"},
                {urls: "turn:numb.viagenie.ca", credential: "webrtc", username: "admin%40camspark.com"},
                {url: "stun:stun.l.google.com:19302"},
                {url: "stun:stun.services.mozilla.com"},
                {url: "stun:stun.stunprotocol.org:3478"},
                {url: "turn:numb.viagenie.ca", credential: "webrtc", username: "admin%40camspark.com"}
  ]
}

//offer SDP = [Session Description Protocol] tells other peers what you would like
var rtc_media_constraints = {
  mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
  }
};

var rtc_peer_options = {
  optional: [
              {DtlsSrtpKeyAgreement: true}, //To make Chrome and Firefox to interoperate.
  ]
}
var finishSDPVideoOfferOrAnswer = false;
var isOfferer = false;
var iceCandidates = [];
var PeerConnection = RTCPeerConnection || window.PeerConnection || window.webkitPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
var IceCandidate = RTCIceCandidate || window.mozRTCIceCandidate || window.RTCIceCandidate;
var SessionDescription = RTCSessionDescription || window.mozRTCSessionDescription || window.RTCSessionDescription;
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        
function hasSupportForVideoChat() {
   return window.RTCPeerConnection && window.RTCIceCandidate && window.RTCSessionDescription && navigator.mediaDevices && navigator.mediaDevices.getUserMedia && (RTCPeerConnection.prototype.addStream || RTCPeerConnection.prototype.addTrack) ? true : false;
}

function reloadCameraStream(isOfferer_) {
    loadMyCameraStream();
    loadStrangerCameraStream(isOfferer_);
    addStatusMsg("Attempted to Reload Camera Streams.");
}

function loadMyCameraStream() {
    if (getUserMedia) {
      getUserMedia.call(navigator, { video: {facingMode: "user", aspectRatio: 4 / 3/*height: 272, width: 322*/}, audio: { echoCancellation : true } },
        function(localMediaStream) {
          //Add my video
          $("div#videoBox video#you")[0].muted = true;
          $("div#videoBox video#you")[0].autoplay = true;
          $("div#videoBox video#you").attr('playsinline', '');
          $("div#videoBox video#you").attr('webkit-playsinline', '');
          $("div#videoBox video#you")[0].srcObject = localMediaStream;
          localStream = localMediaStream;
        },
        function(e) {
          addStatusMsg("Your Video has error : " + e);
        }
      );
    } else {
      addStatusMsg("Your browser does not support WebRTC (Camera/Voice chat).");
      return;
    }
}    

function loadStrangerCameraStream(isOfferer_) {
    if(!hasSupportForVideoChat())
      return;
    
    //Only add pending ICE Candidates when getOffer() is finished.
    finishSDPVideoOfferOrAnswer = false;
    iceCandidates = []; //clear ICE Candidates array.
    isOfferer = isOfferer_;
    
    peerConnection = new PeerConnection(rtc_server, rtc_peer_options);
    if (peerConnection.addTrack !== undefined)
      localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
    else
      peerConnection.addStream(localStream);
            
    peerConnection.onicecandidate = function(e) {
      if (!peerConnection || !e || !e.candidate)
        return;    
      
      ws.send(JSON.stringify(['ice_candidate', {"candidate": e.candidate}]));
    };

    if (peerConnection.addTrack !== undefined) {
      //newer technology
      peerConnection.ontrack = function(e) {
        //e.streams.forEach(stream => doAddStream(stream));
        //addStatusMsg("ontrack called");
        //Add stranger video
        $("div#videoBox video#stranger").attr('playsinline', '');
        $("div#videoBox video#stranger").attr('webkit-playsinline', '');
        $('div#videoBox video#stranger')[0].srcObject = e.streams[0];
        $("div#videoBox video#stranger")[0].autoplay = true;
      };
    } else {
      //older technology
      peerConnection.onaddstream = function(e) {
        //addStatusMsg("onaddstream called");
        //Add stranger video
        $("div#videoBox video#stranger").attr('playsinline', '');
        $("div#videoBox video#stranger").attr('webkit-playsinline', '');
        $('div#videoBox video#stranger')[0].srcObject = e.stream;
        $("div#videoBox video#stranger")[0].autoplay = true;
      };
    }
    
    if(isOfferer) {
      peerConnection.createOffer(
        function(offer) {
          peerConnection.setLocalDescription(offer, function () {
            //both offer and peerConnection.localDescription are the same.
            //addStatusMsg('createOffer, localDescription: ' + JSON.stringify(peerConnection.localDescription));
            //addStatusMsg('createOffer, offer: ' + JSON.stringify(offer));
            ws.send(JSON.stringify(['send_offer', {"offer": peerConnection.localDescription}]));
          },
          function(e) {
            addStatusMsg('createOffer, set description error' + e);
          });
        },
        function(e) {
          addStatusMsg("createOffer error: " + e);
        },
        rtc_media_constraints
      );
    }
}

function closeStrangerCameraStream() {
    $('div#videoBox video#stranger')[0].srcObject = null
    if(peerConnection)
      peerConnection.close();
}     
    
function iceCandidate(candidate) {
  //ICE = Interactive Connectivity Establishment
  if(!finishSDPVideoOfferOrAnswer) {
    iceCandidates.push(candidate);
    //addStatusMsg("Queued iceCandidate");
    return;
  }
  
  if(!peerConnection) {
    addStatusMsg("iceCandidate peerConnection not created error.");
    return;
  }
  
  peerConnection.addIceCandidate(new IceCandidate(candidate));
  //addStatusMsg("Added on time, Peer Ice Candidate = " + JSON.stringify(candidate));
}

function getAnswer(answer) {    
    if(!hasSupportForVideoChat())
      return;

    if(!peerConnection) {
      addStatusMsg("getAnswer peerConnection not created error.");
      return;
    }

    peerConnection.setRemoteDescription(new SessionDescription(answer), function() {
      //addStatusMsg("getAnswer SessionDescription answer is ok");
      finishSDPVideoOfferOrAnswer = true;
      while (iceCandidates.length) {
        var candidate = iceCandidates.shift();
        try {
          peerConnection.addIceCandidate(new IceCandidate(candidate));
          //addStatusMsg("Adding queued ICE Candidates");
        } catch(e) {
          addStatusMsg("Error adding queued ICE Candidates error:" + e);
        }
      }
      iceCandidates = [];
    },
    function(e) {
      addStatusMsg("getAnswer SessionDescription fail error: " + e);
    });
}

function getOffer(offer) {
    if(!hasSupportForVideoChat())
      return;
    
    if(!peerConnection) {
      addStatusMsg("getOffer peerConnection not created error.");
      return;
    }
    
    //addStatusMsg("getOffer setRemoteDescription offer: " + JSON.stringify(offer));
    peerConnection.setRemoteDescription(new SessionDescription(offer), function() {
      finishSDPVideoOfferOrAnswer = true;
      while (iceCandidates.length) {
        var candidate = iceCandidates.shift();
        try {
          peerConnection.addIceCandidate(new IceCandidate(candidate));
          //addStatusMsg("Adding queued ICE Candidates");
        } catch(e) {
          addStatusMsg("Error adding queued ICE Candidates error:" + e);
        }
      }
      iceCandidates = [];
      if(!isOfferer) {
        peerConnection.createAnswer(
          function(answer) {
            peerConnection.setLocalDescription(answer);
            //addStatusMsg("getOffer create answer sent: " + JSON.stringify(answer));
            ws.send(JSON.stringify(['send_answer', {"answer": answer}]));
          },
          function(e) {
            addStatusMsg("getOffer setRemoteDescription create answer fail: " + e);
          }
        );
      }
    });
}

对于服务器端,我使用Java服务器,其中有Java代码

                    you.videoChatOfferer = true;
                    stranger.videoChatOfferer = false;
                    JSONObject obj = new JSONObject();
                    JSONArray list = new JSONArray();
                    list.put("connected");
                    obj.put("videoChatOfferer", you.videoChatOfferer); //first guy offerer for WebRTC.
                    list.put(obj);
                    server.sendMessage(websocket, list.toString()); //connected to chat partner
                    
                    obj = new JSONObject();
                    list = new JSONArray();
                    list.put("connected");
                    obj.put("videoChatOfferer", stranger.videoChatOfferer); //second guy isn't offerer.
                    list.put(obj);
                    server.sendMessage(stranger.getWebSocket(), list.toString()); //connected to chat partner

这是您在聊天中输入的强制命令,它会强制视频流刷新,并且在 90% 的情况下都可以正常工作。

        } else if(newMessage.toLowerCase().startsWith("/forcevid")) {
            UserProfile stranger = you.stranger;
            if(stranger == null) {
                obj = new JSONObject();
                list = new JSONArray();

                list.put("gotMessage");
                obj.put("msg", "Couldn't force the video streams as stranger is glitched up");
                list.put(obj);

                sendMessage(websocket, list.toString()); //send message to you
                return;
            }
            if(you.videoChatOfferer) {
                you.videoChatOfferer = false;
                stranger.videoChatOfferer = true;
            } else if(stranger.videoChatOfferer) {
                stranger.videoChatOfferer = false;
                you.videoChatOfferer = true;
            }

            obj = new JSONObject();
            list = new JSONArray();
            list.put("reloadCameraStream");
            obj.put("videoChatOfferer", you.videoChatOfferer); //first guy offerer for WebRTC.
            list.put(obj);
            sendMessage(websocket, list.toString()); //connected to chat partner
            
            obj = new JSONObject();
            list = new JSONArray();
            list.put("reloadCameraStream");
            obj.put("videoChatOfferer", stranger.videoChatOfferer); //second guy isn't offerer.
            list.put(obj);
            sendMessage(stranger.getWebSocket(), list.toString()); //connected to chat partner
        }

我的数据包处理程序也有用于应答/提供的 ECHO 命令

public void processPackets(WebSocket websocket, String message) {
    UserProfile you = clients.get(websocket);
    if(you == null) //strange why this happens, but o well.
        return;

    JSONArray packet = new JSONArray(new JSONTokener(message));
    String opcode = (String) packet.get(0);
    opcode = opcode.toLowerCase().trim();
    //System.out.println("PACKET OpCode = " + opcode);

    JSONObject obj = new JSONObject();
    JSONArray list = new JSONArray();

    switch(opcode) {
       case "ice_candidate":
            JSONObject candidatePackage = (JSONObject) packet.get(1);
            JSONObject candidate = (JSONObject) candidatePackage.get("candidate");

            obj = new JSONObject();
            list = new JSONArray();

            list.put("iceCandidate");
            obj.put("candidate", candidate);
            list.put(obj);

            writeStranger(you, list.toString()); //send ice candidate to stranger

            break;
        case "send_answer":
            JSONObject sendAnswerPackage = (JSONObject) packet.get(1);
            JSONObject answer = (JSONObject) sendAnswerPackage.get("answer");

            obj = new JSONObject();
            list = new JSONArray();

            list.put("getAnswer");
            obj.put("answer", answer);
            list.put(obj);

            writeStranger(you, list.toString()); //send answer to stranger

            break;
        case "send_offer":
            JSONObject offerPackage = (JSONObject) packet.get(1);
            JSONObject offer = (JSONObject) offerPackage.get("offer");

            obj = new JSONObject();
            list = new JSONArray();

            list.put("getOffer");
            obj.put("offer", offer);
            list.put(obj);

            writeStranger(you, list.toString()); //send ice candidate to stranger

            break;
javascript socket.io webrtc
1个回答
0
投票

这种竞争条件的主要原因似乎是 loadMyCameraStreamloadStrangerCameraStream 之间缺少序列依赖关系。您可以尝试在 loadMyCameraStream; 中调用 loadStrangerCameraStream

 getUserMedia.call(navigator, { video: {facingMode: "user", aspectRatio: 4 / 3/*height: 272, width: 322*/}, audio: { echoCancellation : true } },
        function(localMediaStream) {
          //Add my video
          $("div#videoBox video#you")[0].muted = true;
          $("div#videoBox video#you")[0].autoplay = true;
          $("div#videoBox video#you").attr('playsinline', '');
          $("div#videoBox video#you").attr('webkit-playsinline', '');
          $("div#videoBox video#you")[0].srcObject = localMediaStream;
          localStream = localMediaStream;
          loadStrangerCameraStream(isOfferer_); // <-- from here
        },

对于与您的流程类似的简约工作示例,您可以检查以下代码;

https://github.com/kaplanke/tamed-chat-client/blob/master/test/standalone_client.html

© www.soinside.com 2019 - 2024. All rights reserved.