Files
SellyCloudSDK_Android_demo/docs/SellySDK_直播推拉流接入文档_Android.md
shou 54a78130b1 demo:新增xor播放支持
sdk:player: texture 播放链优化
2026-04-14 00:53:11 +08:00

29 KiB
Raw Blame History

Selly Live SDK 推拉流接入文档Android

统一 SDK 名称:SellyCloudSDK 本文档适用于 Android 客户端,面向对外集成方与内部使用。


1. 概述

Selly Live SDK 提供完整的音视频直播能力,支持 推流(直播发布)拉流(直播播放) 两大核心场景,适用于泛直播、互动直播、实时音视频等业务。

主要能力

  • 支持 RTMP / RTC 推流与播放模式
  • 支持 SurfaceView / TextureView 两套渲染后端
  • 直播播放器与点播播放器支持 SurfaceTexture 高级渲染接入
  • 高性能音视频采集与编码
  • 灵活的视频参数配置(分辨率 / 帧率 / 码率)
  • 推流状态与统计回调
  • 拉流播放状态与错误回调
  • 支持视频帧处理(美颜 / 滤镜 / 水印)
  • 基于 Token 的安全鉴权机制
  • 支持 RTMP Payload XOR 保护(可选)
  • 支持 RTCWHEP/WHIPWebRTC Frame 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

dependencies {
    implementation files("libs/sellycloudsdk-1.0.1.aar")
}

若接入美颜等能力,请按业务侧 SDK 要求额外引入第三方美颜库Demo 使用 FaceUnity AAR

3.3 权限配置AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

如需截图保存且兼容 Android 9 及以下,可额外申请 WRITE_EXTERNAL_STORAGE


4. SDK 初始化与代理配置

4.1 SDK 初始化

在使用任何推流 / 拉流功能前,必须先初始化 SDK

SellyCloudManager.initialize(
    context = applicationContext,
    appId = "your-app-id",
    config = SellyCloudConfig(
        vhost = "your-vhost",
        vhostKey = "your-vhost-key",
        defaultStreamId = "default-stream",
        defaultLiveMode = SellyLiveMode.RTMP
    )
)

initialize 参数说明:

参数 类型 说明
context Context 应用上下文
appId String 应用 ID权威值会覆盖 config 中的 appId
config SellyCloudConfig? 可选配置,不传则使用默认值

SellyCloudConfig 字段说明:

字段 类型 说明
vhost String 虚拟主机
vhostKey String vhost 密钥(用于鉴权签名)
defaultStreamId String 默认流 ID
logEnabled Boolean 是否启用日志,默认 true
defaultLiveMode SellyLiveMode 默认推拉流模式RTMP / RTC
appName String 应用名称,为空时自动使用 appId一般无需设置

config.appId 无需设置SDK 内部会用 initialize(appId=) 参数覆盖。

4.2 代理地址配置(可选)

SDK 支持通过外部代理(如洋葱盾等安全加速服务)进行流媒体连接。代理地址由业务方在 SDK 外部获取,然后通过以下接口注入:

// 设置代理地址
SellyCloudManager.setProxyAddress("http://127.0.0.1:12345")

// 清除代理(恢复直连)
SellyCloudManager.setProxyAddress(null)

// 查询当前代理地址
val proxy = SellyCloudManager.getProxyAddress()  // null 表示未设置

格式要求:

  • 必须以 http://https:// 开头
  • null 或空字符串表示清除代理
  • 格式不合法时抛出 IllegalArgumentException

生效范围:

  • 设置后对 RTMP 推拉流、RTCWHEP/WHIP播放推流、Signaling 信令连接均生效
  • SDK 内部通过代理地址解析真实服务器 IP对上层透明

时机要求:

  • 必须在推流 / 拉流 开始之前 设置
  • 推流 / 拉流过程中修改代理地址,需停止后重新开始才能生效

Demo 中使用 KiwiHelper 封装了洋葱盾 SDK 的初始化与代理地址获取流程,通过 SellyCloudManager.setProxyAddress() 将结果传给 SDK。详见 example/src/main/java/com/demo/SellyCloudSDK/KiwiHelper.kt


