Web SDK 开发手册

SDK 概述

网易云信 SDK 为 Web 应用提供一个完善的 IM 系统开发框架, 屏蔽掉 IM 系统的复杂的细节, 对外提供较为简洁的 API 接口, 方便第三方应用快速集成 IM 功能。 网易云信还开发了可供开发者们参考,如何使用该SDK的Web Demo:

开发准备

下载并引入 SDK 文件

babel打包

如果开发者选用 webpack/babel 来打包, 那么请使用 exclude 将 SDK 文件排除, 避免 babel 二次打包引起的错误

浏览器兼容性

云信 Web SDK (不包含实时音视频)兼容到 IE8

数据库兼容性

在支持数据库的浏览器上 SDK 会将数据缓存到数据库中, 后续同步都是增量更新, 加快初始化速度

是否支持数据库

// 通过此 `boolean` 值来查看 SDK 在某个浏览器上是否支持数据库
NIM.support.db

不使用数据库

如果开发者不想使用数据库, 那么可以设置初始化参数dbfalse来禁用数据库

var nim = NIM.getInstance({
    db: false
});

微信小程序

使用前请找技术支持开通功能

require

请查阅开发准备来下载并引入 SDK 文件

// 只使用 NIM
var NIM = require('NIM_Web_NIM_v')
// 只使用 Chatroom
var Chatroom = require('NIM_Web_Chatroom_v')

接口调用

微信小程序的大部分接口跟浏览器环境完全一致, 如有不同会额外说明, 请查阅其它章节

真机准备

微信公众品台 > 设置 > 开发设置 > 服务器配置, 配置域名白名单. 注意一个月内可申请3次修改, 请慎重修改.

设置 IM 需要的域名

设置聊天室需要的域名

依赖说明

初始化 SDK

初始化SDK

登录与登出

登录与登出

音视频通话(插件版)

备注

准备工作

兼容性要求

初始化音视频通话

请参考示例代码来初始化音视频通话, 示例代码和参数解释如下:

const netcall = Netcall.getInstance({
  nim: window.nim,
  container: document.getElementById('container'),
  remoteContainer: document.getElementById('container'),
  mirror: true
})

音视频通话事件

在初始化音视频通话之后, 在调用任何方法之前, 请先监听一些音视频通话事件, 基本上所有的音视频通话操作都是异步的, 而且这些操作会触发音视频通话的某些事件, 具体事件会在各个操作里面详细介绍.

初始化信令

Web 音视频通话依赖于 PC 插件, 所以在使用任何音视频通话功能之前, 需要先建立和 PC 插件之间的信令通道, 示例代码如下

var signalInited = false
// 信令通道初始化完毕之后, 开发者可以启用音视频通话相关的 UI, 比如说展示呼叫别人的按钮
// 信令通道初始化失败的时候, 请展示错误并禁用所有音视频通话相关的 UI
netcall.initSignal().then(() => {
  console.log('signalInited')
  signalInited = true
}).catch(err => {
  console.log('initSignalError', err)
  signalInited = false
}))
// 当信令通道断开时, 会触发 signalClosed 事件
netcall.on('signalClosed', () => {
  console.log('on signalClosed')
  signalInited = false
  hangup()
})
// 初始化过程中会通过 devices 事件回传所有的设备列表
netcall.on('devices', obj => {
  console.log('on devices', obj)
})

停止信令

当音视频通话结束之后, 需要停止信令通道, 然后禁用所有音视频通话相关的 UI, 示例代码如下

netcall.stopSignal()

发起音视频通话呼叫

在初始化信令之后, 可以发起音视频通话呼叫

const switchToAudioIfNoVideoDevice = true
let type = Netcall.DEVICE_TYPE_VIDEO
let callTimer
netcall.getDevicesOfType(type).then(obj => {
  // 在没有摄像头设备的时候切换到音频通话
  if (type === Netcall.NETCALL_TYPE_VIDEO && switchToAudioIfNoVideoDevice && !obj.devices.length) {
    type = Netcall.NETCALL_TYPE_AUDIO
  }
  netcall.call({
    type,
    account: 'callee',
    webrtcEnable: true,
    pushConfig: {
      // enable: true,
      // needBadge: true,
      // needPushNick: true,
      pushContent: '推送内容',
      custom: JSON.stringify({
        key: 'value'
      })
    },
    sessionConfig: {

    }
  }).then(obj => {
    console.log('call success', obj)
  }, err => {
    // 被叫不在线
    if (err.code === 11001) {
      console.log('callee offline', err)
    }
  })
  // 设置超时计时器
  callTimer = setTimeout(() => {
    if (!netcall.callAccepted) {
      console.log('超时未接听, hangup')
      hangup()
    }
  }, 1000 * 30)
})

清理音视频通话呼叫超时计时器

clearCallTimer () {
  clearTimeout(callTimer)
}

挂断音视频通话

function resetWhenHangup () {
  beCalledInfo = null
  beCalling = false
  clearCallTimer()
  netcall.stopLocalStream()
  netcall.stopRemoteStream()
  netcall.stopDeviceAudioIn()
  netcall.stopDeviceAudioOutLocal()
  netcall.stopDeviceAudioOutChat()
  netcall.stopDeviceVideo()
},
function hangup () {
  netcall.hangup()
  resetWhenHangup()
},

