提问人:Creepy 提问时间:10/4/2023 更新时间:10/4/2023 访问量:19
重新协商 webrtc 连接时,流变为“空”
When renegotiating a webrtc connection, stream gets "empty"
问:
我目前正在使用 webrtc 进行点对点视频聊天,并提供打开和关闭麦克风和摄像头的选项。初始设置工作正常,如果我选择了麦克风,只有我的麦克风会发送给其他用户,反之亦然,视频源或两者兼而有之,但是如果我尝试更改它(将自己静音或关闭相机)在“第一次提供”参与者上,其他参与者将一无所获,即使我尝试重新打开它, 它仍然显示一个空的提要。有时我可以设法让它再次工作,但我不知道是什么模式使它工作。
我的主要 js、saveOffer、getOffer、getAnswer、setAnswer 等都是对服务器的 ajax 请求,其中 sdp-s 作为“信令通道”作为文件存储
let element = document.getElementById('mysdp');
function SignalingChannel() {
// Create an array to store event listeners
this.listeners = [];
// Method to add an event listener
this.addEventListener = function (eventName, callback) {
this.listeners.push({ eventName, callback });
};
// Method to trigger the "message" event
this.postMessage = function (data) {
// Emit the "message" event
this.emitEvent("message", data);
};
// Method to trigger the "send" function
this.send = function (data) {
// Display the data
//console.log("Sent data: " + data);
};
// Method to emit events
this.emitEvent = function (eventName, data) {
// Find the listeners for the given event
const eventListeners = this.listeners.filter(
(listener) => listener.eventName === eventName
);
// Call the callback functions for each listener
eventListeners.forEach((listener) => {
listener.callback(data);
});
};
}
const signalingChannel = new SignalingChannel();
var options = { offerToReceiveAudio: true, offerToReceiveVideo: true };
signalingChannel.addEventListener('message', message => {
console.log(message);
});
const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
const peerConnection = new RTCPeerConnection(configuration);
let dataChannel;
let sender;
function addTrack(stream){
console.log("AddTrack start");
const senders = peerConnection.getSenders();
senders.forEach((sender) => peerConnection.removeTrack(sender));
console.log("Previous tracks removed");
stream.getTracks().forEach(track => {
console.log("Adding track: ");
console.log(track);
sender = peerConnection.addTrack(track, localStream);
});
console.log("AddTrack end");
}
//Used for renegotiation
async function changeCall(roomid){
console.log("Creating offer");
let offer = await peerConnection.createOffer(options);
console.log("New offer created");
await peerConnection.setLocalDescription(offer);
console.log("Local description set");
let lsdp = JSON.stringify(peerConnection.localDescription);
let lsdpoffer = {'offer' : lsdp};
console.log("Renegotiating, saving offer to server");
await saveOffer(lsdpoffer,roomid);
//await dataChannel.send('{"renegotiation":true}');
console.log("Sending renegotiation ping");
await dataChannel.send('{"renegotiation":true}');
}
//Used for initiating a call
async function makeCall(roomid) {
console.log("call");
signalingChannel.addEventListener('message', async message => {
message = JSON.parse(message);
if (message.answer) {
const remoteDesc = new RTCSessionDescription(message.answer);
await peerConnection.setRemoteDescription(remoteDesc);
console.log("remote description set");
}
});
dataChannel = peerConnection.createDataChannel("Channel");
dataChannel.onmessage = e => {
try {
console.log(e.data);
let json = JSON.parse(e.data);
if(json.renegotiation){
getOffer(roomid);
}
} catch (e) {
return false;
}
};
dataChannel.onopen = e => console.log("open!!!!");
dataChannel.onclose = e => leave(roomid);
peerConnection.onnegotiationneeded = e => {
renegotiateStart(roomid)
}
peerConnection.onicecandidate = e => {
console.log(" NEW ice candidate!! on localconnection reprinting SDP " )
let lsdp = JSON.stringify(peerConnection.localDescription);
let lsdpoffer = {'offer' : lsdp};
saveOffer(lsdpoffer,roomid);
tryUntilAnswer(roomid);
//console.log(lsdpoffer);
}
let offer = await peerConnection.createOffer(options);
await peerConnection.setLocalDescription(offer);
console.log("Local description set");
let lsdp = JSON.stringify(peerConnection.localDescription);
let lsdpoffer = {'offer' : lsdp};
//console.log(lsdpoffer);
console.log("Calling");
saveOffer(lsdpoffer,roomid);
//console.log(JSON.stringify(peerConnection.localDescription))
//console.log(tosendoffer);
}
//Answering to a call
signalingChannel.addEventListener('message', async message => {
message = JSON.parse(message);
if (message.offer) {
peerConnection.setRemoteDescription(message.offer);
peerConnection.onnegotiationneeded = e => {
renegotiateStart(roomid)
}
peerConnection.onicecandidate = e => {
console.log(" NEW ice candidate!! on localconnection reprinting SDP " )
let lsdp = JSON.stringify(peerConnection.localDescription);
let lsdpanswer = {'answer':lsdp};
//console.log(lsdpanswer);
setAnswer(lsdpanswer,roomid);
}
peerConnection.ondatachannel= e => {
dataChannel = e.channel;
dataChannel.onmessage =e => {
try {
console.log(e.data);
let json = JSON.parse(e.data);
if(json.renegotiation){
getOffer(roomid);
}
} catch (e) {
return false;
}
};
dataChannel.onopen = e => console.log("open!!!!");
dataChannel.onclose =e => leave(roomid);
peerConnection.channel = dataChannel;
}
let answer = await peerConnection.createAnswer(options);
await peerConnection.setLocalDescription(answer);
}
});
// Listen for connectionstatechange on the local RTCPeerConnection
const remoteVideo = document.getElementById('remoteVideo');
peerConnection.addEventListener('track', async (event) => {
console.log("type: "+typeof(event.streams));
console.log("streams: "+event.streams);
const [remoteStream] = event.streams;
remoteVideo.srcObject = remoteStream;
console.log("New remoteStream:", remoteStream);
});
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// Peers connected!
console.log("Connected!");
}
});
切换摄像头和麦克风的代码
function toggleVisible(eid){
let e = document.getElementById(eid);
if(e.classList.contains('d-none')){
e.classList.remove('d-none');
}
else{
e.classList.add('d-none');
}
}
async function toggleCamera(){
console.log(localStream);
let togbut = document.getElementById("toggleCameraButton");
let togmic = document.getElementById("toggleMicrophoneButton");
let audio = false;
if(localStream){
localStream.getTracks().forEach((track) => track.stop());
localStream = null;
}
if(togmic.classList.contains('on')){
audio = true;
}
let localPlayer = document.getElementById('localVideo');
if(togbut.classList.contains('off')){
if(await playVideoFromCamera(true, audio) == false) {
return false;
}
togbut.src = "/ozekiservices/chatvideo/attachments/camera-video.svg";
togbut.classList.remove('off');
togbut.classList.add('on');
return;
}
togbut.src = "/ozekiservices/chatvideo/attachments/camera-video-off.svg";
localPlayer.pause();
if(audio){
if(await playVideoFromCamera(false, audio) == false){
return false;
}
}
togbut.classList.add('off');
togbut.classList.remove('on');
localPlayer.classList.add('d-none');
//makeCall(roomid);
}
async function toggleMicrophone(){
console.log(localStream);
console.log("toggleMic");
let togbut = document.getElementById("toggleMicrophoneButton");
let togcam = document.getElementById("toggleCameraButton");
console.log("Stopping tracks");
if(localStream){
localStream.getTracks().forEach((track) => track.stop());
localStream = null;
}
console.log("Tracks stopped");
let video = false;
if(togcam.classList.contains('on')){
console.log("CAM feed ON");
video = true;
}
let localPlayer = document.getElementById('localVideo');
if(togbut.classList.contains('off')){
console.log("Turn MIC ON");
if(await playVideoFromCamera(video, true) == false) return false;
togbut.src = "/ozekiservices/chatvideo/attachments/mic.svg";
togbut.classList.remove('off');
togbut.classList.add('on');
return;
}
togbut.src = "/ozekiservices/chatvideo/attachments/mic-mute.svg";
localPlayer.pause();
if(video){
if(await playVideoFromCamera(video, false) == false) return false;
}
togbut.classList.add('off');
togbut.classList.remove('on');
if(!video){
localPlayer.classList.add('d-none');
}
}
打开相机源的代码
let minWidth= 200;
let minHeight= 200;
function cameratooption(camera)
{
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
return cameraOption;
}
function updateCameraList(cameras) {
const listElement = document.getElementById('availableCameras');
listElement.innerHTML = '';
let cameraoptions = cameras.map(camera => cameratooption(camera));
console.log(cameraoptions);
cameraoptions.forEach(cameraOption => listElement.add(cameraOption));
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
let cameras = devices.filter(device => device.kind === type);
updateCameraList(cameras);
}
// Get the initial set of cameras connected
const videoCameras = getConnectedDevices('videoinput');
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('videoinput');
});
// Open camera with at least minWidth and minHeight capabilities
async function openCamera(cameraId, minWidth, minHeight, video, audio) {
if(video){
video = {
'deviceId': cameraId,
'width': {'min': minWidth},
'height': {'min': minHeight}
}
}
if(audio){
audio = {'echoCancellation': true}
}
const constraints = {
'audio': audio,
'video': video
}
let result = false;
try{
result = await navigator.mediaDevices.getUserMedia(constraints);
}
catch{
console.log("cam not accesible");
}
return result;
}
var localStream;
async function playVideoFromCamera(video, audio) {
if(!(video || audio)){
return;
}
try {
e = document.getElementById('availableCameras');
cameraId = e.options[e.selectedIndex].value;
console.log(cameraId);
const stream = await openCamera(cameraId, minWidth, minHeight, video, audio);
if(stream == false){
return false;
}
localStream = stream;
const videoElement = document.querySelector('video#localVideo');
if(video){
videoElement.classList.remove('d-none');
}
let newStream = new MediaStream(stream.getTracks());
let videoTrack = newStream.getVideoTracks()[0];
let audioTrack = newStream.getAudioTracks()[0];
if(audioTrack){
newStream.removeTrack(audioTrack);
}
videoElement.srcObject = newStream;
addTrack(stream);
console.log("streamdata: "+stream);
} catch(error) {
console.error('Error opening video camera.', error);
}
}
我尝试调试代码的基本上每个部分(由于我的代码拙劣,这非常困难),尝试了不同的方法,但没有运气。如果有人能告诉我我做错了什么,我会非常感激,因为我在这一点上没有想法,老实说只是卡住了。
答: 暂无答案
评论