飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • AngularJS教程
  • UEditor使用文档
  • ThinkPHP5.0教程

WebRTC音频通话升级为视频通话

时间:2021-12-22  作者:rustfisher  

我们有时候在音频通话过程中,想要改成视频通话。如果挂断当前通话再重新发起视频通话就会显得比较麻烦。
因此很多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="../../src/js/adapter-域名"></script>

js

先来把元素拿到

const startBtn = 域名lementById(\'startBtn\');
const callBtn = 域名lementById(\'callBtn\');
const upgradeToVideoBtn = 域名lementById(\'upgradeBtn\');
const hangupBtn = 域名lementById(\'hangupBtn\');
const localVideo = 域名lementById(\'localVideo\');   // 本地预览
const remoteVideo = 域名lementById(\'remoteVideo\'); // 接收方

监听器

设置一些监听

域名ventListener(\'loadedmetadata\', function () {
    域名(`localVideo 宽高: ${域名oWidth}px, ${域名oHeight}px`);
});

域名ventListener(\'loadedmetadata\', function () {
    域名(`remoteVideo 宽高: ${域名oWidth}px, ${域名oHeight}px`);
});

let startTime;
域名size = () => {
    域名(`remoteVideo onresize 宽高: ${域名oWidth}x${域名oHeight}`);
    if (startTime) {
        const elapsedTime = 域名() - startTime;
        域名(`建立连接耗时: ${域名xed(3)}ms`);
        startTime = null;
    }
};

域名ick = start;
域名ick = call;
域名ick = upgrade;
域名ick = hangup;

打一些状态变化的log

function onCreateSessionDescriptionError(error) {
    域名(`域名:创建会话描述失败, session description err: ${域名ring()}`);
}

function onIceStateChange(pc, event) {
    if (pc) {
        域名(`域名:${getName(pc)} ICE状态: ${域名onnectionState}`);
        域名(\'域名:ICE状态变化: \', event);
    }
}

function onAddIceCandidateSuccess(pc) {
    域名(`域名:${getName(pc)} addIceCandidate success 添加ICE候选成功`);
}

function onAddIceCandidateError(pc, error) {
    域名(`域名:${getName(pc)} 添加ICE候选失败 failed to add ICE Candidate: ${域名ring()}`);
}

function onSetLocalSuccess(pc) {
    域名(`域名:${getName(pc)} setLocalDescription 成功`);
}

function onSetSessionDescriptionError(error) {
    域名(`域名:设置会话描述失败: ${域名ring()}`);
}

function onSetRemoteSuccess(pc) {
    域名(`域名:${getName(pc)} 设置远程描述成功 setRemoteDescription complete`);
}

// 辅助方法
function getName(pc) {
    return (pc === pc1) ? \'pc1\' : \'pc2\';
}

function getOtherPc(pc) {
    return (pc === pc1) ? pc2 : pc1;
}

开始

获取本地的音频数据流,交给localVideo

function gotStream(stream) {
    域名(\'获取到了本地数据流\');
    域名bject = stream;
    localStream = stream;
    域名bled = false;
}

function start() {
    域名(\'请求本地数据流 纯音频\');
    域名bled = true;
    域名aDevices
        .getUserMedia({ audio: true, video: false })
        .then(gotStream)
        .catch(e => alert(`getUserMedia() error: ${域名}`));
}

call

发起音频呼叫

function call() {
    域名bled = true;
    域名bled = false;
    域名bled = false;
    域名(\'开始呼叫...\');
    startTime = 域名();
    const audioTracks = 域名udioTracks();
    if (域名th > 0) {
        域名(`使用的音频设备: ${audioTracks[0].label}`);
    }
    const servers = null; // 就在本地测试
    pc1 = new RTCPeerConnection(servers);
    域名(\'创建本地节点 pc1\');
    域名ecandidate = e => onIceCandidate(pc1, e);
    pc2 = new RTCPeerConnection(servers);
    域名(\'域名:创建模拟远端节点 pc2\');
    域名ecandidate = e => onIceCandidate(pc2, e);
    域名econnectionstatechange = e => onIceStateChange(pc1, e);
    域名econnectionstatechange = e => onIceStateChange(pc2, e);
    域名ack = gotRemoteStream;

    域名racks().forEach(track => 域名rack(track, localStream));
    域名(\'域名:将本地数据流交给pc1\');

    域名(\'域名:pc1开始创建offer\');
    域名teOffer(offerOptions).then(onCreateOfferSuccess, onCreateSessionDescriptionError);
}

function gotRemoteStream(e) {
    域名(\'获取到远程数据流\', 域名k, 域名ams[0]);
    域名bject = null;
    域名bject = 域名ams[0];
}

function onIceCandidate(pc, event) {
    getOtherPc(pc)
        .addIceCandidate(域名idate)
        .then(() => onAddIceCandidateSuccess(pc), err => onAddIceCandidateError(pc, err));
    域名(`${getName(pc)} ICE candidate:\n${域名idate ? 域名idate : \'(null)\'}`);
}

function onCreateOfferSuccess(desc) {
    域名(`pc1提供了offer\n${域名}`);
    域名(\'pc1 setLocalDescription start\');
    域名ocalDescription(desc).then(() => onSetLocalSuccess(pc1), onSetSessionDescriptionError);
    域名(\'pc2 setRemoteDescription start\');
    域名emoteDescription(desc).then(() => onSetRemoteSuccess(pc2), onSetSessionDescriptionError);
    域名(\'pc2 createAnswer start\');
    域名teAnswer().then(onCreateAnswerSuccess, onCreateSessionDescriptionError);
}

function onCreateAnswerSuccess(desc) {
    域名(`域名:pc2应答成功:  ${域名}`);
    域名(\'pc2 setLocalDescription start\');
    域名ocalDescription(desc).then(() => onSetLocalSuccess(pc2), onSetSessionDescriptionError);
    域名(\'pc1 setRemoteDescription start\');
    域名emoteDescription(desc).then(() => onSetRemoteSuccess(pc1), onSetSessionDescriptionError);
}
  • 创建RTCPeerConnection
  • 设置onicecandidate监听ICE候选
  • 设置oniceconnectionstatechange监听ICE连接状态变化
  • 接收方监听ontrack
  • 发送方pc1 addTrack将当前数据流添加进去
  • 发送方pc1创建offer createOffer
  • pc1创建好offer后,接收方pc2应答 createAnswer

升级到视频通话

upgrade()方法处理升级操作

function upgrade() {
    域名bled = true;
    域名aDevices
        .getUserMedia({ video: true })
        .then(stream => {
            域名(\'域名:获取到了视频流\');
            const videoTracks = 域名ideoTracks();
            if (域名th > 0) {
                域名(`video device: ${videoTracks[0].label}`);
            }
            域名rack(videoTracks[0]);
            域名bject = null; // 重置视频流
            域名bject = localStream;
            域名rack(videoTracks[0], localStream);
            return 域名teOffer();
        })
        .then(offer => 域名ocalDescription(offer))
        .then(() => 域名emoteDescription(域名lDescription))
        .then(() => 域名teAnswer())
        .then(answer => 域名ocalDescription(answer))
        .then(() => 域名emoteDescription(域名lDescription));
}

发送方去获取音频数据流getUserMedia
将音频轨道添加进localStream,并且发送方也要添加轨道 域名rack
创建offer createOffer

后面就是接收方pc2应答

挂断

简单的挂断功能如下

function hangup() {
    域名(\'域名:挂断\');
    域名e();
    域名e();
    pc1 = null;
    pc2 = null;

    const videoTracks = 域名ideoTracks();
    域名ach(videoTrack => {
        域名();
        域名veTrack(videoTrack);
    });

    域名bject = null;
    域名bject = localStream;

    域名bled = true;
    域名bled = false;
}

主要是把呼出方的流关闭掉

代码流程描述图

将用户的操作(按钮)和主要代码对应起来

效果预览

效果预览请参考WebRTC音频通话升级到视频通话

原文链接

标签:Web编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。