Как установить удаленное описание для вызывающего абонента WebRTC в Chrome без ошибок?
Я надеюсь, что в логике нет недостатков.
Шаг 1: звонящий создает предложение
Шаг 2: вызывающая сторона устанавливает localDescription
Шаг 3: вызывающий абонент отправляет описание вызываемому абоненту
//------------------------------------------------------//
Шаг 4: вызываемый получает предложение устанавливает удаленное описание
Шаг 5: вызываемый создает ответ
Шаг 6: вызываемый устанавливает локальное описание
Шаг 7: вызываемый отправляет описание звонящему
//------------------------------------------------------//
Шаг 8: звонящий получает ответ и устанавливает удаленное описание
И вот код для выше
const socket = io();
const constraints = {
audio: true,
video: true
};
const configuration = {
iceServers: [{
"url": "stun:23.21.150.121"
}, {
"url": "stun:stun.l.google.com:19302"
}]
};
const selfView = $('#selfView')[0];
const remoteView = $('#remoteView')[0];
var pc = new RTCPeerConnection(configuration);
pc.onicecandidate = ({
candidate
}) => {
socket.emit('message', {
to: $('#remote').val(),
candidate: candidate
});
};
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
socket.emit('message', {
to: $('#remote').val(),
desc: pc.localDescription
});
} catch (err) {
console.error(err);
}
};
pc.ontrack = (event) => {
// don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
socket.on('message', async ({
from,
desc,
candidate
}) => {
$('#remote').val(from);
try {
if (desc) {
// if we get an offer, we need to reply with an answer
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream = await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
selfView.srcObject = stream;
await pc.setLocalDescription(await pc.createAnswer());
console.log(pc.localDescription);
socket.emit({
to: from,
desc: pc.localDescription
});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc).catch(err => console.log(err));
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(err => console.log(err));
}
} catch (err) {
console.error(err);
}
});
async function start() {
try {
// get local stream, show it in self-view and add it to be sent
const stream = await requestUserMedia(constraints);
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
attachMediaStream(selfView, stream);
} catch (err) {
console.error(err);
}
}
socket.on('id', (data) => {
$('#myid').text(data.id);
});
// this function is called once the caller hits connect after inserting the unique id of the callee
async function connect() {
try {
await pc.setLocalDescription(await pc.createOffer());
socket.emit('message', {
to: $('#remote').val(),
desc: pc.localDescription
});
} catch (err) {
console.error(err);
}
}
socket.on('error', data => {
console.log(data);
});
Теперь этот код выдает ошибку при выполнении шага 8
DOMException: не удалось выполнить 'setRemoteDescription' для 'RTCPeerConnection': не удалось установить удаленное предложение sdp: вызвано в неправильном состоянии: kHaveLocalOffer
DOMException: не удалось выполнить addIceCandidate для RTCPeerConnection: ошибка при обработке кандидата ICE
Пытался отладить, но не нашел изъянов в логике или коде. Заметил одну странную вещь, что pc
объект имеет localDescription
а также currentLocalDescription
и я думаю, что вызываемый, который создает ответ, должен иметь оба типа описания, чтобы быть answer
но вместо этого показывает localDescription
быть offer
а также currentLocalDescription
тип answer
,
Я понятия не имею, должен ли он вести себя так или нет, как я начинающий.
Заранее спасибо.
1 ответ
Ваш код правильный. Это давняя ошибка в Chrome с negotiationneeded
,
Я инструктировал его в скрипке (щелкните правой кнопкой мыши и откройте в двух смежных окнах, затем нажмите кнопку вызова в одном).
В Firefox это работает. Оферент согласовывает один раз, потому что вы добавляете две дорожки (видео / аудио) одновременно:
negotiating in stable
onmessage answer
и, на стороне ответчика, треки, которые вы добавляете вне 'stable'
К ответу добавляются состояния:
onmessage offer
adding audio track
adding video track
Но в Хроме он сломан, стреляет negotiationneeded
дважды на предложителя, один раз за трек добавлено:
negotiating in stable
negotiating in stable
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
и стрельба negotiationneeded
дважды на стороне ответчика, что даже не в 'stable'
государство:
onmessage offer
adding audio track
adding video track
negotiating in have-remote-offer
negotiating in have-remote-offer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
Эти дополнительные события приводят к хаосу взаимных ошибок состояния, наблюдаемых здесь на обоих концах.
Чтобы быть конкретным, Chrome нарушает две части спецификации здесь:
"Поставить в очередь задачу", чтобы запустить это событие. "постановка в очередь предотвращает преждевременное срабатывание согласования в общей ситуации, когда одновременно вносятся многочисленные изменения в соединение".
Если состояние сигнализации соединения не
"stable"
, отмените эти шаги [чтобы запустить событие].