I have a problem connecting peer-to-peer video from native android browser to safari 11 desktop (vice versa), here is the error:
Unhandled Promise Rejection: OperationError (DOM Exception 34): Failed to set remote offer sdp: Session error code: ERROR_CONTENT. Session error description: Failed to set remote video description send parameters..
I'm currently stuck in this issue
and here is my whole client videochat code, thanks.
import app from '../../../config';
const videoChatService = app.service('participants/video-chat');
let localVid;
let remoteVid;
let localstream;
let rtcPeerConn;
let conversationId;
let userId;
let isSdpSent = false;
let hasAddTrack;
const configuration = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302',
},
{
urls: 'stun:stun.services.mozilla.com',
username: 'louis@mozilla.com',
credential: 'webrtcdemo',
},
// {
// urls: 'turn:mdn-samples.mozilla.org',
// username: 'webrtc',
// credential: 'turnserver' }
] };
function closeVideoCall() {
if (rtcPeerConn) {
rtcPeerConn.onaddstream = null;
rtcPeerConn.ontrack = null;
rtcPeerConn.onremovestream = null;
rtcPeerConn.onicecandidate = null;
if (remoteVid.srcObject) {
remoteVid.srcObject.getTracks().forEach(track => track.stop());
remoteVid.srcObject = null;
}
if (localVid.srcObject) {
localVid.srcObject.getTracks().forEach(track => track.stop());
localVid.srcObject = null;
}
rtcPeerConn.close();
rtcPeerConn = null;
}
}
// set local sdp
function setLocalSDP(desc) {
console.log('>>> setLocalSDP', rtcPeerConn);
return rtcPeerConn.setLocalDescription(desc);
}
function logError(error) {
console.log(`>>>>logError, ${error.name}: ${error.message}`);
}
function handleNegotiatedNeededEvent() {
console.log('>>>>> on negotiation called');
console.log('query >>>', conversationId, userId);
if (!isSdpSent) {
rtcPeerConn.createOffer()
.then(setLocalSDP)
.then(() => {
isSdpSent = true;
videoChatService.patch(null, {
data: {
type: 'video-offer',
message: JSON.stringify(rtcPeerConn.localDescription),
},
}, {
query: {
conversation_id: conversationId,
user_id: userId,
},
}).then().catch(e => { console.log('patch error', e); });
})
.catch(logError);
}
}
function handleRemoveStreamEvent() {
closeVideoCall();
}
function handleTrackEvent (evt) {
console.log('>>>>> going to add their stream...', evt);
remoteVid = document.getElementById('remoteStream');
if (!remoteVid.srcObject) {
remoteVid.srcObject = evt.streams[0];
}
}
function handleAddStreamEvent(evt) {
console.log('>>>>> stream added');
remoteVid = document.getElementById('remoteStream');
remoteVid.srcObject = event.stream;
}
function handleICECandidateEvent(evt) {
console.log('>>>> onicecandidate', evt);
console.log('query >>>', conversationId, userId);
if (evt.candidate) {
videoChatService.patch(null, {
data: {
type: 'new-ice-candidate',
message: JSON.stringify(evt.candidate),
},
}, {
query: {
conversation_id: conversationId,
user_id: userId,
},
});
}
}
function handleICEConnectionStateChangeEvent() {
console.log(`>>>>> ICE connection state changed to ${rtcPeerConn.iceConnectionState}`);
switch (rtcPeerConn.iceConnectionState) {
case 'closed':
case 'failed':
case 'disconnected':
console.log('>>>> disconnected');
closeVideoCall();
break;
}
}
function handleSignalingStateChangeEvent() {
console.log(`>>>>> WebRTC signaling state changed to: ${rtcPeerConn.signalingState}`);
switch (rtcPeerConn.signalingState) {
case 'closed':
console.log('>>>> closed');
closeVideoCall();
break;
}
}
function createPeerConnection() {
rtcPeerConn = new RTCPeerConnection(configuration);
console.log('>>>>> create peer connection', rtcPeerConn);
hasAddTrack = (rtcPeerConn.addTrack !== undefined);
rtcPeerConn.onicecandidate = handleICECandidateEvent;
rtcPeerConn.onnegotiationneeded = handleNegotiatedNeededEvent;
rtcPeerConn.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
rtcPeerConn.onsignalingstatechange = handleSignalingStateChangeEvent;
rtcPeerConn.onremovestream = handleRemoveStreamEvent;
if (hasAddTrack) {
rtcPeerConn.ontrack = handleTrackEvent;
} else {
rtcPeerConn.onaddstream = handleAddStreamEvent;
}
}
function handleGetUSerMediaError(e) {
switch (e.name) {
case 'NotFoundError':
alert('Unable to open your call because no camera and/or microphone were found.');
break;
case 'SecurityError':
case 'PermissionDeniedError':
// Do nothing; this is the same as the user canceling the call.
break;
default:
alert(`Error opening your camera and/or microphone: ${e.message}`);
break;
}
}
// add video to local and add to track
function gotStream(stream) {
console.log('>>>> gotStream', stream);
localVid.srcObject = stream;
localstream = stream;
if (hasAddTrack) {
stream.getTracks().forEach(track => rtcPeerConn.addTrack(track, localstream));
} else {
rtcPeerConn.addStream(localstream);
}
}
// start signaling
export function startSignaling(conversation_id, user_id) {
localVid = document.getElementById('localStream');
remoteVid = document.getElementById('remoteStream');
console.log('>>>>> startSignaling');
conversationId = conversation_id;
userId = user_id;
return () => {
if (!rtcPeerConn) {
createPeerConnection();
navigator.mediaDevices.getUserMedia({
audio: true,
video: {
facingMode: 'user',
},
})
.then(gotStream)
.catch(handleGetUSerMediaError);
}
};
}
export function handleVideoOfferMsg(conversation_id, user_id, message) {
console.log('>>>>> handleVideoOfferMsg');
localstream = null;
conversationId = conversation_id;
userId = user_id;
localVid = document.getElementById('localStream');
remoteVid = document.getElementById('remoteStream');
return () => {
createPeerConnection();
console.log('query >>>', conversationId, userId);
const sdp = new RTCSessionDescription(message);
// sdp.sdp = sdp.replace('a=setup:active', 'a=setup:passive');
rtcPeerConn.setRemoteDescription(sdp)
.then(() => (
navigator.mediaDevices.getUserMedia({
audio: true,
video: {
facingMode: 'user',
},
})
))
.then(gotStream)
.then(() => (
rtcPeerConn.createAnswer()
))
.then(setLocalSDP)
.then(() => {
videoChatService.patch(null, {
data: {
type: 'video-answer',
message: JSON.stringify(rtcPeerConn.localDescription),
},
}, {
query: {
conversation_id: conversationId,
user_id: userId,
},
}).then().catch(e => { console.log('patch error', e); });
});
};
}
export function handleVideoAnswerMsg(message) {
console.log('>>>>> handle video answer message', message);
return () => {
const sdp = new RTCSessionDescription(message);
rtcPeerConn.setRemoteDescription(sdp)
.catch(logError);
};
}
// Adding ice candidate
export function addIceCandidate(message) {
console.log('>>>> addIceCandidate', message);
return () => {
const candidate = new RTCIceCandidate(message);
rtcPeerConn.addIceCandidate(candidate)
.then(() => {
console.log('>>> candidate added ');
})
.catch(e => {
console.log('Error candidate', e);
});
};
}