网易云信P2P客户端: 即时通信+音视频
一、即时通信(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端
2.1.1 集成
可以npm安装,也可以下载中心,下载下来import引用。
2.1.2 初始化实例
//创建 client 实例
rtc.client = NERTC.createClient({
appkey: '<yourAppKey>', //您的 App Key
debug: true, //是否开启调试日志
});
2.1.3 创建音视频房间(服务端)
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(服务端)
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端
更多推荐


所有评论(0)