音视频通话 2.0
Web
新手接入指南
产品简介
产品介绍
功能特性
产品优势
应用场景
基本概念
使用限制
产品计费
按量计费
资源包
体验 Demo
更新日志
下载 SDK 和示例代码
快速开始
快速跑通 Sample Code
接入流程
创建应用
开通服务
集成 SDK
实现音视频通话
Token 鉴权
高级 Token 鉴权
基础功能
设置音频属性
设置视频属性
设置通话音量
屏幕共享
音频共享
监测发言者音量
通话中质量监测
进阶功能
音频管理
客户端音视频录制
自定义音频采集与渲染
音效与伴音
视频管理
视频截图
基础美颜
水印
自定义视频采集
设备管理
音视频设备检测
音频设备管理
媒体流加密
云端录制
使用云代理
媒体插件
虚拟背景
高级美颜
AI 降噪
最佳实践
音视频参数配置推荐
浏览器自动播放受限处理
通话前网络质量探测和设备检测
实现在线教育
API 参考
Web API 参考
服务端 API
错误码
控制台指南
常见问题处理
FAQ
错题集
音频常见问题排查
视频常见问题排查
服务协议

实现音视频通话

更新时间: 2023/02/14 19:15:45

网易云信音视频通话产品的基本功能包括高质量的实时音视频通话。当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程。本文档为您展示音视频通话提供的基本业务流程。

前提条件

请确认您已完成以下操作:

请使用 HTTPS 协议访问项目,勿使用 HTTP 协议访问非 localhost 的项目,否则 Web 浏览器控制台会报错 NOT_SUPPORT 41001

示例代码

网易云信为您提供完整的实现基础音视频通话(包括创建界面)的示例代码作为参考,您可以直接拷贝用于运行测试。

实现音视频通话的示例代码
<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
  • 本端视频窗口
  • 远端视频窗口
  • 开始通话按钮
  • 结束通话按钮

效果图如下图所示。

web界面

步骤二 引用文件

在项目相应的前端页面文件中,对 NIM_Web_NERTC_vx.x.x.js 文件进行引用。

  • import 方式引入:

    import NERTC from '../../NIM_Web_NERTC_vx.x.x.js'
    
  • script 标签引入:

    <script src="./NIM_Web_NERTC_vx.x.x.js"></script>
    

步骤三 初始化

执行 createClient 方法创建 client 实例。

您需要将 appkey 替换为您的应用对应的 App Key。

//创建client实例
rtc.client = NERTC.createClient({
  appkey: '<yourAppKey>', //您的 App Key
  debug: true, //是否开启调试日志
});

步骤四 加入房间

加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,可以使用信令,总体实现流程请参见一对一会话操作流程,具体呼叫邀请机制的实现请参见邀请机制

调用 join 方法加入房间。

示例代码如下:

//加入房间
rtc.client.join({
    channelName: '房间名称',
    uid: uid, 
    token: ''  //调试模式下可设置为 null。正式上线前设置为相应的 Token,具体请参见“Token 鉴权”章节。
}).then((obj) => {
    console.info('加入房间成功...')
    //初始化本地流,并且发布
    initLocalStream() //后面介绍说明
})

参数说明

参数 说明
token 安全认证签名(NERTC Token)。
  • 调试模式下:可设置为 null。产品默认为安全模式,您可以在网易云信控制台将鉴权模式修改为调试模式,具体请参见 Token 鉴权
    调试模式的安全性不高,请在产品正式上线前修改为安全模式。
  • 产品正式上线后:请设置为已获取的 NERTC Token。安全模式下必须设置为获取到的 Token 。若未传入正确的 Token 将无法进入房间。

    推荐使用安全模式

channelName 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。
设置相同房间名称的用户会进入同一个通话房间。
您也可以在加入通道前,通过创建房间接口创建房间。加入房间时,若传入的 {channelName} 未事先创建,则云信服务器内部将为其自动创建一个名为 {channelName} 的通话房间。
uid 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。此 uid 为用户在您应用中的 ID,请在您的业务服务器上自行管理并维护。

为了实现标准音视频通话业务,您还需要在初始化时注册相关必要回调,建议您请在初始化方法中传入以下回调:

//房间连接状态改变通知回调
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);
});
//网络质量通知回调(请列出所有枚举)
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)
  })
})

步骤五 设置本地视图

