# Selly Live SDK 推拉流接入文档(Android) > 统一 SDK 名称:**SellyCloudSDK** > 本文档适用于 Android 客户端,面向对外集成方与内部使用。 --- ## 1. 概述 Selly Live SDK 提供完整的音视频直播能力,支持 **推流(直播发布)** 与 **拉流(直播播放)** 两大核心场景,适用于泛直播、互动直播、实时音视频等业务。 ### 主要能力 - 支持 **RTMP / RTC** 推流与播放模式 - 高性能音视频采集与编码 - 灵活的视频参数配置(分辨率 / 帧率 / 码率) - 推流状态与统计回调 - 拉流播放状态与错误回调 - 支持视频帧处理(美颜 / 滤镜 / 水印) - 基于 **Token 的安全鉴权机制** - 支持 **RTMP H264 + AAC payload XOR 保护(可选)** --- ## 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 ``` > 如需截图保存且兼容 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. 停止并重新开始推流 / 拉流流程 ### 4.4 RTMP Payload XOR 保护(可选) 用途: - 防止他人拿到 RTMP 地址后直接播放、转码或截图 生效范围与约束: - 仅对 **RTMP** 生效 - 仅支持 **H264 + AAC**(当前版本) - 只处理 payload,配置帧(SPS/PPS、AAC Sequence Header)保持不变 - 推流端与播放端必须使用**同一个 key** Key 格式: - `hex` 字符串,建议 16 或 32 字节(即 32/64 个 hex 字符) - 支持 `0x` 前缀 - 长度必须为偶数 - 非法 key 会被忽略并关闭 XOR(会输出 warning 日志) 时机要求: - 推流:请在 `startLiveWithStreamId(...)` / `startLiveWithUrl(...)` 之前设置 key - 拉流:请在 `initWithStreamId(...)` / `initWithUrl(...)` 创建播放器时传入 `xorKeyHex` --- ## 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 ``` #### RTMP Payload XOR(可选) ```kotlin val xorKeyHex = "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6" // 建议在 startLiveWith... 之前设置 pusher.setXorKey(xorKeyHex) ``` > 若在推流中修改 key,需停止并重新开始推流后才会使用新 key。 ### 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)`:更新视频参数 - `setXorKey(hexKey)`:设置 RTMP payload XOR key(可选) - `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, xorKeyHex = "" // RTC 场景可留空 ) // 或直接使用完整 URL // val player = SellyLiveVideoPlayer.initWithUrl(this, playUrl, xorKeyHex = "A1B2...") ``` 若需要指定 `vhost` / `appName`: ```kotlin val player = SellyLiveVideoPlayer.initWithStreamId( context = this, streamId = streamId, liveMode = SellyLiveMode.RTMP, vhost = "your-vhost", appName = "live", xorKeyHex = "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6" ) ``` > 使用 RTMP 加密流时,请在创建播放器时传入 `xorKeyHex`;后续如需换 key,请重建播放器实例。 ### 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, xorKeyHex)`:使用 streamId 创建播放器 - `initWithUrl(context, url, xorKeyHex)`:使用完整 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 支持 ### Q4:加密流播放花屏/噪音怎么办? **A:** 重点检查以下项: - 推流端与播放端 `xorKeyHex` 是否完全一致 - key 格式是否为合法 hex(偶数长度,支持 `0x` 前缀) - 当前是否为 RTMP + H264 + AAC - 变更 key 后是否已重启推流 / 重建播放器