5. Token 鉴权机制(重点)

5.1 Token 注入方式

场景 设置位置
推流 SellyLiveVideoPusher.token
拉流 SellyLiveVideoPlayer.token

说明:

  • Token 不拼接到 URL
  • Token 不绑定 streamId
  • SDK 内部在建立连接时自动携带当前 Token
  • 直接使用 RTMP 地址推/拉流不需要 Token可不设置

5.2 Token 设置时机(强约束)

推流

必须在以下接口调用 之前 设置:

  • startLiveWithStreamId(...)

拉流

必须在以下接口调用 之前 设置:

  • prepareToPlay()
  • play()

在连接建立后修改 Token不会影响当前连接。

5.3 Token 刷新机制说明

  • SDK 不提供自动刷新
  • 业务层可在任意时刻 重新设置 token 属性

推荐流程:

  1. 业务侧向服务端获取新 Token
  2. 调用 pusher.token = newToken / player.token = newToken
  3. 停止并重新开始推流 / 拉流流程

5.4 RTMP / WebRTC XOR 保护(可选)

用途:

  • 提高流地址泄露后被直接播放、转码或抓流的门槛

生效范围与约束:

  • RTMP 推拉流:支持 payload XOR当前仅支持 H264 + AAC
  • RTCWHEP/WHIP 推拉流:支持 WebRTC frame XOR 加解密
  • 当前这里的 WebRTC 指直播 RTC 推拉流,不包含互动通话高层 API
  • RTMP 只处理 payload配置帧SPS/PPS、AAC Sequence Header保持不变
  • 推流端与播放端必须使用同一个 key

Key 格式:

  • hex 字符串,建议 16 或 32 字节(即 32/64 个 hex 字符)
  • 支持 0x 前缀
  • 长度必须为偶数
  • 非法 key 会直接抛出 IllegalArgumentException,不会静默降级

时机要求:

  • 推流:请在 startLiveWithStreamId(...) / startLiveWithUrl(...) 之前调用 setXorKey(...)
  • 拉流:请在 initWithStreamId(...) / initWithUrl(...) 创建播放器时传入 xorKeyHex
  • 运行中修改 key 不会影响当前连接,需重启推流或重建播放器实例

6. 推流接入详解

6.1 创建推流实例

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) {}
}

6.2 视频参数配置与预览

val config = SellyLiveVideoConfiguration.defaultConfiguration().apply {
    videoSize = SellyLiveVideoResolution.RES_1280x720
    videoFrameRate = 25
    videoBitRate = 1000 * 1000
    videoMinBitRate = 500 * 1000
    outputImageOrientation = SellyLiveOrientation.PORTRAIT
}

pusher.attachPreview(previewContainer, useTextureView = false)
pusher.startRunning(
    cameraPosition = SellyLiveCameraPosition.FRONT,
    videoConfig = config,
    audioConfig = null
)

6.2.1 预览后端选择

推流预览支持两种接入方式:

  • attachPreview(container, useTextureView = false)SDK 创建预览 View默认走旧的 Surface/OpenGL 预览链路
  • attachPreview(container, useTextureView = true)SDK 创建 TextureView 预览,适合需要普通 View 层级混排的场景
  • setPreviewView(view):手动传入预览 View
  • setPreviewView(view, mode):当传入 TextureView 时,建议使用这个显式协议版本

示例:

// 默认旧路径
pusher.attachPreview(previewContainer, useTextureView = false)

// TextureView 路径
pusher.attachPreview(previewContainer, useTextureView = true)
// 手动指定 TextureView 时,建议显式传入 liveMode
val textureView = com.sellycloud.sellycloudsdk.widget.AspectRatioTextureView(this)
pusher.setPreviewView(textureView, SellyLiveMode.RTMP)

说明:

  • RTMP 模式下SDK 内部会根据预览 View 类型自动选择 OpenGlViewTextureView
  • RTC/WHIP 预览也支持 TextureView
  • 当前版本建议在 开始采集/推流前 选定预览后端;不保证运行中热切换预览后端

6.3 设置推流 Token使用 streamId 时)

pusher.token = pushToken

推流 XORRTMP / RTC-WHIP可选

