Web SDK 开发手册

SDK 概述

网易云信 SDK 为 Web 应用提供一个完善的 IM 系统开发框架, 屏蔽掉 IM 系统的复杂的细节, 对外提供较为简洁的 API 接口, 方便第三方应用快速集成 IM 功能.

开发准备

下载并引入 SDK 文件

babel打包

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

浏览器兼容性

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

数据库兼容性

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

是否支持数据库

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

不使用数据库

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

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

依赖说明

初始化 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',
    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
  }
})

设置自己画面的尺寸

最终显示的画面不大于所设置的宽和高

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

设置对方画面的尺寸

最终显示的画面不大于所设置的宽和高

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

启动麦克风设备

可以传入 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)
})