我们有时候在音频通话过程中,想要改成视频通话 。如果挂断当前的通话再重新发起视频通话就会显得比较麻烦 。
因此很多App提供了将音频通话升级成视频通话的功能,同时也有将视频通话降为音频通话的功能 。
本文演示的是在本地模拟音频通话,并且将音频通话升级为视频通话 。
准备#界面很简单,2个video加上几个按钮 。
<video id="localVideo" playsinline autoplay muted></video><video id="remoteVideo" playsinline autoplay></video><div><button id="startBtn">开始</button><button id="callBtn">Call</button><button id="upgradeBtn">升级为视频通话</button><button id="hangupBtn">挂断</button></div>
用的是本地的adapter
<script src=https://www.isolves.com/it/cxkf/ydd/baike/2021-12-23/src/js/adapter-2021.js">
js#先来把元素拿到
const startBtn = document.getElementById('startBtn');const callBtn = document.getElementById('callBtn');const upgradeToVideoBtn = document.getElementById('upgradeBtn');const hangupBtn = document.getElementById('hangupBtn');const localVideo = document.getElementById('localVideo');// 本地预览const remoteVideo = document.getElementById('remoteVideo'); // 接收方
监听器#设置一些监听
localVideo.addEventListener('loadedmetadata', function () {console.log(`localVideo 宽高: ${this.videoWidth}px, ${this.videoHeight}px`);});remoteVideo.addEventListener('loadedmetadata', function () {console.log(`remoteVideo 宽高: ${this.videoWidth}px, ${this.videoHeight}px`);});let startTime;remoteVideo.onresize = () => {console.log(`remoteVideo onresize 宽高: ${remoteVideo.videoWidth}x${remoteVideo.videoHeight}`);if (startTime) {const elapsedTime = window.performance.now() - startTime;console.log(`建立连接耗时: ${elapsedTime.toFixed(3)}ms`);startTime = null;}};startBtn.onclick = start;callBtn.onclick = call;upgradeToVideoBtn.onclick = upgrade;hangupBtn.onclick = hangup;
打一些状态变化的log
function onCreateSessionDescriptionError(error) {console.log(`rustfisher.com:创建会话描述失败, session description err: ${error.toString()}`);}function onIceStateChange(pc, event) {if (pc) {console.log(`rustfisher.com:${getName(pc)} ICE状态: ${pc.iceConnectionState}`);console.log('rustfisher.com:ICE状态变化: ', event);}}function onAddIceCandidateSuccess(pc) {console.log(`rustfisher.com:${getName(pc)} addIceCandidate success 添加ICE候选成功`);}function onAddIceCandidateError(pc, error) {console.log(`rustfisher.com:${getName(pc)} 添加ICE候选失败 failed to add ICE Candidate: ${error.toString()}`);}function onSetLocalSuccess(pc) {console.log(`rustfisher.com:${getName(pc)} setLocalDescription 成功`);}function onSetSessionDescriptionError(error) {console.log(`rustfisher.com:设置会话描述失败: ${error.toString()}`);}function onSetRemoteSuccess(pc) {console.log(`rustfisher.com:${getName(pc)} 设置远程描述成功 setRemoteDescription complete`);}// 辅助方法function getName(pc) {return (pc === pc1) ? 'pc1' : 'pc2';}function getOtherPc(pc) {return (pc === pc1) ? pc2 : pc1;}
开始#获取本地的音频数据流,交给localVideo
function gotStream(stream) {console.log('获取到了本地数据流');localVideo.srcObject = stream;localStream = stream;callBtn.disabled = false;}function start() {console.log('请求本地数据流 纯音频');startBtn.disabled = true;navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(gotStream).catch(e => alert(`getUserMedia() error: ${e.name}`));}
call#发起音频呼叫
function call() {callBtn.disabled = true;upgradeToVideoBtn.disabled = false;hangupBtn.disabled = false;console.log('开始呼叫...');startTime = window.performance.now();const audioTracks = localStream.getAudioTracks();if (audioTracks.length > 0) {console.log(`使用的音频设备: ${audioTracks[0].label}`);}const servers = null; // 就在本地测试pc1 = new RTCPeerConnection(servers);console.log('创建本地节点 pc1');pc1.onicecandidate = e => onIceCandidate(pc1, e);pc2 = new RTCPeerConnection(servers);console.log('rustfisher.com:创建模拟远端节点 pc2');pc2.onicecandidate = e => onIceCandidate(pc2, e);pc1.oniceconnectionstatechange = e => onIceStateChange(pc1, e);pc2.oniceconnectionstatechange = e => onIceStateChange(pc2, e);pc2.ontrack = gotRemoteStream;localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));console.log('rustfisher.com:将本地数据流交给pc1');console.log('rustfisher.com:pc1开始创建offer');pc1.createOffer(offerOptions).then(onCreateOfferSuccess, onCreateSessionDescriptionError);}function gotRemoteStream(e) {console.log('获取到远程数据流', e.track, e.streams[0]);remoteVideo.srcObject = null;remoteVideo.srcObject = e.streams[0];}function onIceCandidate(pc, event) {getOtherPc(pc).addIceCandidate(event.candidate).then(() => onAddIceCandidateSuccess(pc), err => onAddIceCandidateError(pc, err));console.log(`${getName(pc)} ICE candidate:n${event.candidate ? event.candidate.candidate : '(null)'}`);}function onCreateOfferSuccess(desc) {console.log(`pc1提供了offern${desc.sdp}`);console.log('pc1 setLocalDescription start');pc1.setLocalDescription(desc).then(() => onSetLocalSuccess(pc1), onSetSessionDescriptionError);console.log('pc2 setRemoteDescription start');pc2.setRemoteDescription(desc).then(() => onSetRemoteSuccess(pc2), onSetSessionDescriptionError);console.log('pc2 createAnswer start');pc2.createAnswer().then(onCreateAnswerSuccess, onCreateSessionDescriptionError);}function onCreateAnswerSuccess(desc) {console.log(`rustfisher.com:pc2应答成功:${desc.sdp}`);console.log('pc2 setLocalDescription start');pc2.setLocalDescription(desc).then(() => onSetLocalSuccess(pc2), onSetSessionDescriptionError);console.log('pc1 setRemoteDescription start');pc1.setRemoteDescription(desc).then(() => onSetRemoteSuccess(pc1), onSetSessionDescriptionError);}
推荐阅读
- 中国最好吃的羊肉在哪?
- 如何选购微型打印机
- Centos7使用ping命令对普通用户提权
- 深入理解 C 语言的 hello world
- 滇红茶产品种类,滇红茶的特色
- 在VUE中实现效果"换一换"功能
- SQLSERVER也能部署在linux环境?SQLServer2019在CENTOS7部署详解
- 斐讯无线路由器怎么设置?
- 斯维尼|西德尼·斯维尼如何在《亢奋》的浓妆艳抹中保持光滑皮肤?
- 黑头|用毛巾搓脸?泡沫越多越好?3种错误的洗脸方式,正在毁掉你的脸