val xorKeyHex = "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6"

// 建议在 startLiveWith... 之前设置
pusher.setXorKey(xorKeyHex)

setXorKey(...) 同时作用于 RTMP 推流与 RTC/WHIP 推流。若在推流中修改 key需停止并重新开始推流后才会使用新 key。

6.4 开始/停止推流

pusher.startLiveWithStreamId(streamId)
pusher.stopLive()

直接使用 RTMP 地址推流Demo 未覆盖)

val rtmpUrl = "rtmp://your.host/live/streamKey"
pusher.startLiveWithUrl(rtmpUrl)

直接使用 RTMP 地址推流不需要 Token可不设置。

停止推流(带回调)

pusher.stopLive { error ->
    if (error != null) {
        // 处理停止失败
    }
}

6.5 常用控制接口

  • setMuted(true/false):静音
  • switchCameraPosition(...):切换摄像头
  • switchCamera():前后摄像头切换(自动切换)
  • startCamera() / stopCamera():控制摄像头
  • startMicrophone() / stopMicrophone():控制麦克风
  • setCameraEnabled(true/false):关闭/开启摄像头
  • setStreamOrientation(...):切换推流方向
  • setVideoConfiguration(...) + changeResolution(...):动态调整分辨率
  • setAutoFramingEnabled(...) / getAutoFramingCapability() / getAutoFramingState():自动取景
  • setBeautyEngine(...) + setBeautyEnabled(...):接入美颜
  • setBeautyLevel(level):设置美颜强度
  • setBitmapAsVideoSource(...) / restoreCameraVideoSource():背景图推流

6.5.1 美颜引擎接入

当前版本推荐通过 BeautyEngine + VideoProcessor 接入美颜。Demo 使用 FaceUnityBeautyEngine,位于:

  • example/src/main/java/com/demo/SellyCloudSDK/beauty/FaceUnityBeautyEngine.kt

接入示例:

val beautyEngine = FaceUnityBeautyEngine()

pusher.setBeautyEngine(beautyEngine)
pusher.setBeautyEnabled(true)
pusher.setBeautyLevel(3.0f)

说明:

  • BeautyEngine.createProcessor() 返回的是 SDK V2 VideoProcessor
  • 当前 Demo 的美颜实现走 TEXTURE_2D + READ_WRITE
  • 美颜属于“完整重写输出”的场景,建议在 VideoProcessorConfig 中设置 fullRewrite = true
  • RTC/WHIP 路径优先推荐 TEXTURE_2D,避免对 texture-backed 帧做额外的 texture-to-CPU 转换

6.5.2 推流前帧处理与观察

直播推流支持:

  • 一个可写 VideoProcessor
  • 多个只读 VideoFrameObserver

只读观测示例:

val disposable = pusher.addVideoFrameObserver(object : VideoFrameObserver {
    override val config = VideoFrameObserverConfig(
        preferredFormat = VideoProcessFormat.TEXTURE_2D
    )

    override fun onTextureFrame(frame: VideoTextureFrame) {
        // 只读观测,不修改输出
    }
})

可写处理示例:

pusher.setVideoProcessor(object : VideoProcessor {
    override val config = VideoProcessorConfig(
        preferredFormat = VideoProcessFormat.TEXTURE_2D,
        mode = VideoProcessMode.READ_WRITE
    )

    override fun processTexture(input: VideoTextureFrame, outputTextureId: Int) {
        // 将滤镜/水印直接写入 SDK 提供的 outputTextureId
    }
})

当前 SDK / Demo 的处理建议:

  • RTC/WHIP 路径优先使用 TEXTURE_2D
  • RTMP 在确实需要 CPU 像素时,可使用 I420 / RGBA
  • READ_WRITE 模式下SDK 会准备输出缓冲;只有“完整覆盖输出”的场景才建议 fullRewrite = true
  • outputTextureId 由 SDK 管理,处理器不应转移所有权,也不应在回调里主动删除纹理
  • VideoFrameObserverConfig 的默认值仍为 I420 以兼容旧接入;新接入建议显式声明 preferredFormat

