Files
SellyCloudSDK_Android_demo/docs/SellySDK_音视频通话接入文档_Android.md

558 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SellyRTC Android SDK 接入文档
本文档用于指导 **Android App 开发者** 快速接入 SellyRTC完成一对一或多人实时音视频通话能力。
SDK 核心以 `InteractiveRtcEngine` 为中心,通过 `InteractiveRtcEngineEventHandler` 回调通话状态、用户事件、音视频状态及异常。
当前版本的互动渲染模型已经从“仅 `SurfaceViewRenderer`”扩展为“`RtcRenderTarget` 抽象 + 多种后端实现”:
- `SurfaceViewRenderer` 旧路径仍可用
- `TextureView` 已可用于本地/远端视频渲染
- 推荐在 **加入频道前** 选定本地渲染后端
---
## 目录
1. 准备工作
2. 快速开始
3. 基础通话流程
4. 常用功能
5. 屏幕分享
6. 视频帧前后处理
7. 事件回调EventHandler
8. 通话统计
9. Token 机制
10. 代理地址配置
11. 常见问题FAQ
---
## 准备工作
### 1. 环境要求
- Android 8.0+Demo `minSdk` 为 26
- 需真机运行(摄像头 / 麦克风)
### 2. 权限配置AndroidManifest.xml
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```
---
## 快速开始
### 1. SDK 初始化
在使用音视频通话功能前,需先初始化 SDK
```kotlin
SellyCloudManager.initialize(
context = applicationContext,
appId = "your-app-id"
)
```
> `initialize` 的 `appId` 参数为权威值。可选传入 `SellyCloudConfig` 配置 `vhost`、`logEnabled` 等,详见推拉流文档。
### 2. 代理地址设置(可选)
若需通过代理(如洋葱盾)连接信令服务器,在创建引擎前设置:
```kotlin
SellyCloudManager.setProxyAddress("http://127.0.0.1:12345")
```
> SDK 内部通过代理地址解析真实信令服务器 IP。不设置则使用直连。详见「代理地址配置」章节。
### 3. 创建引擎
```kotlin
val appId = getString(R.string.signaling_app_id)
val token = getString(R.string.signaling_token).takeIf { it.isNotBlank() }
val rtcEngine = InteractiveRtcEngine.create(
InteractiveRtcEngineConfig(
context = applicationContext,
appId = appId,
defaultToken = token
)
).apply {
setEventHandler(eventHandler)
setClientRole(InteractiveRtcEngine.ClientRole.BROADCASTER)
setVideoEncoderConfiguration(
InteractiveVideoEncoderConfig(
width = 640,
height = 480,
fps = 20,
minBitrateKbps = 150,
maxBitrateKbps = 850
)
)
setDefaultAudioRoutetoSpeakerphone(true)
}
```
`InteractiveRtcEngineConfig` 参数说明:
| 参数 | 类型 | 说明 |
| ---- | ---- | ---- |
| `context` | Context | 应用上下文 |
| `appId` | String | 应用 ID |
| `defaultCallType` | CallType | 默认通话类型,默认 ONE_TO_ONE |
| `defaultToken` | String? | 默认 Token |
| `signalingUrlPrefix` | String | 信令 URL 前缀,默认 `ws://` |
| `signalingUrlSuffix` | String | 信令 URL 后缀,默认 `/ws/signaling` |
> 完整 Demo 见 `example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt`。
### 4. 设置本地/远端画布
推荐使用 `InteractiveVideoCanvas(renderTarget, userId)` 新接口。
#### 4.1 SurfaceViewRenderer 旧路径
```kotlin
val localRenderer = SurfaceViewRenderer(this)
val localCanvas = InteractiveVideoCanvas(
com.sellycloud.sellycloudsdk.render.SurfaceViewRtcTarget(localRenderer),
userId
)
rtcEngine.setupLocalVideo(localCanvas)
```
```kotlin
val remoteRenderer = SurfaceViewRenderer(this)
val remoteCanvas = InteractiveVideoCanvas(
com.sellycloud.sellycloudsdk.render.SurfaceViewRtcTarget(remoteRenderer),
remoteUserId
)
rtcEngine.setupRemoteVideo(remoteCanvas)
```
#### 4.2 TextureView 路径
```kotlin
val localTextureView = com.sellycloud.sellycloudsdk.widget.AspectRatioTextureView(this)
val localCanvas = InteractiveVideoCanvas(
com.sellycloud.sellycloudsdk.render.TextureViewRtcTarget(localTextureView),
userId
)
rtcEngine.setupLocalVideo(localCanvas)
```
```kotlin
val remoteTextureView = com.sellycloud.sellycloudsdk.widget.AspectRatioTextureView(this)
val remoteCanvas = InteractiveVideoCanvas(
com.sellycloud.sellycloudsdk.render.TextureViewRtcTarget(remoteTextureView),
remoteUserId
)
rtcEngine.setupRemoteVideo(remoteCanvas)
```
兼容说明:
- `InteractiveVideoCanvas(view: SurfaceViewRenderer, userId)` 旧构造仍可用deprecated
- 推荐新接入统一走 `RtcRenderTarget`
- 当前高层互动 API 还没有直接暴露 `SurfaceTexture` 入口Android 场景推荐 `SurfaceViewRenderer``TextureView`
所有权说明:
- 调用方自己创建的 `SurfaceViewRenderer` / `TextureView`,由调用方负责释放
- SDK 只在 `setupLocalVideo` / `setupRemoteVideo` 中绑定 target`leaveChannel` 时解绑
- 调用方应在 `leaveChannel` 之后、Activity 销毁前释放自己创建的 View
### 5. 加入通话
```kotlin
val options = InteractiveChannelMediaOptions(callType = CallType.ONE_TO_ONE)
rtcEngine.joinChannel(
token = token,
channel = callId,
userId = userId,
options = options,
tokenSecret = tokenSecret,
tokenExpiresAtSec = tokenExpiresAtSec,
tokenTtlSeconds = tokenTtlSeconds
)
```
结束通话:
```kotlin
rtcEngine.leaveChannel()
```
销毁引擎:
```kotlin
InteractiveRtcEngine.destroy(rtcEngine)
```
### 6. 进阶配置Demo 未覆盖)
#### 6.1 InteractiveChannelMediaOptions 订阅控制
```kotlin
val options = InteractiveChannelMediaOptions(
callType = CallType.GROUP,
autoSubscribeAudio = true,
autoSubscribeVideo = false
)
```
#### 6.2 InteractiveVideoEncoderConfig 更多参数
可选项(按需设置):
- `codecType`:编码类型
- `bitrateMode`:码率控制模式
- `minBitrateKbps` / `maxBitrateKbps`:码率范围
- `orientationMode`:画面方向策略
- `degradationPreference`:弱网降级策略
- `mirrorMode`:镜像策略
---
## 基础通话流程
1. 初始化 SDK`SellyCloudManager.initialize`
2. 设置代理地址(可选,`SellyCloudManager.setProxyAddress`
3. 创建 `InteractiveRtcEngine`
4. 设置 `EventHandler`
5. 配置 `InteractiveVideoEncoderConfig`
6. 设置本地画布 `setupLocalVideo`(建议在 `joinChannel` 前完成,并在此阶段确定 backend
7. `joinChannel` 加入频道
8. `onUserJoined` 后设置远端画布;也可以提前为某个 `userId` 调用 `setupRemoteVideo`SDK 会在用户真正上线后自动 attach
9. 通话中进行音视频控制
10. `leaveChannel` 并释放资源
---
## 常用功能
### 本地音视频控制
```kotlin
rtcEngine.enableLocalVideo(true)
rtcEngine.enableLocalAudio(false)
```
### 切换摄像头
```kotlin
rtcEngine.switchCamera()
```
### 音频输出(扬声器 / 听筒)
```kotlin
rtcEngine.setDefaultAudioRoutetoSpeakerphone(true)
```
### 发送自定义消息
```kotlin
rtcEngine.sendMessage("hello") { error ->
// error == null 表示成功
}
```
### 按用户静音远端音频/视频
```kotlin
rtcEngine.muteRemoteAudioStream(remoteUserId, true)
rtcEngine.muteRemoteVideoStream(remoteUserId, true)
```
### 全量静音远端音视频Demo 未覆盖)
```kotlin
rtcEngine.muteAllRemoteAudioStreams(true)
rtcEngine.muteAllRemoteVideoStreams(true)
```
### 清理远端画布Demo 未覆盖)
```kotlin
rtcEngine.clearRemoteVideo(remoteUserId)
```
### 向指定用户发送消息Demo 未覆盖)
```kotlin
rtcEngine.sendMessage("hello", targetUserId) { error ->
// error == null 表示成功
}
```
---
## 屏幕分享
```kotlin
val started = rtcEngine.startScreenShare(
resultCode = resultCode,
data = data,
width = 720,
height = 1280,
fps = 15
)
```
```kotlin
rtcEngine.stopScreenShare()
```
```kotlin
val isSharing = rtcEngine.isScreenSharing()
```
> Android 14+ 屏幕捕获需要前台服务Demo 使用 `InteractiveForegroundService` 处理)。
---
## 视频帧前后处理
```kotlin
rtcEngine.setCaptureVideoProcessor(object : VideoProcessor {
override val config = VideoProcessorConfig(
preferredFormat = VideoProcessFormat.TEXTURE_2D,
mode = VideoProcessMode.READ_WRITE,
fullRewrite = true
)
override fun processTexture(input: VideoTextureFrame, outputTextureId: Int) {
// 推荐在 GPU texture 上处理采集前帧,美颜/滤镜直接写入 outputTextureId
}
})
```
```kotlin
val renderObserver = rtcEngine.addRenderVideoFrameObserver(object : VideoFrameObserver {
override val config = VideoFrameObserverConfig(
preferredFormat = VideoProcessFormat.TEXTURE_2D,
stage = VideoStage.RENDER_PRE_DISPLAY
)
override fun onTextureFrame(frame: VideoTextureFrame) {
// 远端渲染前只读观测
val userId = frame.sourceId
}
})
```
> 推荐优先使用 `TEXTURE_2D`
> - `TEXTURE_2D` 适合美颜、滤镜、AR、水印等 GPU 处理链路。
> - `I420` / `RGBA` 仅在算法必须访问 CPU 像素时再使用。
> - 对 RTC / WHIP 的 texture-backed 帧,走 CPU observer / processor 会触发额外的 texture-to-CPU 转换。
> - `VideoFrameObserverConfig` 默认仍为 `I420` 以兼容旧接入;新 RTC / WHIP 接入建议显式写 `preferredFormat = TEXTURE_2D`。
> - 完整重写输出的处理器建议设置 `fullRewrite = true`;水印/叠加类处理保留默认值即可。
>
> Demo 中的采集前美颜示例见:
> `example/src/main/java/com/demo/SellyCloudSDK/beauty/FaceUnityBeautyEngine.kt`
>
> 当前 Demo 的互动页接入见:
> `example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt`
---
## 事件回调EventHandler
```kotlin
val eventHandler = object : InteractiveRtcEngineEventHandler {
override fun onJoinChannelSuccess(channel: String, userId: String, code: Int) {}
override fun onLeaveChannel(durationSeconds: Int) {}
override fun onUserJoined(userId: String, code: Int) {}
override fun onUserLeave(userId: String, code: Int) {}
override fun onConnectionStateChanged(state: InteractiveConnectionState, reason: Int, userId: String?) {}
override fun onMessageReceived(message: String, userId: String?) {}
override fun onError(code: String, message: String) {}
override fun onTokenWillExpire(token: String?, expiresAt: Long) {}
override fun onTokenExpired(token: String?, expiresAt: Long) {}
override fun onLocalVideoStats(stats: InteractiveStreamStats) {}
override fun onRemoteVideoStats(stats: InteractiveStreamStats) {}
override fun onDuration(durationSeconds: Long) {}
override fun onRemoteVideoEnabled(enabled: Boolean, userId: String?) {}
override fun onRemoteAudioEnabled(enabled: Boolean, userId: String?) {}
override fun onStreamStateChanged(peerId: String, state: RemoteState, code: Int, message: String?) {}
}
```
---
## 通话统计
`InteractiveStreamStats` 常用字段:
- width / height
- fps
- videoBitrateKbps / audioBitrateKbps
- rttMs
- videoCodec / audioCodec
---
## Token 机制
### Token 来源
- 业务侧服务端生成 Token 并下发
- Demo 可在 `strings.xml` 配置 `signaling_token``signaling_secret` 进行本地生成
### Token 过期处理
- `onTokenWillExpire` / `onTokenExpired` 回调提示即将过期或已过期
- 建议获取新 Token 并 **重新 joinChannel**
Token 续期接口Demo 未覆盖):
```kotlin
rtcEngine.renewToken(newToken, expiresAtSec)
```
---
## 代理地址配置
SDK 支持通过外部代理(如洋葱盾等安全加速服务)连接信令服务器。代理地址由业务方在 SDK 外部获取,然后注入 SDK。
### 设置方式
```kotlin
// 设置代理地址(在 joinChannel 之前)
SellyCloudManager.setProxyAddress("http://127.0.0.1:12345")
// 清除代理(恢复直连)
SellyCloudManager.setProxyAddress(null)
// 查询当前代理地址
val proxy = SellyCloudManager.getProxyAddress() // null 表示未设置
```
### 格式要求
- 必须以 `http://``https://` 开头
-`null` 或空字符串表示清除代理
- 格式不合法时抛出 `IllegalArgumentException`
### 生效范围
设置后SDK 内部通过代理地址解析真实信令服务器 IP对上层接口透明。
### 时机要求
- 必须在 `joinChannel()` **之前** 设置
- 通话过程中修改代理地址,需 `leaveChannel` 后重新 `joinChannel` 才能生效
### Demo 中的接入示例
Demo 使用 `KiwiHelper` 封装洋葱盾的初始化与代理获取,采用三阶段模式:
```kotlin
// 阶段 1Application.onCreate() 异步初始化
KiwiHelper.initializeAsync()
// 阶段 2Activity 初始化时启动代理获取(非阻塞)
KiwiHelper.startProxySetup(enableKiwi = true, rsName = "your-rs-name")
// 阶段 3joinChannel 前确保代理已就绪
lifecycleScope.launch {
KiwiHelper.awaitProxyReady()
rtcEngine.joinChannel(...)
}
```
> `KiwiHelper` 内部通过 `SellyCloudManager.setProxyAddress()` 将代理地址传给 SDK。
> 详见 `example/src/main/java/com/demo/SellyCloudSDK/KiwiHelper.kt`。
---
## 更多 API 速览(含 Demo 未覆盖)
引擎创建与销毁:
- `InteractiveRtcEngine.create(config)`:创建引擎
- `InteractiveRtcEngine.destroy(engine)` / `engine.destroy()`:释放引擎
SDK 初始化与代理:
- `SellyCloudManager.initialize(context, appId, config)`:初始化 SDK
- `SellyCloudManager.setProxyAddress(address)`:设置代理地址
- `SellyCloudManager.getProxyAddress()`:获取当前代理地址
通话控制:
- `setEventHandler(handler)`:设置事件回调
- `setClientRole(role)`:设置角色(主播/观众)
- `setVideoEncoderConfiguration(config)`:设置编码参数
- `setDefaultAudioRoutetoSpeakerphone(true/false)`:设置音频输出
- `joinChannel(...)` / `leaveChannel()`:加入 / 离开频道
本地与远端控制:
- `setupLocalVideo(canvas)` / `setupRemoteVideo(canvas)`:设置画布
- `InteractiveVideoCanvas(renderTarget, userId, renderMode)`:推荐画布模型
- `clearRemoteVideo(userId)`:清理远端画面
- `enableLocalVideo(true/false)` / `enableLocalAudio(true/false)`:开关本地音视频
- `muteRemoteAudioStream(userId, true/false)` / `muteRemoteVideoStream(userId, true/false)`:按用户静音
- `muteAllRemoteAudioStreams(true/false)` / `muteAllRemoteVideoStreams(true/false)`:全量静音
- `switchCamera()`:切换摄像头
帧处理与屏幕共享:
- `setCaptureVideoProcessor(...)`:采集前可写处理
- `addCaptureVideoFrameObserver(...)`:采集前只读观测
- `addRenderVideoFrameObserver(...)`:远端渲染前只读观测
- `startScreenShare(...)` / `stopScreenShare()` / `isScreenSharing()`:屏幕共享
消息与 Token
- `sendMessage(message, targetUserId, callback)`:发送消息(`targetUserId` 可为空)
- `renewToken(newToken, expiresAtSec)`:续期 Token
---
## 常见问题FAQ
### Q远端画面不显示
1. 是否设置了正确的 `remoteUserId`
2. 是否在 `onUserJoined` 后调用 `setupRemoteVideo`
3. 远端是否关闭了视频
### Q互动直播可以用 `TextureView` 吗?
可以。
推荐用法是:
- 本地:`InteractiveVideoCanvas(TextureViewRtcTarget(textureView), userId)`
- 远端:`InteractiveVideoCanvas(TextureViewRtcTarget(textureView), remoteUserId)`
注意:
- 建议在 `joinChannel` 前确定本地 backend
- 当前 Demo 在首页设置中统一选择本地 backend进入互动页面后不再暴露切换入口
- 高层互动 API 当前未直接暴露 `SurfaceTexture` 入口
### Q加入频道失败
1. 检查 `signaling_app_id` 是否正确
2. Token 是否为空或已过期
3. 网络是否受限
4. 若使用代理,检查代理地址是否已正确设置
### Q屏幕分享失败
1. 是否已获取 `MediaProjection` 授权
2. Android 14+ 是否启动前台服务
### Q互动通话支持 XOR 吗?
当前高层互动 API 还没有暴露 `xorKeyHex` 一类的配置入口。
- 目前已支持 XOR 的 WebRTC 路径,是直播 RTC 的 `WHIP / WHEP` 推拉流
- 互动通话如需接入 XOR需要后续在互动链路单独暴露配置并挂载 FrameCrypto
### Q如何接入代理/加速服务?
SDK 本身不集成任何第三方代理 SDK。业务方需在外部完成代理初始化获取本地代理地址后通过 `SellyCloudManager.setProxyAddress()` 注入。详见「代理地址配置」章节。