更新时间: 2021-05-25

# 前置条件

# 服务开通
  1. 登录 开发者后台 (opens new window) 创建应用。
  2. 按下图所示位置开通音视频服务。
# 注意事项
  1. 浏览器页面地址必须为 https 协议地址,或使用 localhost 域名。
  2. RTCLib 暂未完全适配移动设备浏览器,仅能保证在少数 Android 平台 Chromium 内核版本浏览器中正常使用。
  3. RTCLib 底层依赖 RongIMLib 作为信令通道,故需保证 IM 连接正常。
  4. RTCLib v5 版本与 v3 版本可音视频互通,但 API 并不兼容,故 v3 版本用户无法平滑升级至 v5 版本
  5. RTCLib v5 版本兼容 RongIMLib 2.8 以上 或 RongIMLib 4.3 以上,不兼容 RongIMLib 3.0
# 浏览器兼容性

Web RTCLib v5.1.0 版本开始已支持大部分现代浏览器,但由于各浏览器厂商对于 WebRTC 能力实现不同,故 RTCLib 在浏览器中的功能支持情况也有所不同。

功能 音频 视频 视频小流 屏幕共享 自定义音视频
平台 浏览器 --- --- --- --- ---
Windows Chrome 57+ 57+ 63+ 72+ 57+
FireFox 56+ 56+ 56+ 66+ 56+
Edge 79+ 79+ 79+ 79+ 79+
Opera 76+ 76+ 76+
QQ 10+ 10+ 10+ 10+
360 12+ 12+ 12+ 12+ 12+
MacOSX Chrome 57+ 57+ 63+ 72+ 57+
Safari 11+ 11+ 11+ 11+
FireFox 56+ 56+ 56+ 66+ 56+
Edge 79+ 79+ 79+ 79+ 79+
Opera 46+ 46+ 46+
QQLite
iOS 14.3+ Safari
Chrome
FireFox
微信内置浏览器 微信 6.5 微信 6.5
Android 6.0+ Chrome 90+ 90+
FireFox 87+ 87+
Opera 62+ 62+
WebView
微信内置浏览器

其他已知问题说明

  • 华为 Android 设备上使用 Chrome 浏览器无法使用视频功能
  • Vivo Android 设备内置浏览器不支持 WebRTC 能力,故无法使用 RTCLib
  • 小米 Android 设备内置小米浏览器不支持 WebRTC 能力,故无法使用 RTCLib

# SDK 导入

# 安装 RongIMLib

RTCLib 5.x 需配合使用 RongIMLib 2.8 + 或 RongIMLib 4.3 +,不兼容 RongIMLib 3.0

# 安装 RTCLib
# 安装 RTCLib
npm install @rongcloud/plugin-rtc --save
已复制
1
2

# 初始化

RTCLib 是基于 RongIMLib 的插件机制所实现的插件,故在初始化 RTCLib 之前,需先初始化 RongIMLib。

此处仅说明获取 IMLib 实例部分以便于后续示例代码的阅读,关于 RongIMLib 的详细初始化过程,请当查阅 IM 集成文档

以下示例使用 Typescript 进行编码,便于开发者更好的理解相关值的类型信息。

# 建立 IM 连接
  • RTCLib 相关业务依赖 IMLib 作为信令通道,因此在进行 RTC 业务前请确保 IM 连接正常初始化 RTCLib 不依赖于 IMLib 连接成功,可在拿到 IMLib 实例后直接初始化)。
  • 以下列出了不同版本 IM SDK 的初始化过程,请根据您使用的 IM SDK 版本进行参考阅读。
# RTC 初始化
// 初始化 RCRTCClient,初始化过程推荐放在建立连接之前
const rtcClient = im.install(installer, { /*初始化参数请参考下方参数说明*/ })
已复制
1
2

参数说明

