update
This commit is contained in:
401
docs/SellySDK_直播推拉流接入文档_Android.md
Normal file
401
docs/SellySDK_直播推拉流接入文档_Android.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# Selly Live SDK 推拉流接入文档(Android)
|
||||
|
||||
> 统一 SDK 名称:**SellyCloudSDK**
|
||||
> 本文档适用于 Android 客户端,面向对外集成方与内部使用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
Selly Live SDK 提供完整的音视频直播能力,支持 **推流(直播发布)** 与 **拉流(直播播放)** 两大核心场景,适用于泛直播、互动直播、实时音视频等业务。
|
||||
|
||||
### 主要能力
|
||||
|
||||
- 支持 **RTMP / RTC** 推流与播放模式
|
||||
- 高性能音视频采集与编码
|
||||
- 灵活的视频参数配置(分辨率 / 帧率 / 码率)
|
||||
- 推流状态与统计回调
|
||||
- 拉流播放状态与错误回调
|
||||
- 支持视频帧处理(美颜 / 滤镜 / 水印)
|
||||
- 基于 **Token 的安全鉴权机制**
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统要求
|
||||
|
||||
- Android 8.0+(Demo `minSdk` 为 26)
|
||||
- 需真机运行(音视频采集限制)
|
||||
- 需要摄像头 / 麦克风权限
|
||||
|
||||
---
|
||||
|
||||
## 3. SDK 集成
|
||||
|
||||
### 3.1 项目结构参考
|
||||
|
||||
- `example/`:Android Demo 工程
|
||||
- 推流示例:`example/src/main/java/com/demo/SellyCloudSDK/live/LivePushActivity.kt`
|
||||
- 拉流示例:`example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt`
|
||||
- `example/libs/`:本地 AAR 依赖存放目录
|
||||
|
||||
### 3.2 Gradle 依赖方式(仅 AAR)
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation files("libs/sellycloudsdk-1.0.0.aar")
|
||||
}
|
||||
```
|
||||
|
||||
> 若接入美颜等能力,请按业务侧 SDK 要求额外引入第三方美颜库(Demo 使用 FaceUnity AAR)。
|
||||
|
||||
### 3.3 权限配置(AndroidManifest.xml)
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
```
|
||||
|
||||
> 如需截图保存且兼容 Android 9 及以下,可额外申请 `WRITE_EXTERNAL_STORAGE`。
|
||||
|
||||
---
|
||||
|
||||
## 4. Token 鉴权机制(重点)
|
||||
|
||||
### 4.1 Token 注入方式
|
||||
|
||||
| 场景 | 设置位置 |
|
||||
| ---- | ---- |
|
||||
| 推流 | `SellyLiveVideoPusher.token` |
|
||||
| 拉流 | `SellyLiveVideoPlayer.token` |
|
||||
|
||||
说明:
|
||||
|
||||
- Token **不拼接到 URL**
|
||||
- Token **不绑定 streamId**
|
||||
- SDK 内部在建立连接时自动携带当前 Token
|
||||
- 直接使用 RTMP 地址推/拉流不需要 Token,可不设置
|
||||
|
||||
### 4.2 Token 设置时机(强约束)
|
||||
|
||||
#### 推流
|
||||
|
||||
必须在以下接口调用 **之前** 设置:
|
||||
|
||||
- `startLiveWithStreamId(...)`
|
||||
|
||||
#### 拉流
|
||||
|
||||
必须在以下接口调用 **之前** 设置:
|
||||
|
||||
- `prepareToPlay()`
|
||||
- `play()`
|
||||
|
||||
> ⚠️ 在连接建立后修改 Token,不会影响当前连接。
|
||||
|
||||
### 4.3 Token 刷新机制说明
|
||||
|
||||
- SDK **不提供自动刷新**
|
||||
- 业务层可在任意时刻 **重新设置 token 属性**
|
||||
|
||||
推荐流程:
|
||||
|
||||
1. 业务侧向服务端获取新 Token
|
||||
2. 调用 `pusher.token = newToken` / `player.token = newToken`
|
||||
3. 停止并重新开始推流 / 拉流流程
|
||||
|
||||
---
|
||||
|
||||
## 5. 推流接入详解
|
||||
|
||||
### 5.1 创建推流实例
|
||||
|
||||
```kotlin
|
||||
val pusher = SellyLiveVideoPusher.initWithLiveMode(
|
||||
context = this,
|
||||
liveMode = SellyLiveMode.RTMP
|
||||
)
|
||||
pusher.delegate = object : SellyLiveVideoPusherDelegate {
|
||||
override fun liveStatusDidChanged(status: SellyLiveStatus) {}
|
||||
override fun onStatisticsUpdate(stats: SellyLivePusherStats) {}
|
||||
override fun onError(error: SellyLiveError) {}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 视频参数配置与预览
|
||||
|
||||
```kotlin
|
||||
val config = SellyLiveVideoConfiguration.defaultConfiguration().apply {
|
||||
videoSize = SellyLiveVideoResolution.RES_1280x720
|
||||
videoFrameRate = 25
|
||||
videoBitRate = 1000 * 1000
|
||||
videoMinBitRate = 500 * 1000
|
||||
outputImageOrientation = SellyLiveOrientation.PORTRAIT
|
||||
}
|
||||
|
||||
pusher.attachPreview(previewContainer)
|
||||
pusher.startRunning(
|
||||
cameraPosition = SellyLiveCameraPosition.FRONT,
|
||||
videoConfig = config,
|
||||
audioConfig = null
|
||||
)
|
||||
```
|
||||
|
||||
### 5.3 设置推流 Token(使用 streamId 时)
|
||||
|
||||
```kotlin
|
||||
pusher.token = pushToken
|
||||
```
|
||||
|
||||
### 5.4 开始/停止推流
|
||||
|
||||
```kotlin
|
||||
pusher.startLiveWithStreamId(streamId)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
pusher.stopLive()
|
||||
```
|
||||
|
||||
#### 直接使用 RTMP 地址推流(Demo 未覆盖)
|
||||
|
||||
```kotlin
|
||||
val rtmpUrl = "rtmp://your.host/live/streamKey"
|
||||
pusher.startLiveWithUrl(rtmpUrl)
|
||||
```
|
||||
|
||||
> 直接使用 RTMP 地址推流不需要 Token,可不设置。
|
||||
|
||||
#### 停止推流(带回调)
|
||||
|
||||
```kotlin
|
||||
pusher.stopLive { error ->
|
||||
if (error != null) {
|
||||
// 处理停止失败
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 常用控制接口
|
||||
|
||||
- `setMuted(true/false)`:静音
|
||||
- `switchCameraPosition(...)`:切换摄像头
|
||||
- `switchCamera()`:前后摄像头切换(自动切换)
|
||||
- `startCamera()` / `stopCamera()`:控制摄像头
|
||||
- `startMicrophone()` / `stopMicrophone()`:控制麦克风
|
||||
- `setCameraEnabled(true/false)`:关闭/开启摄像头
|
||||
- `setStreamOrientation(...)`:切换推流方向
|
||||
- `setVideoConfiguration(...)` + `changeResolution(...)`:动态调整分辨率
|
||||
- `setBeautyEngine(...)` + `setBeautyEnabled(...)`:接入美颜
|
||||
- `setBeautyLevel(level)`:设置美颜强度
|
||||
- `setBitmapAsVideoSource(...)` / `restoreCameraVideoSource()`:背景图推流
|
||||
|
||||
### 5.6 生命周期建议
|
||||
|
||||
在宿主 Activity 中对齐生命周期:
|
||||
|
||||
- `onResume()` → `pusher.onResume()`
|
||||
- `onPause()` → `pusher.onPause()`
|
||||
- `onDestroy()` → `pusher.release()`
|
||||
|
||||
### 5.7 状态与统计回调
|
||||
|
||||
**状态枚举:**
|
||||
|
||||
- `Idle`
|
||||
- `Connecting`
|
||||
- `Publishing`
|
||||
- `Reconnecting`
|
||||
- `Stopped`
|
||||
- `Failed`
|
||||
|
||||
**统计字段:**
|
||||
|
||||
- fps
|
||||
- videoBitrateKbps / audioBitrateKbps
|
||||
- rttMs
|
||||
- cpu 使用率(Demo 通过 `CpuUsage` 读取)
|
||||
|
||||
### 5.8 推流 API 速览(含 Demo 未覆盖)
|
||||
|
||||
初始化与预览:
|
||||
|
||||
- `initWithLiveMode(context, liveMode)`:创建推流实例
|
||||
- `setPreviewView(view)`:设置预览 View
|
||||
- `attachPreview(container)`:将预览 View 添加到容器
|
||||
- `getPreviewView()`:获取当前预览 View
|
||||
|
||||
采集与推流:
|
||||
|
||||
- `startRunning(cameraPosition, videoConfig, audioConfig)`:开始采集预览
|
||||
- `setVideoConfiguration(config)`:更新视频参数
|
||||
- `startLiveWithStreamId(streamId)`:使用 streamId 推流
|
||||
- `startLiveWithUrl(url)`:使用完整 URL 推流
|
||||
- `stopLive()` / `stopLive(callback)`:停止推流
|
||||
|
||||
设备与音视频控制:
|
||||
|
||||
- `switchCameraPosition(position)` / `switchCamera()`:切换摄像头
|
||||
- `startCamera()` / `stopCamera()`:启动 / 停止摄像头
|
||||
- `startMicrophone()` / `stopMicrophone()`:启动 / 停止麦克风
|
||||
- `setMuted(true/false)`:静音推流音频
|
||||
- `setCameraEnabled(true/false)`:开启 / 关闭摄像头
|
||||
|
||||
美颜与画面:
|
||||
|
||||
- `setBeautyEngine(engine)`:设置美颜引擎
|
||||
- `setBeautyEnabled(true/false)`:启用 / 关闭美颜
|
||||
- `setBeautyLevel(level)`:设置美颜强度
|
||||
- `setStreamOrientation(orientation)`:设置推流方向
|
||||
- `changeResolution(width, height)`:动态调整分辨率
|
||||
- `setBitmapAsVideoSource(bitmap)` / `restoreCameraVideoSource()`:背景图推流
|
||||
|
||||
生命周期:
|
||||
|
||||
- `onPause()` / `onResume()` / `release()`:与 Activity 生命周期对齐
|
||||
|
||||
---
|
||||
|
||||
## 6. 拉流接入详解
|
||||
|
||||
### 6.1 创建播放器
|
||||
|
||||
```kotlin
|
||||
val player = SellyLiveVideoPlayer.initWithStreamId(
|
||||
context = this,
|
||||
streamId = streamId,
|
||||
liveMode = SellyLiveMode.RTC
|
||||
)
|
||||
// 或直接使用完整 URL
|
||||
// val player = SellyLiveVideoPlayer.initWithUrl(this, playUrl)
|
||||
```
|
||||
|
||||
若需要指定 `vhost` / `appName`:
|
||||
|
||||
```kotlin
|
||||
val player = SellyLiveVideoPlayer.initWithStreamId(
|
||||
context = this,
|
||||
streamId = streamId,
|
||||
liveMode = SellyLiveMode.RTMP,
|
||||
vhost = "your-vhost",
|
||||
appName = "live"
|
||||
)
|
||||
```
|
||||
|
||||
### 6.2 设置拉流 Token(使用 streamId 时)
|
||||
|
||||
```kotlin
|
||||
player.token = playToken
|
||||
```
|
||||
> 直接使用 RTMP 地址拉流不需要 Token,可不设置。
|
||||
|
||||
### 6.3 播放流程
|
||||
|
||||
```kotlin
|
||||
player.attachRenderView(renderContainer)
|
||||
player.prepareToPlay()
|
||||
player.play()
|
||||
```
|
||||
|
||||
控制接口:
|
||||
|
||||
- `pause()`
|
||||
- `stop()`
|
||||
- `play()`
|
||||
- `setMuted(true/false)`
|
||||
- `release()`
|
||||
|
||||
补充接口(Demo 未覆盖):
|
||||
|
||||
- `setRenderView(view)`:手动指定渲染 View
|
||||
- `seekBy(deltaMs)`:播放进度跳转(仅在流支持快进/回放时有效)
|
||||
|
||||
### 6.4 播放回调
|
||||
|
||||
```kotlin
|
||||
player.delegate = object : SellyLiveVideoPlayerDelegate {
|
||||
override fun playbackStateChanged(state: SellyPlayerState) {}
|
||||
override fun onFirstVideoFrameRendered() {}
|
||||
override fun onFirstAudioFrameRendered() {}
|
||||
override fun onLatencyChasingUpdate(update: SellyLatencyChasingUpdate) {}
|
||||
override fun onLatencyChasingReloadRequired(latencyMs: Long) {}
|
||||
override fun onError(error: SellyLiveError) {}
|
||||
}
|
||||
```
|
||||
|
||||
状态枚举:
|
||||
|
||||
- `Idle`
|
||||
- `Connecting`
|
||||
- `Playing`
|
||||
- `Paused`
|
||||
- `Stopped`
|
||||
- `Reconnecting`
|
||||
- `Failed`
|
||||
|
||||
### 6.5 播放 API 速览(含 Demo 未覆盖)
|
||||
|
||||
创建与渲染:
|
||||
|
||||
- `initWithStreamId(context, streamId, liveMode, vhost, appName)`:使用 streamId 创建播放器
|
||||
- `initWithUrl(context, url)`:使用完整 URL 创建播放器
|
||||
- `attachRenderView(container)` / `setRenderView(view)`:设置渲染 View
|
||||
- `getRenderView()`:获取当前渲染 View
|
||||
|
||||
播放控制:
|
||||
|
||||
- `prepareToPlay()` / `play()` / `pause()` / `stop()`:播放流程控制
|
||||
- `seekBy(deltaMs)`:播放进度跳转(流支持回放时有效)
|
||||
- `isPlaying()`:查询播放状态
|
||||
- `setMuted(true/false)`:静音播放
|
||||
|
||||
统计与释放:
|
||||
|
||||
- `setStatsListener { snapshot -> }`:播放统计回调
|
||||
- `release()`:释放播放器资源
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误处理与重试建议
|
||||
|
||||
### Token 错误
|
||||
|
||||
1. 停止当前推 / 拉
|
||||
2. 获取新 Token
|
||||
3. 重新设置 Token
|
||||
4. 重新开始推流 / 拉流流程
|
||||
|
||||
### 网络错误
|
||||
|
||||
- 监听 `onStatisticsUpdate` 或播放器状态
|
||||
- 弱网时适当降低分辨率 / 码率
|
||||
- 必要时重启连接
|
||||
|
||||
---
|
||||
|
||||
## 8. 最佳实践
|
||||
|
||||
- 推流前先完成采集预览
|
||||
- Token 即将过期前提前刷新
|
||||
- 使用统计回调做质量监控
|
||||
- 拉流失败避免无限重试
|
||||
|
||||
---
|
||||
|
||||
## 9. 常见问题(FAQ)
|
||||
|
||||
### Q1:Token 可以拼接到 URL 吗?
|
||||
**A:** 不可以。
|
||||
SDK 不解析 URL 中的鉴权信息,所有鉴权均通过 `token` 属性完成。
|
||||
|
||||
### Q2:运行中修改 Token 是否生效?
|
||||
**A:**
|
||||
运行中修改 Token **不会影响当前已建立的连接**。
|
||||
**下次重连或重新启动推流 / 拉流时会使用新的 Token**。
|
||||
|
||||
### Q3:播放器出现黑屏怎么办?
|
||||
**A:** 可按以下步骤排查:
|
||||
|
||||
- 检查播放地址是否正确
|
||||
- 确认当前网络连接正常
|
||||
- 查看播放器回调中的错误信息
|
||||
- 确认视频流格式是否被 SDK 支持
|
||||
Reference in New Issue
Block a user