Demo 中当前可直接验证的模式:

  • 帧回调纹理TEXTURE_2D observer
  • 帧回调空CPU:声明 I420,不处理像素
  • 帧回调单CPU:单个 I420 observer
  • 帧回调双CPU:两个 I420 observer共享同一次 CPU 转换
  • 改帧RTC 下走 TEXTURE_2DRTMP 示例走 RGBA

6.5.3 自动取景Auto Framing

当前高层 API 已暴露:

  • setAutoFramingEnabled(enabled):开启 / 关闭自动取景
  • getAutoFramingCapability():查询当前是否支持及原因
  • getAutoFramingState():读取当前状态
  • delegate.onAutoFramingStateChanged(state):接收状态变化回调

状态枚举:

  • OFF
  • INACTIVE
  • FRAMING
  • CONVERGED
  • UNSUPPORTED

当前约束:

  • 当前自动取景只在 RTMP 推流 路径可用
  • RTC / WHIP 推流当前会返回 UNSUPPORTED
  • 需要摄像头已启动后再查询 capability相机关闭、背景图推流等场景也会返回不支持

示例:

val capability = pusher.getAutoFramingCapability()
if (capability.supported) {
    pusher.setAutoFramingEnabled(true)
}

6.6 生命周期建议

在宿主 Activity 中对齐生命周期:

  • onResume()pusher.onResume()
  • onPause()pusher.onPause()
  • onDestroy()pusher.release()

6.7 状态与统计回调

状态枚举:

  • Idle
  • Connecting
  • Publishing
  • Reconnecting
  • Stopped
  • Failed

统计字段:

  • fps
  • videoBitrateKbps / audioBitrateKbps
  • rttMs
  • cpu 使用率Demo 通过 CpuUsage 读取)
  • auto framing state通过 onAutoFramingStateChanged / getAutoFramingState() 获取)

6.8 推流 API 速览(含 Demo 未覆盖)

初始化与预览:

  • initWithLiveMode(context, liveMode):创建推流实例
  • setPreviewView(view):设置预览 ViewTextureView 会按当前 liveMode 选择协议
  • setPreviewView(view, mode):显式设置预览 View 与协议,TextureView 推荐使用
  • attachPreview(container):将默认预览 View 添加到容器
  • attachPreview(container, useTextureView):创建并绑定 Surface/OpenGLTextureView 预览
  • getPreviewView():获取当前预览 View

采集与推流:

  • startRunning(cameraPosition, videoConfig, audioConfig):开始采集预览
  • setVideoConfiguration(config):更新视频参数
  • setXorKey(hexKey):设置推流 XOR keyRTMP payload / RTC-WHIP frame可选
  • setAutoFramingEnabled(enabled) / getAutoFramingCapability() / getAutoFramingState():自动取景控制与状态查询
  • 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):设置美颜强度
  • onAutoFramingStateChanged(state):自动取景状态回调
  • setStreamOrientation(orientation):设置推流方向
  • changeResolution(width, height):动态调整分辨率
  • setBitmapAsVideoSource(bitmap) / restoreCameraVideoSource():背景图推流

生命周期:

  • onPause() / onResume() / release():与 Activity 生命周期对齐

7. 拉流接入详解

7.1 创建播放器

val player = SellyLiveVideoPlayer.initWithStreamId(
    context = this,
    streamId = streamId,
    liveMode = SellyLiveMode.RTC,
    xorKeyHex = "" // 加密流传入同一 key明文流可留空
)
// 或直接使用完整 URL
// val player = SellyLiveVideoPlayer.initWithUrl(this, playUrl, xorKeyHex = "A1B2...")

若需要指定 vhost / appName

val player = SellyLiveVideoPlayer.initWithStreamId(
    context = this,
    streamId = streamId,
    liveMode = SellyLiveMode.RTMP,
    vhost = "your-vhost",
    appName = "live",
    xorKeyHex = "A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6"
)

使用 RTMP 或 RTC/WHEP 加密流时,请在创建播放器时传入 xorKeyHex;后续如需换 key请重建播放器实例。

7.2 设置拉流 Token使用 streamId 时)

player.token = playToken

直接使用 RTMP 地址拉流不需要 Token可不设置。