初始化本地流成功后,可以设置本地视图,预览本地图像。

  1. 调用 getDevices 方法获取麦克风和摄像头设备的 deviceId。具体步骤请参考音视频设备检测

  2. 加入房间后,调用 createStream 方法创建本地音视频流,并设置 deviceId

  3. 调用 initplay 方法初始化并播放本地音视频流,以预览本地图像;可以再通过 setLocalRenderMode 方法设置视频画面的渲染模式,比如视频宽、高和裁剪选项。

在加入房间前,默认预览分辨率为 640*480,您可以通过 setVideoProfile 接口的 resolution 参数调整采集分辨率,但请在 initopen 之前调用。

  1. 可以调用 publish 方法发布自己的多媒体流至流媒体,供其他用户订阅。

示例代码如下:

//初始化本地流并且发布
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 成功')
    })
  })
}

步骤六 设置远端视图

音视频通话过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。

  1. 通过以下回调获取相关信息:

    • Client.on("peer-online"):监听远端用户加入通话房间的事件,并抛出对方的 uid。当本端加入房间后,也会通过此回调抛出通话房间内已有的其他用户。

      示例代码如下:

      rtc.client.on('peer-online', evt => {
        console.log(`${evt.uid} 加入房间`)
        addLog(`${evt.uid} 加入房间`)
      })
      
    • Client.on("stream-added"):监听远端用户发布视频流的事件,回调中携带对方的 uid 与发布的视频分辨率。

      示例代码如下:

      rtc.client.on('stream-added', evt => {
          var remoteStream = evt.stream;
          console.log('收到别人的发布消息: ', remoteStream.streamID, 'mediaType: ', evt.mediaType)
      })
      
  2. 在监听到远端用户发布视频流后,本端可以通过 subscribe 方法对其发起视频流的订阅,来将对方的视频流渲染到视频画布上。

    示例代码如下:

    //设置要订阅音频或者视频
    remoteStream.setSubscribeConfig({
        audio: true,//订阅麦克风音频
        audioSlave: true,//订阅音频辅流
        video: true,//订阅视频
        screenShare: true,//订阅屏幕共享
        highOrLow: NERTC.STREAM_TYPE.HIGH,//订阅大流
      })
    //发起订阅
    rtc.client.subscribe(remoteStream).then(()=>{
        console.log('发起订阅对端成功')
      })
    
  3. 订阅成功后,可进一步在 Client.on("stream-adsubscribeded") 回调中调用 play 方法播放远端视频流。

    示例代码如下:

    //播放订阅的对端的音视频流
      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 方法退出通话房间。

  //用户无需做一些清除动作,sdk会自动做清除逻辑
  rtc.client.leave()

步骤八 销毁实例

当确定短期内不再使用音视频通话实例时,可以调用 destroy 方法释放对应的对象资源。

  //一般情况下无需使用
  rtc.client.destroy()

常见问题

为什么我订阅远端用户发布的流后听不到声音?

答:NERTC Web SDK 要求您在成功订阅远端发送的音视频流之后调用 play 方法播放,因此您需要在接收到的 stream-subscribed 回调(表示订阅远端的音/视频流成功)里,调用 play 方法播放远端的音视频流,网易云信推荐您此时将 audio 和 video 参数均设置为 true。

为什么我离开房间后,摄像头仍处于使用状态?

答:您可能重复调用了 Stream.init 方法初始化音视频流对象,导致您在本地创建了两条不同的流,同时在使用摄像头设备。因此在您调用 leave 方法离开音视频房间时,只结束了其中一条流的设备采集任务,致使摄像头仍处于使用状态。

建议您在实际业务场景中,需管理好 Stream.init 方法的调用次数及 Stream 对象的生命周期,确保每个 Stream 只被初始化一次,以确保音视频的正常使用。

此文档是否对你有帮助?
有帮助
我要吐槽
  • 前提条件
  • 示例代码
  • 背景信息
  • 操作步骤
  • 步骤一 (可选)创建音视频通话界面
  • 步骤二 引用文件
  • 步骤三 初始化
  • 步骤四 加入房间
  • 步骤五 设置本地视图
  • 步骤六 设置远端视图
  • 步骤七 退出通话房间
  • 步骤八 销毁实例
  • 常见问题
  • 为什么我订阅远端用户发布的流后听不到声音?
  • 为什么我离开房间后,摄像头仍处于使用状态?