Remote video is black screen or blank in WebRTC

雨燕双飞 提交于 2021-02-08 07:58:26

问题


I have signaling server in java and websocket. It works well with local video. but Remote video is black screen or blank But it is not always a blank. If you turn off the server and turn it on again, the remote video will show up on your remote. Why does not it always come out sometimes, and sometimes it does not come out?

this is my code...

 navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;
window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition
        || window.msSpeechRecognition || window.oSpeechRecognition;

    var localVideoStream = null;
var peerConn = null,
        wsc = new WebSocket("ws://localhost:8080/signaling"),
        peerConnCfg = {
            'iceServers': [{
                'url': 'stun:stun.l.google.com:19302'
            }]
        };


var videoCallButton = document.getElementById("caller");
var       endCallButton = document.getElementById("callee");
var     localVideo = document.getElementById('localVideo');
var     remoteVideo = document.getElementById('remoteVideo');
videoCallButton.addEventListener("click", initiateCall);

endCallButton.addEventListener("click", function (evt) {
            wsc.send(JSON.stringify({"closeConnection": true }));
        });
var sdpConstraints = {
    'mandatory': {
        'OfferToReceiveAudio': true,
        'OfferToReceiveVideo': true
    }
};
function prepareCall() {
    peerConn = new RTCPeerConnection(peerConnCfg);
    // send any ice candidates to the other peer
    peerConn.onicecandidate = onIceCandidateHandler;
    // once remote stream arrives, show it in the remote video element
    peerConn.onaddstream = onAddStreamHandler;
};

// run start(true) to initiate a call
function initiateCall() {
    prepareCall();
    // get the local stream, show it in the local video element and send it
    navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
        localVideoStream = stream;
        localVideo.src = URL.createObjectURL(localVideoStream);
        peerConn.addStream(localVideoStream);
        createAndSendOffer();

    }, function(error) { console.log(error);});
};

function answerCall() {
    prepareCall();
    // get the local stream, show it in the local video element and send it
    navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
        localVideoStream = stream;
        localVideo.src = URL.createObjectURL(localVideoStream);
        peerConn.addStream(localVideoStream);
        createAndSendAnswer();

    }, function(error) { console.log(error);});
};

wsc.onmessage = function (evt) {
    var signal = null;
    if (!peerConn) answerCall();
    signal = JSON.parse(evt.data);

    if (signal.sdp) {
        console.log("Received SDP from remote peer.");
        console.log("signal"+ signal);
        peerConn.setRemoteDescription(new RTCSessionDescription(signal.sdp));
    }
    else if (signal.candidate) {
        console.log("signal"+ signal.candidate);
        console.log("Received ICECandidate from remote peer.");
        peerConn.addIceCandidate(new RTCIceCandidate(signal.candidate));
    } else if ( signal.closeConnection){
        console.log("Received 'close call' signal from remote peer.");
        endCall();
    }else{
        console.log("signal"+ signal.candidate);
    }
};

function createAndSendOffer() {
    peerConn.createOffer(
            function (offer) {
                var off = new RTCSessionDescription(offer);
                peerConn.setLocalDescription(new RTCSessionDescription(off),
                        function() {
                            wsc.send(JSON.stringify({"sdp": off }));
                        },
                        function(error) { console.log(error);}
                );
            },
            function (error) { console.log(error);}
    );
};

function createAndSendAnswer() {
    peerConn.createAnswer(
            function (answer) {
                var ans = new RTCSessionDescription(answer);
                peerConn.setLocalDescription(ans, function() {
                            wsc.send(JSON.stringify({"sdp": ans }));
                        },
                        function (error) { console.log(error);}
                );
            },
            function (error) {console.log(error);}
    );
};

function onIceCandidateHandler(evt) {
    if (!evt || !evt.candidate) return;
    wsc.send(JSON.stringify({"candidate": evt.candidate }));
};

function onAddStreamHandler(evt) {
    videoCallButton.setAttribute("disabled", true);
    endCallButton.removeAttribute("disabled");
    // set remote video stream as source for remote video HTML5 element

    remoteVideo.src = window.URL.createObjectURL(evt.stream);
    remoteVideo.play();
    console.log("remote src : "+ remoteVideo.src);
};

function endCall() {
    peerConn.close();
    peerConn = null;
    videoCallButton.removeAttribute("disabled");
    endCallButton.setAttribute("disabled", true);
    if (localVideoStream) {
        localVideoStream.getTracks().forEach(function (track) {
            track.stop();
        });
        localVideo.src = "";
    }
    if (remoteVideo){
        remoteVideo.src = "";
        window.URL.revokeObjectURL(remoteVideo);
    }
};

回答1:


add oniceconnectionstatechange to your prepeareCall Function and see if there is any ICE failure because of NAT issues

function prepareCall() {
    peerConn = new RTCPeerConnection(peerConnCfg);
    // send any ice candidates to the other peer
    peerConn.onicecandidate = onIceCandidateHandler;
    // once remote stream arrives, show it in the remote video element
    peerConn.onaddstream = onAddStreamHandler;
    peerConn.oniceconnectionstatechange = function(){
       console.log('ICE state: ',peerConn.iceConnectionState);
    }
};



回答2:


One of the reasons for WebRTC blank / empty video is having high packet loss. In that scenario, in server and client logs it will show as the connection is successful and video is playing normally, so you won't see any warning or error.

To check if there is a high packet loss, you can go to about:webrtc on firefox, or chrome://webrtc-internals on chrome. For firefox, you can navigate to "RTP Stats". You'll see it shows Received: ... packets and Lost: ... packets. You can calculate packet loss ratio using these counts. For chrome, there is a graph for packet loss ratio. You might have a very high packet loss such as 70% for example.

If you have this extreme high packet loss, one reason is having a smaller MTU https://en.wikipedia.org/wiki/Maximum_transmission_unit on the client network interface than the MTU used by the server. For example, your client network interface can have MTU=1500 bytes when not connected to VPN, and MTU=1250 bytes when connected to VPN. If the server is sending RTP packets (over UDP) with MTU=1400, it can be received by the client if the client is not using VPN, but the packets larger than 1250 bytes will get dropped by the client network interface.

If you want to check the client MTU locally, you can run ifconfig on mac or linux.

Mac example, without example vpn:

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ...
    inet SOME_IP netmask 0xffffff00 broadcast 192.168.1.255
    media: autoselect
    status: active

Mac example, with example vpn:

utun2: flags=80d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1250
    inet SOME_IP --> SOME_IP netmask 0xffffffff
    nd6 options=201<PERFORMNUD,DAD>

How to configure MTU for server:

If you are using GStreamer for WebRTC server, the payload generator element, for example rtpvp8pay has a property to set desired MTU value. By using gst-inspect-1.0 rtpvp8pay you can see that it uses 1400 as MTU by default which can be larger than your client network interface can handle (e.g. 1250 on above example). You can make it work by setting lower MTU on your GStreamer pipeline so that your client network interface won't drop majority of the packages anymore (package loss ratio can reduce to 0.01% just by changing MTU on GStreamer pipeline on the server).

When this is the case, the incoming video can work for ~10 seconds when VPN is freshly reconnected, then the incoming video can freeze and subsequent page refreshes can lead to just blank video with 70%+ packet loss.

This is a very specific scenario but when it happens it is a complete silent/hidden error so hopefully this helps someone.




回答3:


in func did recieve remote video track perform a timer then in timer selector add track to view



来源:https://stackoverflow.com/questions/42720530/remote-video-is-black-screen-or-blank-in-webrtc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!