7.3 播放流程

player.attachRenderView(renderContainer, com.sellycloud.sellycloudsdk.render.RenderBackend.SURFACE_VIEW)
player.prepareToPlay()
player.play()

7.3.1 播放渲染后端选择

直播播放器支持以下渲染接入方式:

  • attachRenderView(container, RenderBackend.SURFACE_VIEW):默认旧路径
  • attachRenderView(container, RenderBackend.TEXTURE_VIEW):使用 TextureView
  • setRenderView(view):手动传入 SurfaceViewSurfaceViewRendererTextureView
  • setRenderSurfaceTexture(surfaceTexture, width, height):高级场景下直接绑定 SurfaceTexture(调用方负责 SurfaceTexture 生命周期)

示例:

val backend = com.sellycloud.sellycloudsdk.render.RenderBackend.TEXTURE_VIEW
player.attachRenderView(renderContainer, backend)
player.prepareToPlay()
player.play()

说明:

  • RTMP 播放支持 SurfaceViewTextureViewSurfaceTexture
  • RTC/WHEP 播放支持 SurfaceViewRendererTextureView,以及高级场景下的 SurfaceTexture
  • RTMP/VODTextureView / SurfaceTexture 默认走 direct output,优先保证首帧和低延迟
  • 当前版本建议在 开始播放前 选定渲染后端;运行中如需变更目标,请走 clearRenderTarget() + setRenderView(...) / setRenderSurfaceTexture(...) 的显式重绑流程
  • Flutter 场景优先使用 setRenderSurfaceTexture(...),配合 Flutter Texture widget 使用;如 UI 层级正确性优先,不建议继续依赖 Hybrid Composition + SurfaceView

控制接口:

  • pause()
  • stop()
  • play()
  • setMuted(true/false)
  • release()

补充接口Demo 未覆盖):

  • setRenderView(view):手动指定渲染 View
  • setRenderSurfaceTexture(surfaceTexture, width, height):直接绑定 SurfaceTexture(调用方负责 SurfaceTexture 生命周期)
  • clearRenderTarget():解绑当前渲染面,播放会话可继续存活
  • seekBy(deltaMs):播放进度跳转(仅在流支持快进/回放时有效)

7.3.2 Flutter / SurfaceTexture 接入建议

如果业务侧需要把视频放到 Flutter UI 层下面,并正常叠加按钮、封面、弹层、动画,推荐使用:

  • Flutter 侧创建 TextureRegistry.SurfaceTextureEntry
  • Android 插件层取出 SurfaceTexture
  • 调用 setRenderSurfaceTexture(surfaceTexture, width, height)
  • Flutter 页面使用 Texture(textureId) 显示视频

示意:

player.setRenderSurfaceTexture(surfaceTexture, width, height)
player.prepareToPlay()
player.play()

说明:

  • SurfaceTexture 生命周期由调用方负责
  • 销毁前建议先调用 clearRenderTarget() 或直接 release()
  • 如果页面重建、Texture 重新申请或 Flutter 侧切换 textureId需要重新绑定新的 SurfaceTexture

7.4 播放回调

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

首帧语义说明:

  • 默认 DIRECT 模式下,onFirstVideoFrameRendered() 对应 decoder 首帧可用时机
  • TextureView / SurfaceTexture 且启用了 playback processing 的场景SDK 会等待目标渲染面确认首帧已真正呈现后,再触发 onFirstVideoFrameRendered()
  • onFirstAudioFrameRendered() 仍表示音频首帧可播放时机;在 texture-backed processing 场景中,音频与视频首帧不一定完全同一时刻

7.4.1 播放侧帧回调与二次处理

播放器支持一组独立于采集/推流链路的播放侧高级能力:

  • setPlaybackFrameObserver(observer):播放侧只读帧回调
  • setPlaybackVideoProcessor(processor):播放侧可写纹理处理

