实现音视频通话
更新时间: 2024/10/18 15:03:21
网易云信音视频通话产品的基本功能包括高质量的实时音视频通话。当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程。本文为您展示音视频通话提供的基本业务流程。
前提条件
请确认您已完成以下操作:
-
请使用 HTTPS 协议访问项目,勿使用 HTTP 协议访问非 localhost 的项目,否则 Web 浏览器控制台会报错
NOT_SUPPORT 41001
。
示例代码
网易云信为您提供完整的 实现基础音视频通话(包括创建界面)的示例代码作为参考,您可以直接拷贝用于运行测试。
单击展开查看实现音视频通话的示例代码。
HTML<html>
<head>
<meta charset="UTF-8">
<title>NERTC Video Call</title>
<style type="text/css">
* {
font-family: sans-serif;
}
h1,
h4 {
text-align: center;
}
.container {
text-align: center;
}
#localVideoContent, #remoteVideoContent {
width: 640px;
height: 480px;
border: 1px solid #dfdfdf;
}
#localVideoContent {
position: relative;
margin: 0 auto;
display: block;
}
#remoteVideoContent{
display: flex;
margin: auto;
position: relative !important;
}
</style>
</head>
<body>
<h1>
NERTC Video Call
</h1>
<div class="container">
<input type="number" id="channelName" name="房间名称" placeholder="房间名称" required>
<input type="number" id="uid" name="UID" placeholder="uid" required>
<button type="button" id="startCall">开始通话</button>
<button type="button" id="finishCall">结束通话</button>
</div>
<h4>Local video</h4>
<div id="localVideoContent"></div>
<h4>Remote video</h4>
<div id="remoteVideoContent"></div>
<script src="NIM_Web_NERTC_x.x.x.js"></script>
<script>
let appkey = ''; // 请输入自己的 appkey
let channelName ; // '您指定的房间号'
let uid ; // '您指定的用户 ID'
let client, localStream;
document.getElementById("startCall").onclick = async function () {
channelName = parseInt(document.querySelector('#channelName').value);
uid = parseInt(document.querySelector('#uid').value);
client = NERTC.createClient({appkey, debug: true})
// 监听事件
client.on('stream-added', event => {
const remoteStream = event.stream;
console.warn('收到别人的发布消息: ', remoteStream.streamID, 'mediaType: ', event.mediaType)
//订阅远端流
client.subscribe(remoteStream).then(()=> {
console.warn(`subscribe 成功 ${remoteStream.streamID}`)
});
});
client.on('stream-subscribed', event => {
// 远端流订阅成功
const remoteStream = event.stream;
console.warn('订阅别人的流成功的通知: ', remoteStream.streamID, 'mediaType: ', event.mediaType)
// 设置远端视频画布
remoteStream.setRemoteRenderMode({
width: 640,
height: 480
});
// 播放远端流
remoteStream.play('remoteVideoContent');
});
// 进房成功后开始推流
try {
await client.join({ channelName,uid });
localStream = NERTC.createStream({ uid, audio: true, video: true, client});
await localStream.init();
// 设置本地视频画布
localStream.setLocalRenderMode({
width: 640,
height: 480
});
// 播放本地流
localStream.play("localVideoContent");
await client.publish(localStream);
} catch (error) {
console.error(error);
}
}
document.getElementById("finishCall").onclick = async function () {
await client.leave();
}
</script>
</body>
</html>
背景信息
使用 SDK 实现音视频通话的主要流程如下图所示。
NERTC 中有两个重要类:
-
Client
代表一个本地客户端。Client 类的方法提供了音视频通话的主要功能,例如加入房间、发布音视频流等。
-
Stream
代表本地和远端的音视频流。Stream 类的方法用于定义音视频流对象的行为,例如流的播放控制、音视频的编码配置等。调用 Stream 方法时,请注意区分本地流和远端流对象。
第一步:创建音视频通话界面
您可以参考此步骤根据业务场景创建相应的音视频通话界面,若您已实现相应界面,请忽略该步骤。
实现基础的音视频通话,建议您参考 实现音视频通话的示例代码 在界面上添加以下控件。
- 房间名称
- 用户 ID
- 本端视频窗口
- 远端视频窗口
- 开始通话按钮
- 结束通话按钮
效果图如下图所示。
第二步:引用文件
在项目相应的前端页面文件中,对 NIM_Web_NERTC_vx.x.x.js
文件进行引用。
-
import 方式引入:
JavaScript
import NERTC from '../../NIM_Web_NERTC_vx.x.x.js'
-
script 标签引入:
JavaScript
<script src="./NIM_Web_NERTC_vx.x.x.js"></script>
第三步:初始化
执行 createClient
方法创建 client 实例。
您需要将 appkey
替换为您的应用对应的 App Key。
JavaScript//创建 client 实例
rtc.client = NERTC.createClient({
appkey: '<yourAppKey>', //您的 App Key
debug: true, //是否开启调试日志
});
第四步:加入房间
加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,可以使用 信令,总体实现流程请参考 一对一会话操作流程,具体呼叫邀请机制的实现请参考 邀请机制。
调用 join
方法加入房间。
示例代码 如下:
JavaScript//加入房间
rtc.client.join({
channelName: '房间名称',
uid: uid,
token: '' //调试模式下可设置为 null。正式上线前设置为相应的 Token,具体请参考 "Token 鉴权" 章节。
}).then((obj) => {
console.info('加入房间成功...')
//初始化本地流,并且发布
initLocalStream() //后面介绍说明
})
参数说明:
参数 | 说明 |
---|---|
token | 安全认证签名(NERTC Token)。
|
channelName | 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。 设置相同房间名称的用户会进入同一个通话房间。 |
uid | 用户的唯一标识 ID,为数字串,房间内每个用户的 uid 必须是唯一的。 |
为了实现标准音视频通话业务,您还需要在初始化时 注册相关必要回调,建议您在初始化方法中传入以下回调。
JavaScript//房间连接状态改变通知回调
rtc.client.on('connection-state-change', (evt)=>{
console.log(`connection-state-change ${evt.prevState} => ${evt.curState}。是否重连:${evt.reconnect}`)
})
//远端用户加入房间通知回调,建议在收到此回调后再进行设置远端视图等的操作
rtc.client.on('peer-online', evt => {
console.log(`${evt.uid} 加入房间`)
addLog(`${evt.uid} 加入房间`)
})
//远端用户退出房间通知回调
rtc.client.on('peer-leave', evt => {
console.log(`${evt.uid} 退出房间`)
leaveLog(`${evt.uid} 退出房间`)
})
//远端用户推流/停止推流通知回调,建议在收到此回调后再进行订阅或取消订阅音视频流的操作
rtc.client.on("stream-added", (evt)=>{
console.log(`远端${evt.stream.getId()}发布了 ${evt.mediaType} 流`)
rtc.client.subscribe(evt.stream)
});
rtc.client.on("stream-removed", (evt)=>{
// 远端流停止,则关闭渲染
evt.stream.stop(evt.mediaType);
});
//网络质量通知回调(请列出所有枚举)
rtc.client.on('network-quality', stats => {
console.log('=====房间里所有成员的网络状况:', stats)
let status = null
stats.forEach(item => {
status = 'uid: ' + item.uid + ',上行:' + item.uplinkNetworkQuality + ',下行:' + item.downlinkNetworkQuality
console.log(status)
})
})
第五步:设置本地视图
初始化本地流成功后,可以设置本地视图,预览本地图像。
-
调用
getDevices
方法获取麦克风和摄像头设备的deviceId
。具体步骤请参考 音视频设备检测。 -
加入房间后,调用
createStream
方法创建本地音视频流,并设置deviceId
。 -
调用
init
和play
方法初始化并播放本地音视频流,以预览本地图像。可以再通过setLocalRenderMode
方法设置视频画面的渲染模式,例如视频宽、高和裁剪选项。在加入房间前,默认预览分辨率为 640*480,您可以通过
setVideoProfile
接口的resolution
参数调整采集分辨率,但请在init
或open
之前调用。 -
可以调用
publish
方法发布自己的多媒体流至流媒体,供其他用户订阅。示例代码 如下:
JavaScript
//初始化本地流并且发布 async function initLocalStream() { const cameras = await NERTC.getCameras(); //获取可用的视频输入设备 const microphones = await NERTC.getMicrophones(); //获取可用的麦克风设备 //创建本端 stream 实例,销毁前无需重复创建 rtc.localStream = NERTC.createStream({ uid: uid, // 本端的 uid audio: true, // 是否从麦克风采集音频 microphoneId: microphones.microphoneId, // 麦克风设备 deviceId,通过 getMicrophones() 获取 video: true, // 是否从摄像头采集视频 cameraId: cameras.cameraId // 摄像头设备 deviceId,通过 getCameras() 获取 }) //启动本地音视频流,销毁前无需重复初始化 rtc.localStream.init().then(()=>{ console.warn('音视频初始化完成,播放本地视频') //用于播放视频的 div 元素 let div = document.getElementById('local-container') //开始播放本地视频流 rtc.localStream.play(div) //设置播放的视频容器大小 rtc.localStream.setLocalRenderMode({ width: 180, height: 150, cut: true // 是否裁剪 }) // 将本地音视频流发布至网易云信服务器,加入房间前不用执行此方法。 rtc.client.publish(rtc.localStream).then(()=>{ console.warn('本地 publish 成功') }) }) }
第六步:设置远端视图
音视频通话过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。
在设置远端视图前,需要提前通过回调获取相关信息。在初始化完成后,通过注册以下回调获取远端用户的状态。
-
Client.on("peer-online"):监听远端用户加入通话房间的事件,并抛出对方的 uid。当本端加入房间后,也会通过此回调抛出通话房间内已有的其他用户。
示例代码 如下:
JavaScript
rtc.client.on('peer-online', evt => { console.log(`${evt.uid} 加入房间`) addLog(`${evt.uid} 加入房间`) })
-
Client.on("stream-added"):监听远端用户发布视频流的事件,回调中携带对方的 uid 与发布的视频分辨率。
示例代码 如下:
JavaScript
rtc.client.on('stream-added', evt => { var remoteStream = evt.stream; console.log('收到别人的发布消息: ', remoteStream.streamID, 'mediaType: ', evt.mediaType) })
-
在监听到远端用户发布视频流后,本端可以通过
subscribe
方法对其发起视频流的订阅,来将对方的视频流渲染到视频画布上。示例代码 如下:
JavaScript
//设置要订阅音频或者视频 remoteStream.setSubscribeConfig({ audio: true,//订阅麦克风音频 audioSlave: true,//订阅音频辅流 video: true,//订阅视频 screenShare: true,//订阅屏幕共享 highOrLow: NERTC.STREAM_TYPE.HIGH,//订阅大流 }) //发起订阅 rtc.client.subscribe(remoteStream).then(()=>{ console.log('发起订阅对端成功') })
-
订阅成功后,可进一步在 Client.on("stream-subscribed") 回调中调用 play 方法播放远端视频流。
示例代码 如下:
JavaScript
//播放订阅的对端的音视频流 rtc.client.on('stream-subscribed', evt => { console.warn('订阅别人的流成功的通知') var remoteStream = evt.stream; let div = document.getElementById('remote-container') //开始播放远端音视频流 remoteStream.play(div).then(()=>{ console.log('播放对端的流成功') remoteStream.setRemoteRenderMode({ width: 180, height: 150, cut: true }) }) })
需要特别注意的是,由于浏览器限制自动播放策略的影响,您需要在播放时引导用户手动恢复播放或直接解除自动播放受限,具体请参考 浏览器自动播放受限处理。
第七步:退出通话房间
通过 leave
方法退出通话房间。
JavaScript //用户无需做一些清除动作,SDK 会自动做清除逻辑
async function leaveRoom() {
try {
// 使用 await 等待 leave 方法完成
await rtc.client.leave();
console.log('成功离开房间');
} catch (error) {
// 处理 leave 方法可能抛出的异常
console.error('离开房间失败', error);
}
}
第八步:销毁实例
当确定短期内不再使用音视频通话实例时,可以调用 destroy
方法释放对应的对象资源。
JavaScript //一般情况下无需使用
rtc.client.destroy()
常见问题
为什么我订阅远端用户发布的流后听不到声音?
NERTC Web SDK 要求您在成功订阅远端发送的音视频流之后调用 play
方法播放,因此您需要在接收到的 stream-subscribed
回调(表示订阅远端的音/视频流成功)里,调用 play
方法播放远端的音视频流,网易云信推荐您此时将 audio 和 video 参数均设置为 true。
为什么我离开房间后,摄像头仍处于使用状态?
您可能重复调用了 Stream.init
方法初始化音视频流对象,导致您在本地创建了两条不同的流,同时在使用摄像头设备。因此在您调用 leave
方法离开音视频房间时,只结束了其中一条流的设备采集任务,致使摄像头仍处于使用状态。
建议您在实际业务场景中,需管理好 Stream.init
方法的调用次数及 Stream 对象的生命周期,确保每个 Stream 只被初始化一次,以确保音视频的正常使用。