问题
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