当前能力边界:

  • 当前仅支持 texture-backed 播放目标:TextureView / SurfaceTexture
  • 当前仅支持 preferredFormat = TEXTURE_2D
  • 当前仅支持 stage = RENDER_PRE_DISPLAY
  • 当前默认渲染模式为 DIRECT
  • 只有设置了有效的 observer / processor才会切到 PROCESSING
  • 如果当前 render target 已经绑定,新增或移除 observer / processor 后,需要 重绑一次 texture render target 才会生效
  • RTC/WHEP 播放当前不支持这套 playback processing当前主要用于 RTMP/VOD 播放链

只读 observer 示例:

player.setPlaybackFrameObserver(object : PlaybackFrameObserver {
    override val config = PlaybackFrameObserverConfig(
        preferredFormat = VideoProcessFormat.TEXTURE_2D,
        stage = VideoStage.RENDER_PRE_DISPLAY
    )

    override fun onTextureFrame(frame: VideoTextureFrame) {
        // 读取播放侧纹理帧信息
    }
})

可写 processor 示例:

player.setPlaybackVideoProcessor(object : PlaybackVideoProcessor {
    override val config = PlaybackVideoProcessorConfig(
        preferredFormat = VideoProcessFormat.TEXTURE_2D,
        mode = VideoProcessMode.READ_WRITE,
        stage = VideoStage.RENDER_PRE_DISPLAY
    )

    override fun processTexture(input: VideoTextureFrame, outputTextureId: Int) {
        // 将后处理结果写入 outputTextureId
    }
})

7.5 播放 API 速览(含 Demo 未覆盖)

创建与渲染:

  • initWithStreamId(context, streamId, liveMode, vhost, appName, xorKeyHex):使用 streamId 创建播放器
  • initWithUrl(context, url, xorKeyHex):使用完整 URL 创建播放器
  • attachRenderView(container):创建默认 SurfaceView 渲染 View
  • attachRenderView(container, backend):创建指定 backend 的渲染 View
  • setRenderView(view):手动设置渲染 View
  • setRenderSurfaceTexture(surfaceTexture, width, height):绑定 SurfaceTexture(调用方负责 SurfaceTexture 生命周期)
  • clearRenderTarget():解绑当前渲染面
  • getRenderView():获取当前渲染 View
  • setPlaybackFrameObserver(observer):设置播放侧只读 observertexture 路径)
  • setPlaybackVideoProcessor(processor):设置播放侧 processortexture 路径)

播放控制:

  • prepareToPlay() / play() / pause() / stop():播放流程控制
  • seekBy(deltaMs):播放进度跳转(流支持回放时有效)
  • isPlaying():查询播放状态
  • setMuted(true/false):静音播放

统计与释放:

  • setStatsListener { snapshot -> }:播放统计回调
  • release():释放播放器资源

7.6 点播播放器渲染说明

SellyVodPlayer 与直播播放器在渲染后端模型上保持一致:

  • attachRenderView(container, backend):支持 SURFACE_VIEW / TEXTURE_VIEW
  • setRenderView(surfaceView) / setRenderView(textureView):手动绑定现有 View
  • setRenderSurfaceTexture(surfaceTexture, width, height):高级场景使用 SurfaceTexture(调用方负责 SurfaceTexture 生命周期)
  • clearRenderTarget():解绑当前渲染面但不一定立即销毁播放实例
  • setPlaybackFrameObserver(observer) / setPlaybackVideoProcessor(processor):点播同样支持 texture-backed playback processing

补充说明:

  • 点播在重绑 TextureView / SurfaceTexture 后,会自动复用最近一次视频宽高信息,保持正确显示比例
  • 如在已有 texture 目标上新增或移除 observer / processor也需要重绑一次 texture render target 才会应用新的渲染模式

因此 Demo 中点播页的 SurfaceView / TextureView 选择,也与直播播放页保持一致,均在首页设置中统一生效。


8. 错误处理与重试建议

Token 错误

  1. 停止当前推 / 拉
  2. 获取新 Token
  3. 重新设置 Token
  4. 重新开始推流 / 拉流流程

网络错误

  • 监听 onStatisticsUpdate 或播放器状态
  • 弱网时适当降低分辨率 / 码率
  • 必要时重启连接

