网易云信P2P客户端: 即时通信+音视频_云信指令消息
目录
- 一、即时通信(p2p)
-
- 1.1 web端
-
- 1.1.1 安装sdk
- 1.1.2 初始化实例
- 1.1.3 各种消息发送方式
- 1.1.4 历史记录查询方式
- 1.1.5 在线消息发布与订阅
- 1.2 小程序端(相关注意事项,借鉴web端)
-
- 1.2.1 安装sdk
- 1.2.2 初始化实例
- 1.2.3 其他
- 1.3 uniapp端(相关注意事项,借鉴web端和uniapp端)
-
- 1.3.1 安装sdk
- 1.3.3 其他
- 二、音视频通话(p2p)
-
- 2.1 web端
-
- 2.1.1 集成
- 2.1.2 初始化实例
- 2.1.3 创建音视频房间(服务端)
- 2.1.4 创建频道并邀请他人
- 2.1.5 获取鉴权token(服务端)
- 2.1.6 加入房间
- 2.1.7 信令监听
- 2.1.8 音视频监听
- 2.1.9 离开房间
- 2.1.10 信令接收端
-
- 点击接听按钮后:
- 点击拒绝按钮后:
- 点击挂断按钮后:
- 2.2 小程序端
- 2.3 uniapp端
文档地址
新手必看文档(内含api服务文档)
一、即时通信(p2p)
目前即时通信最新版本是10,v10是个大版本,下面的其他版本并不兼容,所以如果你的应用有个好几个端(PC、小程序、app等)都在使用,如果想要不同端之间的通讯,请保证大版本上保持一致。
此次由于我的其他端使用的是10以下版本的,所以我使用的是非10版本。目前官网主推v10,v9。 再低版本的文档查看需要查看以下:
此处可查看不同端的文档:
1.1 web端
1.1.1 安装sdk
通过npm安装,注意版本号选<1才是旧版本,然后import引入,注意目前sdk不支持cdn的形式引入。
import NIMSDK from \'nim-web-sdk-ng/dist/NIM_BROWSER_SDK\'
1.1.2 初始化实例
import NIMSDK from \'nim-web-sdk-ng/dist/NIM_BROWSER_SDK\'const nim = NIM.getInstance( /** * param1: NIMInitializeOptions */ { // 参数请阅读新手必看文档,一般需要调用后端接口查看,而不是写死前端 appkey: \'YOUR_APPKEY\', token: \'YOUR_TOKEN\', account: \'YOUR_ACCOUNT\' }, /** * param2: NIMOtherOptions */ { /** * 会话初始化设置 */ sessionConfig: { /** * 用户可以根据应用场景,设置消息是否要计入未读数。 */ unreadCountFilterFn: function (msg) { return true } }, /** * 同步初始化设置 */ syncOptions: { /** * 是否同步置顶会话消息 */ stickTopSessions: true, /** * 是否同步用户信息 */ myInfo: false } })const eventList = [ \'logined\', \'multiPortLogin\', \'kicked\', \'willReconnect\', \'disconnect\', \'msg\', // 接收到消息,对端发送消息,收到会触发,可在这里将收到的消息,push到本地的消息列表 \'pushEvents\', // 对端发布消息,本端订阅,收到会触发 \'updateSession\', // 发出的消息,发送后会触发,可在这里将发出的消息,push到本地的消息列表]eventList.forEach((key: any) => { nim.on(key, (res) => { console.log(`Receive ${key} event:`, res ? JSON.parse(JSON.stringify(res)) : res); });})// then,receive event \'logined\'await nim.connect();
可详细查看相关event说明:
注意:如果需要多页面共用nim实例,可将实例存储在全局变量,如果实例共享,nim.on回调中涉及到的变量要注意作用域,因为当从别的页面返回nim.on的页面时,如果还复用之前的实例,那么回调中的变量都还是上个实例的,不会随着当前新实例动态响应。解决这种问题有两个方法:
- 将回调中的方法放在全局变量中,比如vuex等。
- 实例初始化和事件监听,放在单独的js文件中,页面上调用,然后,在回调方法中设置类似event bus, 回调中发送消息,在页面上订阅发送的消息,触发页面上的回调。
1.1.3 各种消息发送方式
1.1.4 历史记录查询方式
1.1.5 在线消息发布与订阅
一端发布,另一端订阅,可单独从一端向另一端发送状态消息,而不是通过消息收发的方式,在历史消息中不会查到。
// 发布await nim.event.publishEvent({ type: 1, //传入1即可。 value: 10001, ext: \'hello world\', vaildTime: 60, sync: false,});// 订阅await nim.event.subscribeEvent({ type: 1, accounts: [\'cs3\', \'cs4\'], subscribeTime: 70, sync: true,})// 订阅端,收到发布的消息nim.on(\'pushEvents\', function(obj) { // 收到了发布的事件})
1.2 小程序端(相关注意事项,借鉴web端)
1.2.1 安装sdk
借鉴web端,可使用npm安装,然后import引入NIM_MINIAPP_SDK.js,也可将js放入本地文件夹,不npm安装。
注意如果是小程序,sdk在子包引入的话,在mainfest的mp-weixin的配置中,添加\"optimization\":{“subPackages”:true},这样分包中存放的js,会被打包到分包,不占主包大小。
注意在小程序公众平台添加以下内容:
1.2.2 初始化实例
借鉴web端,
1.2.3 其他
其他步骤借鉴web端
1.3 uniapp端(相关注意事项,借鉴web端和uniapp端)
1.3.1 安装sdk
借鉴web端,可使用npm安装,然后import引入NIM_MINIAPP_SDK.js,也可将js放入本地文件夹,不npm安装。
注意如果是小程序,sdk在子包引入的话,在mainfest的mp-weixin的配置中,添加\"optimization\":{“subPackages”:true},这样分包中存放的js,会被打包到分包,不占主包大小。
1.3.3 其他
其他步骤借鉴web端和小程序端
二、音视频通话(p2p)
文档地址
2.1 web端
web端注意查看浏览器适配问题
2.1.1 集成
可以npm安装,也可以下载中心,下载下来import引用。
2.1.2 初始化实例
//创建 client 实例rtc.client = NERTC.createClient({ appkey: \'\', //您的 App Key debug: true, //是否开启调试日志});
2.1.3 创建音视频房间(服务端)
创建房间api
2.1.4 创建频道并邀请他人
信令入门
信令邀请机制
let params = { type: 1, toAccid: \'OTHER_ACCOUNT_ID\', requestId: \'xxx\',}try { const data = await nim.signaling.callEx(params) const channelInfo = data.channelInfo console.warn(\'创建频道成功,data:\', data, \'channelId 为\', channelInfo.channelId, \'name 为\', channelInfo.name)} catch (error) { console.warn(\'创建频道失败,error:\', error) if (error.code == 10405) { console.warn(\'频道已存在,请勿重复创建\') }}
2.1.5 获取鉴权token(服务端)
token文档
2.1.6 加入房间
//加入房间rtc.client.join({ channelName: \'房间名称\', uid: uid, token: \'\' //调试模式下可设置为 null。正式上线前设置为相应的 Token,具体请参考 \"Token 鉴权\" 章节。}).then((obj) => { console.info(\'加入房间成功...\') //初始化本地流,并且发布 initLocalStream() //后面介绍说明})
// 加入房间后初始化本地流并且发布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 成功, 开始发布视频流\') }) })}
2.1.7 信令监听
信令错误码
const signalingEventList = [ \'signalingClose\', // 收到频道关闭事件 \'signalingJoin\', // 收到成员加入频道事件 \'signalingInvite\', // 收到邀请事件 \'signalingCancelInvite\', // 收到取消邀请事件 \'signalingReject\', // 收到拒绝邀请事件--被拒绝此处需要离开音视频房间rtc.client.leave(),关闭信令nim.signaling.close(params),发送离开房间消息 \'signalingAccept\', // 收到接受邀请事件 \'signalingLeave\', // 收到退出频道事件 \'signalingCustomCommand\', // 收到自定义控制指令事件 \'singalingSyncChannels\' // 初始化时同步获取到已加入的频道列表,触发此事件]signalingEventList.forEach((key: any) => { nim.signaling.on(key, (res) => { console.log(`Receive ${key} event:`, res ? JSON.parse(JSON.stringify(res)) : res); });})
2.1.8 音视频监听
//远端用户加入房间通知回调,建议在收到此回调后再进行设置远端视图等的操作rtc.client.on(\'peer-online\', evt => { console.log(`${evt.uid} 加入房间`) // 此处可以开启云录制https://doc.yunxin.163.com/nertc/server-apis/DI2OTE0ODI?platform=server})//远端用户退出房间通知回调rtc.client.on(\'peer-leave\', evt => { console.log(`${evt.uid} 退出房间`) // 对方离开,可以音视频离开房间,关闭信令,发送离开消息})//远端用户推流/停止推流通知回调,建议在收到此回调后再进行订阅或取消订阅音视频流的操作rtc.client.on(\"stream-added\", (evt)=>{ console.log(`远端${evt.stream.getId()}发布了 ${evt.mediaType} 流`) var remoteStream = evt.stream; console.log(\'收到别人的发布消息: \', remoteStream.streamID, \'mediaType: \', evt.mediaType) //设置要订阅音频或者视频 remoteStream.setSubscribeConfig({ audio: true,//订阅麦克风音频 audioSlave: true,//订阅音频辅流 video: true,//订阅视频 screenShare: true,//订阅屏幕共享 highOrLow: NERTC.STREAM_TYPE.HIGH,//订阅大流 }) //发起订阅 rtc.client.subscribe(remoteStream).then(()=>{ console.log(\'发起订阅对端成功\') })});rtc.client.on(\"stream-removed\", (evt)=>{ // 远端流停止,则关闭渲染 evt.stream.stop(evt.mediaType);});// 远端订阅成功后,可进一步在 Client.on(\"stream-subscribed\") 回调中调用 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 }) // 注意浏览器自动播放受限处理https://doc.yunxin.163.com/nertc/guide/jM3NDE0NTI?platform=web }) })
2.1.9 离开房间
try { // 使用 await 等待 leave 方法完成 await rtc.client.leave(); console.log(\'成功离开房间\'); } catch (error) { // 处理 leave 方法可能抛出的异常 console.error(\'离开房间失败\', error); }
2.1.10 信令接收端
需要提前初始化好即时通讯和音视频通话实例,并监听信令signalingInvite事件,在事件回调中,打开音视频页面,
点击接听按钮后:
- 订阅远端流
client.on(\'stream-added\', ({uid, mediaType, isBigNumber}) => { //注意:如果对方开启了音频和视频,SDK 会触发 stream-added 两次,两次的 mediaType 不同分别为\'audio\'、\'video\',需要用户两次都调用 subscribe 方法去服务器订阅,否则服务器会不转发 // mediaType 可以为 \'audio\': 音频、\'video\': 视频、\'slaveAudio\': 音频辅流、\'screenShare\':视频辅流,一般为屏幕共享,订阅完成后:audio 和 video 使用相同的 url,slaveAudio 和 screenShare 各自单独一个 url //如果对端 uid 超出了 number 精度范围,isBigNumber 值为 true,uid 为 String 类型,否则 isBigNumber 为 false,uid 为 number 类型 console.log(`${uid} 发布了自己的 ${mediaType} 数据 `) client.subscribe(uid, mediaType).then(data => { console.log(\'订阅别人成功,获取到拉流地址: \', data.url) //业务层将拉流 url 设置到对应 live-player 组件中 //注意: 同一个 uid,两次订阅(分别订阅 audio、video)返回的 url 是相同的的,可以重复更新相同 live-player 组件的 url,没有什么影响 //注意: 同一个 uid,如果对方同时发布了 \'audio\': 音频、\'video\': 视频、\'slaveAudio\': 音频辅流、\'screenShare\',这 4 中类型的媒体数据,理论上需要 4 次订阅,其中订阅 audio、video 返回的 url 是相同的的,可以重复更新相同 live-player 组件的 url,使用同一个 live-player 组件,slaveAudio 需要单独一个 live-player 组件,screenShare 也需要单独一个 live-player 组件,此时有 3 个 live-player 组件去拉取对端所有类型的媒体 }).catch(e => { console.log(\'订阅别人失败,原因: \', e) })})
- 接受邀请并加入信令频道
let params = { channelId: \'YOUR_CHANNELID\', // 邀请者的账号 ID fromAccid: \'THE_INVITOR\', requestId: \'TARGET_REQURESTID\'}try { let data = await nim.signaling.joinAndAccept(params) console.warn(\'接受邀请并加入成功,data\', data)} catch (error) { console.warn(\'接受邀请并加入失败,error:\', error) switch (error.code) { case 10407: console.warn(\'已经在频道内\') break case 10419: console.warn(\'频道人数超限\') break case 10417: console.warn(\'频道成员uid冲突了\') break case 10420: console.warn(\'当前账号在其他端已经登录,并且已经在频道内\') break case 10404: console.warn(\'频道不存在\') break }}
- 获取鉴权token(服务端)
- 加入房间
- 发布本地流
let mediaType = \'\'//也可以设置为\'audio\'或者\'video\'。如果是空字符串\'\',表示同时发布 audio 音频流和 video 视频流client.publish().then(url => { console.log(\'推流成功, 获取推流地址: \', url); // 设置推流player url //mediaType 不同分别为\'audio\'、\'video\',目前 SDK 的逻辑是\'audio\'和\'video\'使用相同的推流 url,如果开发分别使用 mediaType 为\'audio\'或者\'video\'作为参数,两次调用 publish()接口去推流,得到的 url 是相同的,因此建议用户如果加入房间后就需要同时开启音频和视频时,将 mediaType 设置为\'\',中途需要单独控制音频和视频的开关时,在设置 mediaType 为具体的参数 //业务层将推流 url 设置的 live-pusher 组件中}).catch(e => { console.log(\'推流失败,原因: \', e.message);})
点击拒绝按钮后:
信令拒绝,并发送拒绝消息
let params = { channelId: \'YOUR_CHANNELID\', fromAccid: \'THE_INVITOR\', requestId: \'TARGET_REQURESTID\',}try { await nim.signaling.reject(params) console.warn(\'拒绝邀请成功\')} catch (error) { console.warn(\'拒绝邀请失败,error:\', error) switch (error.code) { case 10408: console.warn(\'邀请不存在或已过期\') break case 10409: console.warn(\'邀请已经拒绝\') break case 10410: console.warn(\'邀请已经接受\') break case 10201: console.warn(\'对方不在线\') break case 10404: console.warn(\'频道不存在\') break }}
点击挂断按钮后:
await rtc.client.leave();let params = { channelId: \'YOUR_CHANNELID\',}try { await nim.signaling.leave(params) console.warn(\'离开频道成功\')} catch (error) { console.warn(\'离开频道成功,error:\', error)}
2.2 小程序端
小程序sdk集成
按照文档配置微信公众平台
引入小程序sdk, 注意分包中sdk文件的打包位置配置。其他参照web端
小程序推流和拉流需要引入小程序组件live-pusher和liver-player
关于推流和拉流的player组件封装,可以交给ai
- 发布本地流
加入房间后,发布本地流
let mediaType = \'\'//也可以设置为\'audio\'或者\'video\'。如果是空字符串\'\',表示同时发布 audio 音频流和 video 视频流client.publish().then(url => { console.log(\'推流成功, 获取推流地址: \', url); //mediaType 不同分别为\'audio\'、\'video\',目前 SDK 的逻辑是\'audio\'和\'video\'使用相同的推流 url,如果开发分别使用 mediaType 为\'audio\'或者\'video\'作为参数,两次调用 publish()接口去推流,得到的 url 是相同的,因此建议用户如果加入房间后就需要同时开启音频和视频时,将 mediaType 设置为\'\',中途需要单独控制音频和视频的开关时,在设置 mediaType 为具体的参数 //业务层将推流 url 设置的 live-pusher 组件中}).catch(e => { console.log(\'推流失败,原因: \', e.message);})
- 订阅远端流:
// 当远端流发布到房间时,会触发 stream-added 事件,您需要通过 client.on 监听该事件并在回调中订阅新加入的远端流,并且使用 live-player 组件去拉流。client.on(\'stream-added\', ({uid, mediaType, isBigNumber}) => { //注意:如果对方开启了音频和视频,SDK 会触发 stream-added 两次,两次的 mediaType 不同分别为\'audio\'、\'video\',需要用户两次都调用 subscribe 方法去服务器订阅,否则服务器会不转发 // mediaType 可以为 \'audio\': 音频、\'video\': 视频、\'slaveAudio\': 音频辅流、\'screenShare\':视频辅流,一般为屏幕共享,订阅完成后:audio 和 video 使用相同的 url,slaveAudio 和 screenShare 各自单独一个 url //如果对端 uid 超出了 number 精度范围,isBigNumber 值为 true,uid 为 String 类型,否则 isBigNumber 为 false,uid 为 number 类型 console.log(`${uid} 发布了自己的 ${mediaType} 数据 `) client.subscribe(uid, mediaType).then(data => { console.log(\'订阅别人成功,获取到拉流地址: \', data.url) //业务层将拉流 url 设置到对应 live-player 组件中 //注意: 同一个 uid,两次订阅(分别订阅 audio、video)返回的 url 是相同的的,可以重复更新相同 live-player 组件的 url,没有什么影响 //注意: 同一个 uid,如果对方同时发布了 \'audio\': 音频、\'video\': 视频、\'slaveAudio\': 音频辅流、\'screenShare\',这 4 中类型的媒体数据,理论上需要 4 次订阅,其中订阅 audio、video 返回的 url 是相同的的,可以重复更新相同 live-player 组件的 url,使用同一个 live-player 组件,slaveAudio 需要单独一个 live-player 组件,screenShare 也需要单独一个 live-player 组件,此时有 3 个 live-player 组件去拉取对端所有类型的媒体 }).catch(e => { console.log(\'订阅别人失败,原因: \', e) })})
2.3 uniapp端
uniapp sdk集成
注意如果是小程序,需要单独引入小程序sdk,,注意分包中sdk文件的打包位置配置。其他参照web端