{
  /**
   * 自定义 MediaServer Url,公有云用户无需关注
   * @description
   * 1. 仅当 `location.hostname` 为 `localhost` 时,`http` 协议地址有效,否则必须使用 `https` 协议地址
   * 2. 当该值有效时,将不再从 IMLib 导航数据中获取 mediaServer 地址
   */
  mediaServer?: string,
  /**
   * 输出日志等级,通过 import { LogLevel } from '@rongcloud/plugin-rtc' 获取枚举值
   * @description
   * * 0 - DEBUG
   * * 1 - INFO
   * * 2 - WARN(default)
   * * 3 - ERROR
   */
  logLevel?: LogLevel
  /**
   * 覆盖默认的日志输出函数,便于业务层保存或上传日志
   */
  logStdout?: (logLevel: LogLevel, content: string) => void
  /**
   * 与 MediaServer 的 http 请求超时时间,单位为毫秒,默认值为 `5000`,有效值 `5000-30000`。
   * 优先级:用户配置 > 导航配置 > 默认时间。
   */
  timeout?: number,
  /**
   * 房间 Ping 间隔时长,默认 `10000` ms,有效值 `3000`-`10000`
   */
  pingGap?: number,
}
已复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 加入房间

// 加入普通音视频房间,从 5.0.7 开始增加返回 `tracks` 与 `userIds`
// * userIds - 当前已加入房间的远端人员列表
// * tracks  - 当前已发布至房间内的远端资源列表
const { code, room, userIds, tracks: remoteTracks } = await rtcClient.joinRTCRoom('roomId')

// 若加入失败,则 room、userIds、tracks 值为 undefined
if (code !== RCRTCCode.SUCCESS) {
  console.log('join living room failed:', code)
  return
}

// 注册房间事件监听器,重复注册时,仅最后一次注册有效
room.registerRoomEventListener({
  /**
   * 本端被踢出房间时触发
   * @description 被踢出房间可能是由于服务端超出一定时间未能收到 rtcPing 消息,所以认为己方离线。
   * 另一种可能是己方 rtcPing 失败次数超出上限,故而主动断线
   * @param byServer
   * 当值为 false 时,说明本端 rtcPing 超时
   * 当值为 true 时,说明本端收到被踢出房间通知
   */
  onKickOff (byServer: boolean) {
  },
  /**
   * 接收到房间信令时回调,用户可通过房间实例的 `sendMessage(name, content)` 接口发送信令
   * @param name 信令名
   * @param content 信令内容
   * @param senderUserId 发送者 Id
   * @param messageUId 消息唯一标识
   */
  onMessageReceive (name: string, content: any, senderUserId: string, messageUId: string) {
  },
  /**
   * 监听房间属性变更通知
   * @param name
   * @param content
   */
  onRoomAttributeChange (name: string, content: string) {
  },
  /**
   * 发布者禁用/启用音频
   * @param audioTrack RCRemoteAudioTrack 类实例
   */
  onAudioMuteChange (audioTrack: RCRemoteAudioTrack) {
  },
  /**
   * 发布者禁用/启用视频
   * @param videoTrack RCRemoteVideoTrack 类实例对象
   */
  onVideoMuteChange (videoTrack: RCRemoteVideoTrack) {
  },
  /**
   * 房间内其他用户新发布资源时触发
   * 如需获取加入房间之前房间内某个用户发布的资源列表,可使用 room.getRemoteTracksByUserId('userId') 获取
   * @param tracks 新发布的音轨与视轨数据列表,包含新发布的 RCRemoteAudioTrack 与 RCRemoteVideoTrack 实例
   */
  onTrackPublish (tracks: RCRemoteTrack[]) {
    // 按业务需求选择需要订阅资源,通过 room.subscribe 接口进行订阅
    const { code } = await room.subscribe(tracks)
    if (code !== RCRTCCode.SUCCESS) {
      console.log('资源订阅失败 ->', code)
    }
  },
  /**
   * 房间用户取消发布资源
   * @param tracks 被取消发布的音轨与视轨数据列表
   * @description 当资源被取消发布时,SDK 内部会取消对相关资源的订阅,业务层仅需处理 UI 业务
   */
  onTrackUnpublish (tracks: RCRemoteTrack[]) {
  },
  /**
   * 订阅的音视频流通道已建立, track 已可以进行播放
   * @param track RCRemoteTrack 类实例
   */
  onTrackReady (track: RCRemoteTrack) {
    if (track.isAudioTrack()) {
      // 音轨不需要传递播放控件
      track.play()
    } else {
      // 视轨需要一个 video 标签才可进行播放
      const element = document.createElement('video')
      document.body.appendChild(element)
      track.play(element)
    }
  },
  /**
   * 人员加入
   * @param userIds 加入的人员 id 列表
   */
  onUserJoin (userIds: string[]) {
  },
  /**
   * 人员退出
   * @param userIds
   */
  onUserLeave (userIds: string[]) {
  }
})