监听挂断音视频通话

当一方挂断之后, 另一方会收到 hangup 事件, 此时做一些清理工作即可

netcall.on('hangup', obj => {
  console.log('on hangup', obj)
  resetWhenHangup()
})

被呼叫

被叫用户在初始化音视频通话之后可以监听被呼叫的事件, 然后展示接听和挂断按钮

let beCalling = false
let beCalledInfo = null
netcall.on('beCalling', obj => {
  console.log('on beCalling', obj)
  // 获取通话标识符 channelId, 每一通会话的 channelId 都不一样
  const {channelId} = obj
  // 通知对方自己已经收到此次通话的请求
  netcall.control({
    channelId,
    command: Netcall.NETCALL_CONTROL_COMMAND_START_NOTIFY_RECEIVED
  })
  // 只有在没有通话并且没有被叫的时候才记录被叫信息, 否则直接挂断
  if (!netcall.calling && !beCalling) {
    beCalling = true
    beCalledInfo = obj
  } else {
    let busy = false
    if (netcall.calling) {
      busy = netcall.notCurrentChannelId(obj)
    } else if (beCalling) {
      busy = beCalledInfo.channelId !== channelId
    }
    // 如果忙, 那么挂断并通知对方自己忙
    if (busy) {
      netcall.control({
        channelId,
        command: Netcall.NETCALL_CONTROL_COMMAND_BUSY
      })
      netcall.response({
        accepted: false,
        beCalledInfo: obj
      })
    }
  }
})

拒绝音视频通话被呼叫

可以先通知对方自己忙, 拒绝的时候需要回传在 beCalling 事件里面接收到的对象

netcall.control({
  channelId: beCalledInfo.channelId,
  command: Netcall.NETCALL_CONTROL_COMMAND_BUSY
})
netcall.response({
  accepted: false,
  beCalledInfo: beCalledInfo
})
beCalledInfo = null
beCalling = false

监听拒绝音视频通话被呼叫

当被叫拒绝音视频通话被呼叫之后, 主叫会收到 callRejected 事件, 一般来讲需要进行如下操作

netcall.on('callRejected', obj => {
  console.log('on callRejected', obj)
  clearCallTimer()
  netcall.stopLocalStream()
  netcall.stopRemoteStream()
})

接听音视频通话被呼叫

beCalling = false
netcall.initSignal().then(() => {
  return netcall.response({
    accepted: true,
    beCalledInfo: beCalledInfo,
    sessionConfig: sessionConfig
  })
}).catch(err => {
  netcall.control({
    channelId: beCalledInfo.channelId,
    command: Netcall.NETCALL_CONTROL_COMMAND_BUSY
  })
  hangup()
  beCalledInfo = null
  console.log('接听失败', err)
})

监听接听音视频通话被呼叫

netcall.on('callAccepted', obj => {
  console.log('on callAccepted', obj)
  clearCallTimer()
  if (obj.type === Netcall.NETCALL_TYPE_VIDEO) {
    startDeviceAudioIn()
    startDeviceAudioOutChat()
    startDeviceVideo()
    netcall.startLocalStream()
    netcall.startRemoteStream()
  } else {
    startDeviceAudioIn()
    startDeviceAudioOutChat()
    stopDeviceVideo()
  }
})
netcall.on('deviceStatus', obj => {
  console.log('on deviceStatus', obj)
})
netcall.on('streamResize', obj => {
  console.log('on streamResize', obj)
})
netcall.on('remoteStreamResize', obj => {
  console.log('on remoteStreamResize', obj)
})

监听音视频通话被叫操作多端同步通知

假如你在多台设备上登录了同一个账号, 此时如果被呼叫, 那么所有的设备都会收到 beCalling 事件, 当你在某台设备接听或者拒绝之后, 其它设备会收到这个操作的通知, 名字叫 callerAckSync, 收到此事件后一般来讲需要隐藏相应的被呼叫界面

netcall.on('callerAckSync', obj => {
  console.log('on callerAckSync', obj)
  if (beCalledInfo && obj.channelId === beCalledInfo.channelId) {
    beCalledInfo = false
    beCalling = false
  }
})

设置自己画面的尺寸

最终显示的画面不大于所设置的宽和高 裁剪: cut: true(默认值), 画面按照提供的宽高等比例裁剪,返回裁剪后的实际大小 cut: false, 画面不进行裁剪, 返回按原始比例放大缩小后的实际宽高

netcall.setVideoViewSize({
  width: 300,
  height: 300,
  cut: true
})

设置对方画面的尺寸

最终显示的画面不大于所设置的宽和高 裁剪: cut: true(默认值), 画面按照提供的宽高等比例裁剪,返回裁剪后的实际大小 cut: false, 画面不进行裁剪, 返回按原始比例放大缩小后的实际宽高

netcall.setVideoViewRemoteSize({
  width: 300,
  height: 300,
  cut: true
})

启动麦克风设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioIn () {
  netcall.startDevice({
    type: Netcall.DEVICE_TYPE_AUDIO_IN,
    device
  }).then(() => {
    // 通知对方自己开启了麦克风
    netcall.control({
      command: Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON
    })
  }).catch(() => {
    console.log('启动麦克风失败')
  })
}

