如何解决Web 音频 API 和 WebRTC:如果我使用 AudiContext 播放远程/对等音频流,我会在远程音频旁边听到自己的声音
我正在尝试使用 WebRTC 实现简单的点对点连接,并有可能为远程音频流增加增益,以使其他对等点声音更大。
P2P 连接部分目前工作正常,但音频部分让我头疼。
我已经解决了 chrome 中的错误,即当远程流未附加到静音音频元素时,AudioContext 未播放。
https://bugs.chromium.org/p/chromium/issues/detail?id=933677
https://bugs.chromium.org/p/chromium/issues/detail?id=952700
但是现在我遇到了一个问题,在我当前的解决方案中,我将远程流附加到 AudiContext 我可以听到我自己(localStream)在来自远程对等方的音频旁边说话。奇怪的是,我从来没有将 localStream 应用到 AudioContext。也许我在这里遗漏了一些明显的东西。
下面我会附上我的客户端代码。
我希望有人能帮助我。
提前致谢。
import './style.css'
import { io } from "socket.io-client";
const servers = {
iceServers: [
{
urls: ['stun:stun1.l.google.com:19302','stun:stun2.l.google.com:19302'],},],iceCandidatePoolSize: 10,};
// Global State
const pc = new RTCPeerConnection(servers);
console.log("connection to Signal Server");
const socket = io("https://chat.psps-apps.de");
let localStream: MediaStream;
let remoteStream: MediaStream;
socket.on("connect",() => {
console.log("Connected");
alert("Connected to Server");
});
socket.on("error",err => {
console.log("Error ",err);
alert("Error "+ err);
});
// HTML elements
const webcamButton = document.getElementById('webcamButton') as HTMLButtonElement;
const callButton = document.getElementById('callButton') as HTMLButtonElement;
const remoteAudio = document.getElementById('remoteAudio ') as HTMLAudioElement;
const hangupButton = document.getElementById('hangupButton') as HTMLButtonElement;
const range = document.getElementById("lautReg") as HTMLInputElement;
const label = document.getElementById("lautLabel") as HTMLLabelElement;
let gainNode: GainNode;
let context: AudioContext;
range.value = "1";
label.innerText = range.value;
range.onchange = (ev) => {
const inp = ev.target as HTMLInputElement;
label.innerText = inp.value;
gainNode.gain.value = parseFloat(inp.value) * 2;
console.log("Gain ",gainNode.gain.value);
console.log(context);
}
socket.on("offer-fw",async offer => {
console.log("ON offer-fw",offer);
await pc.setRemoteDescription(new RTCSessionDescription(offer));
const answerDescription = await pc.createAnswer();
await pc.setLocalDescription(answerDescription);
const answer = {
type: answerDescription.type,sdp: answerDescription.sdp,};
console.log("EMIT answer",answer);
socket.emit("answer",answer);
})
// Get candidates for caller
pc.onicecandidate = event => {
console.log("EMIT new-ice-offer-candidate",event.candidate?.toJSON());
event.candidate && socket.emit("new-ice-offer-candidate",event.candidate.toJSON());
}
// Listen for remote answer
socket.on("answer-fw",answer => {
console.log("ON answer-fw",answer);
const answerDescription = new RTCSessionDescription(answer);
pc.setRemoteDescription(answerDescription);
pc.onicecandidate = event => {
console.log("EMIT new-ice-answer-candidate",event.candidate?.toJSON());
event.candidate && socket.emit("new-ice-answer-candidate",event.candidate.toJSON());
}
})
// When answered,add candidate to peer connection
socket.on("new-ice-answer-candidate-fw",(candidateData) => {
console.log("ON new-ice-answer-candidate-fw",candidateData);
const candidate = new RTCIceCandidate(candidateData);
pc.addIceCandidate(candidate);
});
//################################################################
//when client gets called
socket.on("new-ice-offer-candidate-fw",(candidate) => {
console.log("ON new-ice-offer-candidate-fw");
console.log("Candidate",candidate);
pc.addIceCandidate(new RTCIceCandidate(candidate));
})
webcamButton.onclick = async () => {
localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
//!!!!The remote stream must be added to the audo HTMLElement and must be muted.
//ONLY IF this is the case then the AudioContext is able to play.
remoteStream = new MediaStream();
remoteAudio.srcObject = remoteStream;
remoteAudio.muted = true;
context = new AudioContext();
gainNode = context.createGain();
gainNode.gain.value = 1;
// Push tracks from local stream to peer connection
localStream.getTracks().forEach((track) => {
pc.addTrack(track,localStream);
});
// Pull tracks from remote stream,add to video stream
pc.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track);
});
const mediastreamsource = context.createmediastreamsource(remoteStream);
//this will apply the gain to the stream
mediastreamsource.connect(gainNode);
// this will play the audio with the gain applied
gainNode.connect(context.destination);
// event.streams[0].getTracks().forEach(track => {
// remoteStream.addTrack(track);
// console.log("Track",track);
// })
};
// webcamVideo.srcObject = localStream;
callButton.disabled = false;
webcamButton.disabled = true;
};
callButton.onclick = async () => {
// Create offer
const offerDescription = await pc.createOffer();
await pc.setLocalDescription(offerDescription);
const offer = {
sdp: offerDescription.sdp,type: offerDescription.type,};
socket.emit("offer",offer);
hangupButton.disabled = false;
}
function addGain(stream: MediaStream): MediaStream {
const mediastreamsource = context.createmediastreamsource(stream);
const mediaStreamDestination = context.createMediaStreamDestination();
mediastreamsource.connect(gainNode);
gainNode.connect(mediaStreamDestination);
return mediaStreamDestination.stream;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。