/**
 * 添加音量变化通知
 * @param handler 音量变化通知执行事件
 * @param gap 时间间隔,有效值为 300ms-1000ms,默认为 1000ms
 */
room.onAudioLevelChange((audioLevelReportList: {track: RCLocalAudioTrack | RCRemoteAudioTrack, audioLevel: number}[]) => {}, gap)
已复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

加入房间后,房间内可能已经存在其他参会者发布的音视轨数据,可以通过 subscribe 接口拉取这些声音或图像资源

const { code } = await room.subscribe(remoteTracks)

if (code !== RCRTCCode.SUCCESS) {
  console.log(`资源订阅失败 -> code: ${code}`)
}

// 当资源订阅成功后,等待 onTrackReady 事件回调即可进行播放
已复制
1
2
3
4
5
6
7

# 发布资源

# 资源获取
  1. 由麦克风捕获音频流
// 仅当 `code === RCRTCCode.SUCCESS` 时 audioTrack 有值
// audioTrack 为 RCMicphoneAudioTrack 类型实例
const { code, track: audioTrack } = await rtcClient.createMicrophoneAudioTrack()
已复制
1
2
3
  1. 由摄像头捕获视频流
// 仅当 `code === RCRTCCode.SUCCESS` 时 videoTrack 有值
// videoTrack 为 RCCameraVideoTrack 类型实例
const { code, track: videoTrack } = await rtcClient.createCameraVideoTrack()
已复制
1
2
3
  1. 同时捕获音视频流
// tracks 是一个数组,当 `code !== RCRTCCode.SUCCESS` 时,tracks 长度为 0
const { code, tracks } = await rtcClient.createMicrophoneAndCameraTracks()

if (code === RCRTCCode.SUCCESS) {
  // tracks 包含一个 RCMicphoneAudioTrack 实例和一个 RCCameraVideoTrack 实例
  const [ audioTrack, videoTrack ] = tracks
}
已复制
1
2
3
4
5
6
7

获取屏幕共享资源获取自定义资源 可参考进阶功能-视频管理章节

# 本地视频展示

通常情况下,尽量不要在本端播放本端采集的音频流,因为可能会引起回声问题。以下代码仅展示本地视频流播放。

// 通过 videoTrack.play 方法将 <video> 标签传递给 videoTrack 实例
videoTrack.play(videoNode)

// 播放音频时无需传参,尽量不要在本端播放本端采集的音频流,因为可能会引起回声问题
audioTrack.play()
已复制
1
2
3
4
5
# 资源发布
  1. 发布一个或多个音视频轨道数据
const { code } = await room.publish([audioTrack, videoTrack])

// 若资源发布失败
if (code !== RCRTCCode.SUCCESS) {
  console.log('资源发布失败:', code)
}
已复制
1
2
3
4
5
6
  1. 同时发布视频大小流
const { code } = await room.publish([
  {
    track: videoTrack,
    pubTiny: true // pubTiny 用于指定同时发布视频数据的同时,额外发布一个小流数据
  }
])
已复制
1
2
3
4
5
6

# 取消发布

const { code } = await room.unpublish([audioTrack, videoTrack])

if (code !== RCRTCCode.SUCCESS) {
  console.log('取消发布失败:', code)
}

// 取消发布后,业务层若不再需要播放资源,可调 destroy 方法销毁资源
// audioTrack.destroy()
// videoTrack.destroy()
已复制
1
2
3
4
5
6
7
8
9

# 取消订阅

const { code } = await room.unsubscribe([audioTrack, videoTrack])

// 取消订阅完成后,业务层移除相关的 UI 信息即可
已复制
1
2
3

# 退出房间

// room 为加入房间方法返回的实例对象
const { code } = await rtcClient.leaveRoom(room)
已复制
1
2

# 释放本地资源

资源取消发布或退出房间,需要主动释放本端获取到的音轨或视轨数据,以关闭摄像头、麦克风

videoTrack.destroy()
audioTrack.destroy()
已复制
1
2

文档是否解决您的问题 ?

如果遇到产品相关问题,您可 提交工单 寻求帮助