停止麦克风设备

function stopDeviceAudioIn () {
  netcall.stopDevice(Netcall.DEVICE_TYPE_AUDIO_IN).then(() => {
    // 通知对方自己关闭了麦克风
    netcall.control({
      command: Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF
    })
  })
}

启动播放自己声音的设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioOutLocal () {
  netcall.startDevice({
    type: Netcall.DEVICE_TYPE_AUDIO_OUT_LOCAL,
    device
  }).catch(() => {
    console.log('播放自己的声音失败')
  })
}

停止播放自己声音的设备

function stopDeviceAudioOutLocal () {
  netcall.stopDevice(Netcall.DEVICE_TYPE_AUDIO_OUT_LOCAL)
}

启动播放对方声音的设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioOutChat () {
  netcall.startDevice({
    type: Netcall.DEVICE_TYPE_AUDIO_OUT_CHAT,
    device
  }).catch(() => {
    console.log('播放对方的声音失败')
  })
}

停止播放对方声音的设备

function stopDeviceAudioOutChat () {
  netcall.stopDevice(Netcall.DEVICE_TYPE_AUDIO_OUT_CHAT)
}

启动摄像头设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备 通过参数 width 和 height 来设置摄像头捕获的视频的最大宽度和高度

function startDeviceVideo () {
  netcall.startDevice({
    type: Netcall.DEVICE_TYPE_VIDEO,
    width: 300,
    height: 300
  }).then(() => {
    // 通知对方自己开启了摄像头
    netcall.control({
      command: Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON
    })
  }).catch(() => {
    // 通知对方自己的摄像头不可用
    netcall.control({
      command: Netcall.NETCALL_CONTROL_COMMAND_SELF_CAMERA_INVALID
    })
    console.log('启动摄像头失败')
  })
}

停止摄像头设备

stopDeviceVideo () {
  netcall.stopDevice(Netcall.DEVICE_TYPE_VIDEO).then(() => {
    // 通知对方自己关闭了摄像头
    netcall.control({
      command: Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF
    })
  })
}

是当前会话的channelId

// obj 需包含 channelId 属性
netcall.isCurrentChannelId(obj)

不是当前会话的channelId

// obj 需包含 channelId 属性
netcall.notCurrentChannelId(obj)

获取指定类型的所有设备

此接口返回 Promise, 可选类型有

netcall.getDevicesOfType(type)

开启本地视频流

开启之后, 会接收并绘制自己的视频数据

netcall.startLocalStream()

停止本地视频流

停止之后, 会停止接收并绘制自己的视频数据

netcall.stopLocalStream()

开启远程视频流

开启之后, 会接收并绘制对方的视频数据

netcall.startRemoteStream()

停止远程视频流

停止之后, 会停止接收并绘制对方的视频数据

netcall.stopRemoteStream()

发送音视频通话控制指令

netcall.control({
  channelId,
  command: Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON
})

监听音视频通话控制指令