9. 最佳实践

  • 推流前先完成采集预览
  • SurfaceView / TextureView backend 建议在开始推流或播放前选定
  • Flutter 场景优先使用 setRenderSurfaceTexture(...),不要把 Hybrid Composition + SurfaceView 当成默认方案
  • 普通播放默认保持 DIRECT;只有确实需要播放侧帧观察或纹理后处理时,再启用 playback processing
  • playback processing 当前仅建议用于 TextureView / SurfaceTexture + TEXTURE_2D + RENDER_PRE_DISPLAY
  • 变更 texture 路径的 observer / processor 后,显式重绑一次 render target
  • RTC/WHIP 的美颜、滤镜、水印、观测优先使用 TEXTURE_2D
  • I420 / RGBA 仅在算法必须访问 CPU 像素时再使用
  • 完整重写输出的 GPU 处理器设置 fullRewrite = true;叠加类处理保留默认值
  • Token 即将过期前提前刷新
  • 使用统计回调做质量监控
  • 拉流失败避免无限重试
  • 使用代理时,确保在推拉流开始前代理地址已设置完毕

10. 常见问题FAQ

Q1Token 可以拼接到 URL 吗?

A 不可以。 SDK 不解析 URL 中的鉴权信息,所有鉴权均通过 token 属性完成。

Q2运行中修改 Token 是否生效?

A 运行中修改 Token 不会影响当前已建立的连接下次重连或重新启动推流 / 拉流时会使用新的 Token

Q3播放器出现黑屏怎么办

A 可按以下步骤排查:

  • 检查播放地址是否正确
  • 确认当前网络连接正常
  • 查看播放器回调中的错误信息
  • 确认视频流格式是否被 SDK 支持

Q4加密流播放花屏/噪音怎么办?

A 重点检查以下项:

  • 推流端与播放端 xorKeyHex 是否完全一致
  • key 格式是否为合法 hex偶数长度支持 0x 前缀)
  • 当前是 RTMP 还是 RTC/WHEP,两端是否都走了对应的加密流配置
  • 变更 key 后是否已重启推流 / 重建播放器

Q5什么时候选择 SurfaceView,什么时候选择 TextureView

A

  • 普通原生 Android 页面,优先使用默认 SurfaceView,性能最优
  • 需要与按钮、封面、弹层等普通 View 正常混排时,优先使用 TextureView
  • Flutter 场景通过 setRenderSurfaceTexture() 接入,配合 Flutter Texture widget 使用
  • 当前版本建议在开始推流/播放前选定 backend当前 Demo 在首页设置中统一选择,进入页面后不支持切换

Q5.1TextureView 模式下VOD/RTMP 播放的 BufferQueueProducer timeout 日志是什么?

A

当前 RTMP/VODTextureView / SurfaceTexture 默认走 direct output以缩短首帧和减少黑屏。极端机型或系统版本下仍可能偶现 BufferQueueProducer timeout / BufferQueue has been abandoned 之类系统日志;如果不伴随黑屏、花屏、卡死,通常可视为 Android BufferQueue 机制噪声。开启 playback processing 时texture 路径内部会启用额外的处理中转链,日志形态也可能与 direct 模式不同。

Q5.2attachset 两套 API 的区别?

A

API 谁创建 View 谁释放
attachRenderView() / attachPreview() SDK 创建 SDK 在 release() 时自动释放
setRenderView() / setPreviewView() 调用方创建并传入 调用方负责释放SDK 只做绑定/解绑
setRenderSurfaceTexture() 调用方传入 SurfaceTexture 调用方负责 SurfaceTexture 生命周期

Q6如何接入代理/加速服务(如洋葱盾)?

A SDK 本身不集成任何第三方代理 SDK。业务方需在 SDK 外部完成代理初始化与地址获取,然后通过 SellyCloudManager.setProxyAddress(proxyUrl) 注入。SDK 内部会自动通过代理地址解析真实服务器 IP。

示例流程:

  1. 在 Application 或 Activity 中初始化代理 SDK
  2. 获取本地代理地址(如 http://127.0.0.1:12345
  3. 调用 SellyCloudManager.setProxyAddress("http://127.0.0.1:12345")
  4. 正常进行推流 / 拉流

Demo 中的 KiwiHelper 展示了洋葱盾的完整接入流程,可作为参考。