WebRTC onicecandidate 不会在 chrome for android 上触发,但适用于所有其他浏览器,包括适用于 android 的 firefox
Posted
技术标签:
【中文标题】WebRTC onicecandidate 不会在 chrome for android 上触发,但适用于所有其他浏览器,包括适用于 android 的 firefox【英文标题】:WebRTC onicecandidate is not triggered on chrome for android but works on all other browsers including firefox for android 【发布时间】:2021-12-06 11:30:55 【问题描述】:这里是代码。
sendChannel = localConnection.createDataChannel("sendChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
localConnection.ondatachannel = receiveChannelCallback;
localConnection.onicecandidate = e =>
console.log('candidates found');
e.candidate && offerCandidates.add(e.candidate.toJSON());
;
var offerDescription = await localConnection.createOffer();
await localConnection.setLocalDescription(offerDescription);
我已确认它适用于所有桌面浏览器和 android 上的 firefox,但从未在 chrome for android 或本机 webview 上调用 onicecandidate。
我还更新了 chrome、webview 和 android 本身,问题仍然存在。
编辑:我在另一部运行 chrome 版本 84.0.4147.89 的手机上进行了尝试,它运行良好。有问题的版本是 94.0.4606.85。
我将 chrome 降级到版本 87.0.4280.141,现在它可以工作了,但遗憾的是,降级 webview 并没有帮助,这是最终用例。
我的理论是这是新版本的错误或安全问题。 无论如何,这里是完整的代码,只是为了确保。
import './firebase/firebase.js';
const firebaseConfig =
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
;
if (!firebase.apps.length)
firebase.initializeApp(firebaseConfig);
var connectButton = null;
var disconnectButton = null;
var sendButton = null;
var messageInputBox = null;
var receiveBox = null;
const servers =
iceServers: [
urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'],
,
],
iceCandidatePoolSize: 10,
;
const localConnection = new RTCPeerConnection(servers);
var calldoc;
var offerCandidates;
var answerCandidates;
var sendChannel = null; // RTCDataChannel for the local (sender)
var receiveChannel = null; // RTCDataChannel for the remote (receiver)
var answerInput = null;
var answerButton = null;
var connected = false;
var id = null;
var dataConstraint;
function startup()
connectButton = document.getElementById('connectButton');
disconnectButton = document.getElementById('disconnectButton');
sendButton = document.getElementById('sendButton');
messageInputBox = document.getElementById('message');
receiveBox = document.getElementById('receivebox');
answerInput = document.getElementById('answerID');
answerButton = document.getElementById('answerButton');
// Set event listeners for user interface widgets
answerButton.addEventListener('click', listenForConnection, false);
connectButton.addEventListener('click', connectPeers, false);
disconnectButton.addEventListener('click', disconnectPeers, false);
sendButton.addEventListener('click', sendMessage, false);
function onicecandidate (e)
console.log('candidates found');
e.candidate && offerCandidates.add(e.candidate.toJSON());
;
export async function connectPeers()
// Create the local connection and its event listeners
calldoc = firebase.firestore().collection('calls').doc();
// Create the data channel and establish its event listeners
dataConstraint = null;
sendChannel = localConnection.createDataChannel("sendChannel", dataConstraint);
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
localConnection.ondatachannel = receiveChannelCallback;
localConnection.onicecandidate = onicecandidate;
id = calldoc.id;
offerCandidates = calldoc.collection('offerCandidates');
answerCandidates = calldoc.collection('answerCandidates');
var offerDescription = await localConnection.createOffer();
await localConnection.setLocalDescription(offerDescription);
const offer =
sdp: offerDescription.sdp,
type: offerDescription.type,
;
await calldoc.set(offer);
calldoc.onSnapshot((snapshot) =>
const data = snapshot.data();
if (data !== null)
if (!localConnection.currentRemoteDescription && data.answer)
const answerDescription = new RTCSessionDescription(data.answer);
localConnection.setRemoteDescription(answerDescription);
);
answerCandidates.onSnapshot(snapshot =>
snapshot.docChanges().forEach((change) =>
if (change.type === 'added')
const candidate = new RTCIceCandidate(change.doc.data());
localConnection.addIceCandidate(candidate);
console.log("found answer");
connected = true;
);
);
async function listenForConnection()
calldoc = firebase.firestore().collection('calls').doc(answerInput.value);
answerCandidates = calldoc.collection('answerCandidates');
localConnection.onicecandidate = event =>
event.candidate && answerCandidates.add(event.candidate.toJSON());
;
// Create the data channel and establish its event listeners
sendChannel = localConnection.createDataChannel("receiveChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
localConnection.ondatachannel = receiveChannelCallback;
const cdata = (await calldoc.get()).data();
const offerDescription = cdata.offer;
await localConnection.setRemoteDescription(new
RTCSessionDescription(offerDescription));
const answerDescription = await localConnection.createAnswer();
await localConnection.setLocalDescription(answerDescription);
const answer =
type: answerDescription.type,
sdp: answerDescription.sdp,
;
await calldoc.update( answer );
offerCandidates.onSnapshot((snapshot) =>
snapshot.docChanges().forEach((change) =>
console.log(change)
if (change.type === 'added')
let data = change.doc.data();
localConnection.addIceCandidate(new RTCIceCandidate(data));
);
);
// Handle errors attempting to create a description;
function handleCreateDescriptionError(error)
console.log("Unable to create an offer: " + error.toString());
// Handle successful addition of the ICE candidate
// on the "local" end of the connection.
function handleLocalAddCandidateSuccess()
connectButton.disabled = true;
// Handle successful addition of the ICE candidate
// on the "remote" end of the connection.
function handleRemoteAddCandidateSuccess()
disconnectButton.disabled = false;
// Handle an error that occurs during addition of ICE candidate.
function handleAddCandidateError()
console.log("Oh noes! addICECandidate failed!");
// Handles clicks on the "Send" button by transmitting
export function sendMessage()
if (connected === false)
return
var message = messageInputBox.value;
sendChannel.send(message);
messageInputBox.value = "";
messageInputBox.focus();
// Handle status changes on the local end of the data
function handleSendChannelStatusChange(event)
console.log('on open fired???');
if (sendChannel)
var state = sendChannel.readyState;
if (state === "open")
messageInputBox.disabled = false;
messageInputBox.focus();
sendButton.disabled = false;
disconnectButton.disabled = false;
connectButton.disabled = true;
else
messageInputBox.disabled = true;
sendButton.disabled = true;
connectButton.disabled = false;
disconnectButton.disabled = true;
// Called when the connection opens and the data
// channel is ready to be connected to the remote.
function receiveChannelCallback(event)
receiveChannel = event.channel;
receiveChannel.onmessage = handleReceiveMessage;
receiveChannel.onopen = handleReceiveChannelStatusChange;
receiveChannel.onclose = handleReceiveChannelStatusChange;
// Handle onmessage events for the receiving channel.
// These are the data messages sent by the sending channel.
function handleReceiveMessage(event)
var el = document.createElement("p");
var txtNode = document.createTextNode(event.data);
el.appendChild(txtNode);
receiveBox.appendChild(el);
// Handle status changes on the receiver's channel.
function handleReceiveChannelStatusChange(event)
if (receiveChannel)
console.log("Receive channel's status has changed to " +
receiveChannel.readyState);
// Here you would do stuff that needs to be done
// when the channel's status changes.
/
function disconnectPeers()
// Close the RTCDataChannels if they're open.
sendChannel.close();
receiveChannel.close();
// Close the RTCPeerConnections
localConnection.close();
remoteConnection.close();
sendChannel = null;
receiveChannel = null;
localConnection = null;
remoteConnection = null;
// Update user interface elements
connectButton.disabled = false;
disconnectButton.disabled = true;
sendButton.disabled = true;
messageInputBox.value = "";
messageInputBox.disabled = true;
window.addEventListener('load', startup, false);
【问题讨论】:
请看这里***.com/questions/58429603/… 如果您发布当前正在运行的 Firefox 和 chrome 版本会更有帮助。此外,您可能已经这样做了,但是,请尝试禁用您已安装的所有扩展。 感谢@MarceloLacerda 我测试过的手机仅用于开发,因此没有扩展。 @PRAJINPRAKASH 我已经查看了该链接,但这是一个不同的问题。他们从不设置本地描述,所以它在任何地方都不起作用。 您的 WebRTC 实现已更新,您可以参考以下链接来更新您的 WebRTC 实现。 developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/… 【参考方案1】:找了很久才找到答案是新版chrome的bug,解决办法是为Android 10而不是11构建应用。
【讨论】:
这为我解决了。谢谢! 我们还有其他方法可以解决这个问题吗?看起来我们不能再将我们的 targetSdk 设置为 29(或更低)以进行 google play 上传。因此,如果您需要发布到 google play,此解决方案将不起作用。有任何想法吗?我试过去 GeckoView,但工作量很大:( 我没有发现任何东西,但这绝对是一个错误,谷歌已经知道了,所以他们应该在未来修复它。以上是关于WebRTC onicecandidate 不会在 chrome for android 上触发,但适用于所有其他浏览器,包括适用于 android 的 firefox的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Chrome 中为 WebRTC 调用者设置远程描述而不会出错?