netcall.on('control', obj => {
  // 如果不是当前通话的指令, 直接丢掉
  if (netcall.notCurrentChannelId(obj)) {
    return
  }
  console.log('on control', obj)
  const {type} = obj
  switch (type) {
    // NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON 通知对方自己打开了音频
    case Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON:
      console.log('对方打开了音频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF 通知对方自己关闭了音频
    case Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF:
      console.log('对方关闭了音频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON 通知对方自己打开了视频
    case Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON:
      console.log('对方打开了视频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF 通知对方自己关闭了视频
    case Netcall.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF:
      console.log('对方关闭了视频')
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO 请求从音频切换到视频
    case Netcall.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO:
      agreeSwitchAudioToVideo()
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_REJECT 拒绝从音频切换到视频
    case Netcall.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_REJECT:
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE 同意从音频切换到视频
    case Netcall.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE:
      switchAudioToVideo()
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO 从视频切换到音频
    case Netcall.NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO:
      switchVideoToAudio()
      break
    // NETCALL_CONTROL_COMMAND_BUSY 占线
    case Netcall.NETCALL_CONTROL_COMMAND_BUSY:
      console.log('对方忙')
      break
    // NETCALL_CONTROL_COMMAND_SELF_CAMERA_INVALID 自己的摄像头不可用
    // NETCALL_CONTROL_COMMAND_SELF_ON_BACKGROUND 自己处于后台
    // NETCALL_CONTROL_COMMAND_START_NOTIFY_RECEIVED 告诉发送方自己已经收到请求了(用于通知发送方开始播放提示音)
    // NETCALL_CONTROL_COMMAND_NOTIFY_RECORD_START 通知对方自己开始录制视频了
    // NETCALL_CONTROL_COMMAND_NOTIFY_RECORD_STOP 通知对方自己结束录制视频了
  }
})

/*
 * 音视频通话切换示例函数 begin
 */

function askSwitchVideoToAudio () {
  // 通知对方从视频切换到音频, 不需要同意直接切
  this.netcall.control({
    command: Netcall.NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO
  })
  switchVideoToAudio()
}
function switchVideoToAudio () {
  stopDeviceVideo()
  netcall.switchVideoToAudio()
  netcall.stopLocalStream()
  netcall.stopRemoteStream()
}
function askSwitchAudioToVideo () {
  // 请求从音频切换到视频
  netcall.control({
    command: Netcall.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO
  })
}
function agreeSwitchAudioToVideo () {
  // 同意从音频切换到视频
  netcall.control({
    command: Netcall.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE
  })
  switchAudioToVideo()
}
function switchAudioToVideo () {
  netcall.switchAudioToVideo()
  startDeviceVideo()
  netcall.startLocalStream()
  netcall.startRemoteStream()
}

/*
 * 音视频通话切换示例函数 end
 */

从视频模式切换为音频模式

netcall.switchVideoToAudio()

从音频模式切换为视频模式

netcall.switchAudioToVideo()

设置会话的视频质量

监听接听音视频通话被呼叫之后, 可以动态设置会话的视频质量, 可选视频质量有

netcall.setSessionVideoQuality(Netcall.CHAT_VIDEO_QUALITY_NORMAL)

设置会话的视频帧率

监听接听音视频通话被呼叫之后, 可以动态设置会话的视频帧率, 可选视频帧率有

netcall.setSessionVideoFrameRate(Netcall.CHAT_VIDEO_FRAME_RATE_NORMAL)

设置会话的视频码率

监听接听音视频通话被呼叫之后, 可以动态设置会话的视频码率, >=100000 <= 5000000 有效

netcall.setSessionVideoBitrate(0)

设置采集音量

音量大小, 0-255

netcall.setCaptureVolume(100)

设置播放音量

音量大小, 0-255

netcall.setPlayVolume(100)

网络探测

netcall.netDetect().then(obj => {
  console.log('netDetect success', obj)
}, err => {
  console.log('netDetect error', err)
})

监听各种信息事件

let netStatus = {}
netcall.on('netStatus', obj => {
  netStatus = NIM.util.merge({}, netStatus, obj)
})

let statistics = {}
netcall.on('statistics', obj => {
  statistics = obj
})

let audioVolumn = {}
netcall.on('audioVolumn', obj => {
  audioVolumn = NIM.util.merge({}, audioVolumn, obj)
})

监听异常

netcall.on('error', obj => {
    if (type === 'heartBeatError') {
        // pc35秒没收到心跳包,会抛出异常(此后所有操作会返回code: 1) 需要重新建立信令
        // todo
    }
   this.addLog('on error', obj)
})

多人会议

与点对点通话的流程不同,多人会议暂不支持呼叫、推送和挂断等服务,只提供基本的预订、加入和离开会议接口。 目前呼叫方案可以参照demo使用点对点通知发送呼叫

预订会议
this.netcall.createChannel({
  channelName: channelName //必填
  custom: custom //可选
  webrtcEnable: webrtcEnable // 是否支持WebRTC方式接入,可选,默认为不开启
}).then(obj => {
  // 预定会议成功后的上层逻辑操作
  // eg: 初始化会议UI显示
  // eg: 加入会议
})

需要先预订,本人和其他人才能加入会议。

会议通过 channelName 字段做标识;可以通过扩展字段 custom 在会议的创建和加入之间传递自定义的额外信息。

同一个会议名称,只在会议使用完并且房间被销毁(所有人都离开房间)以后才可以重复使用,开发者需要保证不会出现重复预订某会议名称而不使用的情况。

预定结果通过promise的then和catch捕捉

加入会议
netcall.joinChannel({
  channelName: channelName, //必填
  type: type,
  custom: custom, //可选
  sessionConfig: sessionConfig
}).then(obj => {
  // 加入会议成功后的上层逻辑操作
  // eg: 开启摄像头
  // eg: 开启麦克风
  // eg: 开启本地流
  // eg: 设置音量采集、播放
  // eg: 设置视频画面尺寸等等,具体请参照p2p呼叫模式
})

参数解读

channelName: 会议id(必填)
type: 通话类型(音频还是视频)
sessionConfig 会话配置,具体请参考API文档
{
  videoQuality 视频分辨率
  videoFrameRate 视频帧率
  videoBitrate 视频码率
  highAudio=false 高清语音开关, 默认关闭
  recordVideo=false 视频录制开关, 默认关闭
  recordAudio=false 音频录制开关, 默认关闭
  bypassRtmp=false 推流开关, 默认关闭,推流相关配置前提开关打开
  rtmpUrl 推流地址
  rtmpRecord=false 推流录制开关, 默认关闭
  splitMode 推流的布局, 默认平铺
}

加入会议结果通过promise的then和catch捕捉

离开会议
netcall.leaveChannel().then(obj => {
  // 离开会议后的扫尾工作
})
改变自己在会议中的角色
// 观众切换到互动者
netcall.changeRoleToPlayer().then(obj => {
  // todo
})

// 互动者切换到观众
netcall.changeRoleToAudience().then(obj => {
  // todo
})
指定某用户设置是否对其静音(同p2p)
// 设置禁音
netcall.setAudioBlack(account).then(obj => {
  // todo
})

// 放开禁音
netcall.setAudioBlack(account).then(obj => {
  // todo
})

静音后将听不到该用户的声音。

需要在用户加入以后才能进行设置,该接口也可用于点对点双人通话。

第三方用户加入会议的通知
netcall.on('joinChannel', function (obj) {
  // 通知上层有其他用户加入了会议,上层做相应逻辑和UI处理
})
第三方用户离开会议的通知
netcall.on('leaveChannel', function (obj) {
  // 通知上层有其他用户离开了会议,上层做相应逻辑和UI处理
})

WebRTC(Beta)

备注

  1. 目前该版本为待优化的测试版本,欢迎开发者提供bug反馈和优化建议,让我们一起做得更好
  2. 对于WebRTC Beta版,在有 webrtc 客户端参与的房间中需要打开该开关, 如果没有 webrtc 客户端参与,不要打开该开关。
  3. 请注意:WebRTC 的音视频支持需要开启https, http模式下无法捕捉摄像头和麦克风
  4. 目前同步上线的demo只支持点对点通话,多人通话随后就到~

兼容性要求

准备工作

初始化实时音视频

请参考示例代码来初始化实时音视频, 示例代码和参数解释如下:

const netcall = WebRTC.getInstance({
  nim: window.nim,
  container: document.getElementById('container'),
  remoteContainer: document.getElementById('container')
})

实时音视频事件

在初始化实时音视频之后, 在调用任何方法之前, 请先监听一些实时音视频事件, 基本上所有的实时音视频操作都是异步的, 而且这些操作会触发实时音视频的某些事件, 具体事件会在各个操作里面详细介绍.

发起音视频呼叫

const switchToAudioIfNoVideoDevice = true
let type = WebRTC.NETCALL_TYPE_AUDIO
let callTimer

  netcall.call({
    type,
    account: 'callee',
    pushConfig: {
      // enable: true,
      // needBadge: true,
      // needPushNick: true,
      pushContent: '推送内容',
      custom: JSON.stringify({
        key: 'value'
      })
    },
    sessionConfig: {

    }
  }).then(obj => {
    console.log('call success', obj)
    // 设置超时计时器
    callTimer = setTimeout(() => {
      if (!netcall.callAccepted) {
        console.log('超时未接听, hangup')
        hangup()
      }
    }, 1000 * 30)
  }, err => {
    // 被叫不在线
    if (err.code === 11001) {
      console.log('callee offline', err)
    }
  })

清理音视频呼叫超时计时器

clearCallTimer () {
  clearTimeout(callTimer)
}

挂断音视频通话

function resetWhenHangup () {
  beCalledInfo = null
  beCalling = false
  clearCallTimer()
  netcall.stopLocalStream()
  netcall.stopRemoteStream()
  netcall.stopDeviceAudioIn()
  netcall.stopDeviceAudioOutLocal()
  netcall.stopDeviceAudioOutChat()
  netcall.stopDeviceVideo()
},
function hangup () {
  netcall.hangup()
  resetWhenHangup()
},

监听挂断音视频通话

当一方挂断之后, 另一方会收到 hangup 事件, 此时做一些清理工作即可

netcall.on('hangup', obj => {
  console.log('on hangup', obj)
  resetWhenHangup()
})

被呼叫

被叫用户在初始化实时音视频之后可以监听被呼叫的事件, 然后展示接听和挂断按钮

let beCalling = false
let beCalledInfo = null
netcall.on('beCalling', obj => {
  console.log('on beCalling', obj)
  // 获取通话标识符 channelId, 每一通会话的 channelId 都不一样
  const {channelId} = obj
  // 通知对方自己已经收到此次通话的请求
  netcall.control({
    channelId,
    command: WebRTC.NETCALL_CONTROL_COMMAND_START_NOTIFY_RECEIVED
  })
  // 只有在没有通话并且没有被叫的时候才记录被叫信息, 否则直接挂断
  if (!netcall.calling && !beCalling) {
    beCalling = true
    beCalledInfo = obj
  } else {
    // 通知呼叫方我方繁忙
    netcall.control({
        channelId: channelId,
        command: Netcall.NETCALL_CONTROL_COMMAND_BUSY
    });
  }
})

拒绝音视频被呼叫

可以先通知对方自己忙, 拒绝的时候需要回传在 beCalling 事件里面接收到的对象

netcall.control({
  channelId: beCalledInfo.channelId,
  command: WebRTC.NETCALL_CONTROL_COMMAND_BUSY
})
netcall.response({
  accepted: false,
  beCalledInfo: beCalledInfo
})
beCalledInfo = null
beCalling = false

监听拒绝音视频被呼叫

当被叫拒绝音视频被呼叫之后, 主叫会收到 callRejected 事件, 一般来讲需要进行如下操作

netcall.on('callRejected', obj => {
  console.log('on callRejected', obj)
  clearCallTimer()
})

接听音视频被呼叫

beCalling = false
netcall.response({
  accepted: true,
  beCalledInfo: beCalledInfo,
  sessionConfig: sessionConfig
}).catch(err => {
  netcall.control({
    channelId: beCalledInfo.channelId,
    command: WebRTC.NETCALL_CONTROL_COMMAND_BUSY
  })
  hangup()
  beCalledInfo = null
  console.log('接听失败', err)
})

监听接听音视频被呼叫

netcall.on('deviceStatus', obj => { console.log('on deviceStatus', obj) })

netcall.on('joinChannel', obj => { console.log('on joinChannel', obj) netcall.startRemoteStream() })


### <span id="WebRTC_监听音视频被叫操作多端同步通知">监听音视频被叫操作多端同步通知</span>

假如你在多台设备上登录了同一个账号, 此时如果被呼叫, 那么所有的设备都会收到 `beCalling` 事件, 当你在某台设备接听或者拒绝之后, 其它设备会收到这个操作的通知, 名字叫 `callerAckSync`, 收到此事件后一般来讲需要隐藏相应的被呼叫界面

```js
netcall.on('callerAckSync', obj => {
  console.log('on callerAckSync', obj)
  if (beCalledInfo && obj.channelId === beCalledInfo.channelId) {
    beCalledInfo = false
    beCalling = false
  }
})

设置自己画面的尺寸

最终显示的画面不大于所设置的宽和高 裁剪: cut: true(默认值), 画面按照提供的宽高等比例居中裁剪,返回裁剪后的实际大小 cut: false, 画面不进行裁剪, 返回按原始比例放大缩小后的实际宽高

netcall.setVideoViewSize({
  width: 300,
  height: 300,
  cut: true
})

设置对方画面的尺寸

最终显示的画面不大于所设置的宽和高 裁剪: cut: true(默认值), 画面按照提供的宽高等比例居中裁剪,返回裁剪后的实际大小 cut: false, 画面不进行裁剪, 返回按原始比例放大缩小后的实际宽高

netcall.setVideoViewRemoteSize({
  width: 300,
  height: 300,
  cut: true
})

启动麦克风设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioIn () {
  return netcall.startDevice({
    type: WebRTC.DEVICE_TYPE_AUDIO_IN,
    device
  }).then(() => {
    // 通知对方自己开启了麦克风
    netcall.control({
      command: WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON
    })
  }).catch(() => {
    console.log('启动麦克风失败')
  })
}

停止麦克风设备

function stopDeviceAudioIn () {
  return netcall.stopDevice(WebRTC.DEVICE_TYPE_AUDIO_IN).then(() => {
    // 通知对方自己关闭了麦克风
    netcall.control({
      command: WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF
    })
  })
}

启动播放自己声音的设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioOutLocal () {
  return netcall.startDevice({
    type: WebRTC.DEVICE_TYPE_AUDIO_OUT_LOCAL,
    device
  }).catch(() => {
    console.log('播放自己的声音失败')
  })
}

停止播放自己声音的设备

function stopDeviceAudioOutLocal () {
  return netcall.stopDevice(WebRTC.DEVICE_TYPE_AUDIO_OUT_LOCAL)
}

启动播放对方声音的设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceAudioOutChat () {
  return netcall.startDevice({
    type: WebRTC.DEVICE_TYPE_AUDIO_OUT_CHAT,
    device
  }).catch(() => {
    console.log('播放对方的声音失败')
  })
}

停止播放对方声音的设备

function stopDeviceAudioOutChat () {
  return netcall.stopDevice(WebRTC.DEVICE_TYPE_AUDIO_OUT_CHAT)
}

启动摄像头设备

可以传入 device 参数来指定开启某个特定设备; 如果不传 device 参数, 那么默认启动第一个此类设备

function startDeviceVideo () {
  return netcall.startDevice({
    type: WebRTC.DEVICE_TYPE_VIDEO
  }).then(() => {
    // 通知对方自己开启了摄像头
    netcall.control({
      command: WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON
    })
  }).catch(() => {
    // 通知对方自己的摄像头不可用
    netcall.control({
      command: WebRTC.NETCALL_CONTROL_COMMAND_SELF_CAMERA_INVALID
    })
    console.log('启动摄像头失败')
  })
}

停止摄像头设备

stopDeviceVideo () {
  return netcall.stopDevice(WebRTC.DEVICE_TYPE_VIDEO).then(() => {
    // 通知对方自己关闭了摄像头
    netcall.control({
      command: WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF
    })
  })
}

是当前会话的channelId

// obj 需包含 channelId 属性
netcall.isCurrentChannelId(obj)

不是当前会话的channelId

// obj 需包含 channelId 属性
netcall.notCurrentChannelId(obj)

获取指定类型的所有设备

此接口返回 Promise, 可选类型有

netcall.getDevicesOfType(type)

开始WebRtc音视频连接

开始建立WebRTC连接,发送和接收音视频数据

netcall.startRtc().then(()=>{
  // todo
}).catch(err=>{
  console.log(err)
})

开启本地视频流

开启之后, 会显示自己的视频数据

netcall.startLocalStream()

停止本地视频流

停止之后, 会停止显示自己的视频数据

netcall.stopLocalStream()

开启远程视频流

开启之后, 会显示对方的视频数据 多人会议需要传入目标account或者uid和node节点

// 这里传入account或者uid都可以
netcall.startRemoteStream({
  // account: obj.account
  uid: obj.uid,
  node: document.querySelector('#video')
})

停止远程视频流

停止之后, 会停止显示对方的视频数据 多人会议需要传入目标account或者uid

// 这里传入account或者uid都可以
netcall.stopRemoteStream({
  // account: obj.account
  uid: obj.uid
})

暂停播放自己的视频画面

netcall.suspendLocalStream()

继续播放自己的视频画面

netcall.resumeLocalStream()

暂停播放对方的视频画面

多人会议需要传入目标account或者uid

// 这里传入account或者uid都可以
netcall.suspendRemoteStream({
  // account: obj.account
  uid: obj.uid
})

继续播放对方的视频画面

多人会议需要传入目标account或者uid

// 这里传入account或者uid都可以
netcall.resumeRemoteStream({
  // account: obj.account
  uid: obj.uid
})

发送音视频通话控制指令

netcall.control({
  channelId,
  command: WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON
})

监听音视频通话控制指令

netcall.on('control', obj => {
  // 如果不是当前通话的指令, 直接丢掉
  if (netcall.notCurrentChannelId(obj)) {
    return
  }
  console.log('on control', obj)
  const {type} = obj
  switch (type) {
    // NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON 通知对方自己打开了音频
    case WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_ON:
      console.log('对方打开了音频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF 通知对方自己关闭了音频
    case WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_AUDIO_OFF:
      console.log('对方关闭了音频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON 通知对方自己打开了视频
    case WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_ON:
      console.log('对方打开了视频')
      break
    // NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF 通知对方自己关闭了视频
    case WebRTC.NETCALL_CONTROL_COMMAND_NOTIFY_VIDEO_OFF:
      console.log('对方关闭了视频')
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO 请求从音频切换到视频
    case WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO:
      agreeSwitchAudioToVideo()
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_REJECT 拒绝从音频切换到视频
    case WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_REJECT:
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE 同意从音频切换到视频
    case WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE:
      switchAudioToVideo()
      break
    // NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO 从视频切换到音频
    case WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO:
      switchVideoToAudio()
      break
    // NETCALL_CONTROL_COMMAND_BUSY 占线
    case WebRTC.NETCALL_CONTROL_COMMAND_BUSY:
      console.log('对方忙')
      break
    // NETCALL_CONTROL_COMMAND_SELF_CAMERA_INVALID 自己的摄像头不可用
    // NETCALL_CONTROL_COMMAND_SELF_ON_BACKGROUND 自己处于后台
    // NETCALL_CONTROL_COMMAND_START_NOTIFY_RECEIVED 告诉发送方自己已经收到请求了(用于通知发送方开始播放提示音)
  }
})

/*
 * 音视频切换示例函数 begin
 */

function askSwitchVideoToAudio () {
  // 通知对方从视频切换到音频, 不需要同意直接切
  this.netcall.control({
    command: WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_VIDEO_TO_AUDIO
  })
  switchVideoToAudio()
}
function switchVideoToAudio () {
  stopDeviceVideo()
  netcall.stopLocalStream()
  netcall.stopRemoteStream()
  netcall.switchVideoToAudio()
}
function askSwitchAudioToVideo () {
  // 请求从音频切换到视频
  netcall.control({
    command: WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO
  })
}
function agreeSwitchAudioToVideo () {
  // 同意从音频切换到视频
  netcall.control({
    command: WebRTC.NETCALL_CONTROL_COMMAND_SWITCH_AUDIO_TO_VIDEO_AGREE
  })
  switchAudioToVideo()
}
function switchAudioToVideo () {
  startDeviceVideo().then(function() {
    netcall.startLocalStream()
  }.bind(this)).then(function() {
    return netcall.switchAudioToVideo();
  }.bind(this)).then(function(){
    netcall.startRemoteStream()
  }.bind(this)).catch(function(e) {
    console.log(e);
  })
}

/*
 * 音视频切换示例函数 end
 */

从视频模式切换为音频模式

netcall.switchVideoToAudio()

从音频模式切换为视频模式

netcall.switchAudioToVideo()

设置会话的视频质量

设置会话的视频质量, 可选视频质量有

netcall.setSessionVideoQuality(WebRTC.CHAT_VIDEO_QUALITY_NORMAL)

设置会话的视频帧率

设置会话的视频帧率, 可选视频帧率有

netcall.setSessionVideoFrameRate(WebRTC.CHAT_VIDEO_FRAME_RATE_NORMAL)

设置采集音量

音量大小, 0-255

netcall.setCaptureVolume(100)

设置播放音量

音量大小, 0-255 多人会议需要传入目标account或者uid

netcall.setPlayVolume({
  account: 'testaccount'
  volume: 100
})

设置目标静音

设置后,不能听到目标声音 多人会议需要传入目标account,不设置则操作所有远程流

netcall.setAudioBlack(account)

设置目标非静音

设置后,能听到目标声音 多人会议需要传入目标account,不设置则操作所有远程流

netcall.setAudioStart(account)

显示目标画面

设置后,能看到目标视频画面 多人会议需要传入目标account,不设置则操作所有远程流

netcall.setVideoShow(account)

禁止显示目标画面

设置后,不能看到目标视频画面 多人会议需要传入目标account,不设置则操作所有远程流

netcall.setVideoBlack(account)

开启video录制

录制目标帐号的视频和音频,不传参则录制当前登录帐号的视频和音频 目前只支持webm格式的视频,可以用chrome浏览器打开

netcall.startRecordMp4({account}).then().catch()

停止video录制

结束视频和音频录制,弹框选择保存本地

netcall.stopRecordMp4().then().catch()

开启混音录制

混合本地和所有远程音频流,进行录制并保存 目前只支持webm格式的音频,可以用chrome浏览器打开

netcall.startRecordAac().then().catch()

停止混音录制

结束混音录制,弹框选择保存本地

netcall.stopRecordAac().then().catch()

开启多人会议

与点对点通话的流程不同,多人会议暂不支持呼叫、推送和挂断等服务,只提供基本的预订、加入和离开会议接口。 目前呼叫方案可以参照demo使用点对点通知发送呼叫

预订会议
this.netcall.createChannel({
  channelName: channelName //必填
  custom: custom //可选
}).then(obj => {
  // 预定会议成功后的上层逻辑操作
  // eg: 初始化会议UI显示
  // eg: 加入会议
}).catch()

需要先预订,本人和其他人才能加入会议。

会议通过 channelName 字段做标识;可以通过扩展字段 custom 在会议的创建和加入之间传递自定义的额外信息。

同一个会议名称,只在会议使用完并且房间被销毁(所有人都离开房间)以后才可以重复使用,开发者需要保证不会出现重复预订某会议名称而不使用的情况。

预定结果通过promise的then和catch捕捉

加入会议
netcall.joinChannel({
  channelName: channelName,
  sessionConfig: sessionConfig
}).then(obj => {
  // 加入会议成功后的上层逻辑操作
  // eg: 开启摄像头
  // eg: 开启麦克风
  // eg: 开启本地流
  // eg: 设置音量采集、播放
  // eg: 设置视频画面尺寸等等,具体请参照p2p呼叫模式
  // 下面为示例代码
  console.log('joinChannel', obj)
  if (this.role === 'player') {
    changeRoleToPlayer()
  } else {
    changeRoleToAudience()
  }

  var promise;
  if (obj.type === WebRTC.NETCALL_TYPE_VIDEO) {
    promise = startDeviceVideo()
  } else {
    promise = stopDeviceVideo()
  }
  promise.then(function () {
    setVideoViewSize()
    return startDeviceAudioIn();
  }.bind(this)).then(function () {
    setCaptureVolume()
  }.bind(this)).then(function () {
    addLog("开始webrtc连接")
    return webrtc.startRtc();
  }.bind(this)).then(function () {
    addLog("webrtc连接成功")
    return startDeviceAudioOutChat();
  }.bind(this)).catch(function (e) {
    addLog("连接出错");
    console.error(e);
    hangup()
  }.bind(this))
}, err => {
  console.error('joinChannelErr', err)
})

参数解读

channelName: 会议id(必填)
type: 通话类型(音频还是视频)
sessionConfig 会话配置,具体请参考API文档
{
  videoQuality 视频分辨率
  videoFrameRate 视频帧率
  highAudio=false 高清语音开关, 默认关闭
}

加入会议结果通过promise的then和catch捕捉

离开会议
netcall.leaveChannel().then(obj => {
  // 离开会议后的扫尾工作
})
改变自己在会议中的角色
// 观众切换到互动者
netcall.changeRoleToPlayer().then(obj => {
  // todo
})

// 互动者切换到观众
netcall.changeRoleToAudience().then(obj => {
  // todo
})

开启互动直播

多人模式中可以进行互动直播, 前提条件:

  1. 创建房间后以主播身份加入房间, 后续的连麦者才能加入房间, 否则连麦者直接加入房间失败
// 创建房间
netcall.createChannel({
  channelName: this.channelName,
  custom: this.channelCustom
}).then(obj => {
  console.log('createChannel', obj)
}, err => {
  console.log('createChannelErr', err)
})

// 主播加入房间
netcall.joinChannel({
  channelName: this.channelName,
  type,
  custom: this.channelCustom,
  sessionConfig: {
    isHost:true, //'是否是主播',
    bypassRtmp: true, //是否开启推流开关
    rtmpRecord: true, //是否开启推流录制开关
    rtmpUrl: '推流地址', //主播必须设置
    splitMode: '主播和连麦者的布局模式', // 主播必须设置
    layout: '自定义布局模式'
  }
}).then(obj => {
  console.log('joinChanel', obj)
}, err => {
  console.log('joinChannelErr', err)
})

// 连麦者加入房间
netcall.joinChannel({
  channelName: this.channelName,
  type,
  custom: this.channelCustom,
  sessionConfig: {
    isHost:false, //'非主播,必须设置false',
    bypassRtmp: true, //是否开启推流开关
    rtmpRecord: true //是否开启推流录制开关
  }
}).then(obj => {
  console.log('joinChannel', obj)
}, err => {
  console.log('joinChannelErr', err)
})

监听各种信息事件


// 通话过程中,WebRTC连接断开
netcall.on('rtcConnectFailed', event => {

})

// 设备列表
netcall.on('devices', (devices) => {

})

// 设备列表发生变化
netcall.on('deviceStatus', (devices) => {

})

// 发现新设备
netcall.on('deviceAdd', (devices) => {

})

// 有设备被移除
netcall.on('deviceRemove', (devices) => {

})

// 音量监控
netcall.on('audioVolume', (obj) => {

})

// 有人加入多人会议房间
netcall.on('joinChannel', obj => {

})

// 有人离开多人会议房间
netcall.on('leaveChannel', obj => {

})