diff --git a/docs/SellySDK_直播推拉流接入文档_Android.md b/docs/SellySDK_直播推拉流接入文档_Android.md
new file mode 100644
index 0000000..842a804
--- /dev/null
+++ b/docs/SellySDK_直播推拉流接入文档_Android.md
@@ -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
+
+
+```
+
+> 如需截图保存且兼容 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 支持
diff --git a/docs/SellySDK_音视频通话接入文档_Android.md b/docs/SellySDK_音视频通话接入文档_Android.md
new file mode 100644
index 0000000..df5d9e6
--- /dev/null
+++ b/docs/SellySDK_音视频通话接入文档_Android.md
@@ -0,0 +1,375 @@
+# SellyRTC Android SDK 接入文档
+
+本文档用于指导 **Android App 开发者** 快速接入 SellyRTC,完成一对一或多人实时音视频通话能力。
+
+SDK 核心以 `InteractiveRtcEngine` 为中心,通过 `InteractiveRtcEngineEventHandler` 回调通话状态、用户事件、音视频状态及异常。
+
+---
+
+## 目录
+
+1. 准备工作
+2. 快速开始
+3. 基础通话流程
+4. 常用功能
+5. 屏幕分享
+6. 视频帧前后处理
+7. 事件回调(EventHandler)
+8. 通话统计
+9. Token 机制
+10. 常见问题(FAQ)
+
+---
+
+## 准备工作
+
+### 1. 环境要求
+- Android 8.0+(Demo `minSdk` 为 26)
+- 需真机运行(摄像头 / 麦克风)
+
+### 2. 权限配置(AndroidManifest.xml)
+
+```xml
+
+
+```
+
+---
+
+## 快速开始
+
+### 1. 创建引擎
+
+```kotlin
+val appId = getString(R.string.signaling_app_id)
+val token = getString(R.string.signaling_token).takeIf { it.isNotBlank() }
+val kiwiRsName = getString(R.string.signaling_kiwi_rsname).trim()
+
+val rtcEngine = InteractiveRtcEngine.create(
+ InteractiveRtcEngineConfig(
+ context = applicationContext,
+ appId = appId,
+ defaultToken = token,
+ kiwiRsName = kiwiRsName
+ )
+).apply {
+ setEventHandler(eventHandler)
+ setClientRole(InteractiveRtcEngine.ClientRole.BROADCASTER)
+ setVideoEncoderConfiguration(
+ InteractiveVideoEncoderConfig(
+ width = 640,
+ height = 480,
+ fps = 20,
+ minBitrateKbps = 150,
+ maxBitrateKbps = 850
+ )
+ )
+ setDefaultAudioRoutetoSpeakerphone(true)
+}
+```
+
+> `InteractiveRtcEngineConfig` 与默认 token 配置见 `example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt`。
+
+### 2. 设置本地/远端画布
+
+```kotlin
+val localRenderer = SurfaceViewRenderer(this)
+rtcEngine.setupLocalVideo(InteractiveVideoCanvas(localRenderer, userId))
+```
+
+```kotlin
+val remoteRenderer = SurfaceViewRenderer(this)
+rtcEngine.setupRemoteVideo(InteractiveVideoCanvas(remoteRenderer, remoteUserId))
+```
+
+### 3. 加入通话
+
+```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)
+```
+
+### 4. 进阶配置(Demo 未覆盖)
+
+#### 4.1 InteractiveRtcEngineConfig 高级字段
+
+```kotlin
+val config = InteractiveRtcEngineConfig(
+ context = applicationContext,
+ appId = appId,
+ defaultCallType = CallType.ONE_TO_ONE,
+ defaultToken = token,
+ kiwiRsName = kiwiRsName,
+ signalingUrlPrefix = "https://",
+ signalingUrlSuffix = "/signaling"
+)
+```
+
+#### 4.2 InteractiveChannelMediaOptions 订阅控制
+
+```kotlin
+val options = InteractiveChannelMediaOptions(
+ callType = CallType.GROUP,
+ autoSubscribeAudio = true,
+ autoSubscribeVideo = false
+)
+```
+
+#### 4.3 InteractiveVideoEncoderConfig 更多参数
+
+可选项(按需设置):
+
+- `codecType`:编码类型
+- `bitrateMode`:码率控制模式
+- `minBitrateKbps` / `maxBitrateKbps`:码率范围
+- `orientationMode`:画面方向策略
+- `degradationPreference`:弱网降级策略
+- `mirrorMode`:镜像策略
+
+---
+
+## 基础通话流程
+
+1. 创建 `InteractiveRtcEngine`
+2. 设置 `EventHandler`
+3. 配置 `InteractiveVideoEncoderConfig`
+4. 设置本地画布 `setupLocalVideo`
+5. `joinChannel` 加入频道
+6. `onUserJoined` 后设置远端画布
+7. 通话中进行音视频控制
+8. `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.setCaptureVideoFrameInterceptor { frame ->
+ // 在此处理美颜/滤镜,返回新的 frame
+ frame
+}
+```
+
+```kotlin
+rtcEngine.setRenderVideoFrameInterceptor { frame, userId ->
+ // 远端渲染前处理,返回 true 表示继续渲染
+ true
+}
+```
+
+> Demo 中的美颜示例见:
+> `example/src/main/java/com/demo/SellyCloudSDK/beauty/FuVideoFrameInterceptor.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)
+```
+
+---
+
+## 更多 API 速览(含 Demo 未覆盖)
+
+引擎创建与销毁:
+
+- `InteractiveRtcEngine.create(config)`:创建引擎
+- `InteractiveRtcEngine.destroy(engine)` / `engine.destroy()`:释放引擎
+
+通话控制:
+
+- `setEventHandler(handler)`:设置事件回调
+- `setClientRole(role)`:设置角色(主播/观众)
+- `setVideoEncoderConfiguration(config)`:设置编码参数
+- `setDefaultAudioRoutetoSpeakerphone(true/false)`:设置音频输出
+- `joinChannel(...)` / `leaveChannel()`:加入 / 离开频道
+
+本地与远端控制:
+
+- `setupLocalVideo(canvas)` / `setupRemoteVideo(canvas)`:设置画布
+- `clearRemoteVideo(userId)`:清理远端画面
+- `enableLocalVideo(true/false)` / `enableLocalAudio(true/false)`:开关本地音视频
+- `muteRemoteAudioStream(userId, true/false)` / `muteRemoteVideoStream(userId, true/false)`:按用户静音
+- `muteAllRemoteAudioStreams(true/false)` / `muteAllRemoteVideoStreams(true/false)`:全量静音
+- `switchCamera()`:切换摄像头
+
+帧处理与屏幕共享:
+
+- `setCaptureVideoFrameInterceptor(...)`:采集前帧处理
+- `setRenderVideoFrameInterceptor(...)`:渲染前帧处理
+- `startScreenShare(...)` / `stopScreenShare()` / `isScreenSharing()`:屏幕共享
+
+消息与 Token:
+
+- `sendMessage(message, targetUserId, callback)`:发送消息(`targetUserId` 可为空)
+- `renewToken(newToken, expiresAtSec)`:续期 Token
+
+---
+
+## 常见问题(FAQ)
+
+### Q:远端画面不显示?
+1. 是否设置了正确的 `remoteUserId`
+2. 是否在 `onUserJoined` 后调用 `setupRemoteVideo`
+3. 远端是否关闭了视频
+
+### Q:加入频道失败?
+1. 检查 `signaling_app_id` 是否正确
+2. Token 是否为空或已过期
+3. 网络是否受限
+
+### Q:屏幕分享失败?
+1. 是否已获取 `MediaProjection` 授权
+2. Android 14+ 是否启动前台服务
diff --git a/example/build.gradle b/example/build.gradle
index b75979b..6ac8db9 100644
--- a/example/build.gradle
+++ b/example/build.gradle
@@ -3,12 +3,9 @@ plugins {
id 'org.jetbrains.kotlin.android'
}
-def sdkGroupId = rootProject.findProperty("sellySdkGroupId") ?: "com.sellycloud"
-def sdkArtifactId = rootProject.findProperty("sellySdkArtifactId") ?: "sellycloudsdk"
-def sdkVersion = rootProject.findProperty("sellySdkVersion") ?: "1.0.0"
-def hasLocalSdk = rootProject.file("SellyCloudSDK").exists()
+def sdkAarPath = "libs/${findProperty("sellySdkArtifactId") ?: "sellycloudsdk"}-${findProperty("sellySdkVersion") ?: "1.0.0"}.aar"
def releaseStorePath = project.rootProject.file(findProperty("MY_STORE_FILE") ?: "release.keystore")
-def hasReleaseKeystore = releaseStorePath != null && releaseStorePath.exists()
+def hasReleaseKeystore = releaseStorePath.exists()
android {
namespace 'com.demo.SellyCloudSDK'
@@ -67,34 +64,19 @@ android {
}
dependencies {
- if (hasLocalSdk) {
- implementation project(':SellyCloudSDK')
- } else {
- implementation files(
- "libs/${sdkArtifactId}-${sdkVersion}.aar",
- "libs/ijkplayer-cmake-release.aar",
- "libs/Kiwi.aar",
- "libs/libwebrtc.aar"
- )
- implementation 'com.google.code.gson:gson:2.10.1'
- implementation 'com.github.pedroSG94.RootEncoder:library:2.6.6'
- }
-
- implementation fileTree(
- dir: "libs",
- include: ["*.jar", "*.aar"],
- exclude: [
- "${sdkArtifactId}-${sdkVersion}.aar",
- "ijkplayer-cmake-release.aar",
- "Kiwi.aar",
- "libwebrtc.aar"
- ]
+ implementation files(
+ sdkAarPath,
+ "libs/fu_core_all_feature_release.aar",
+ "libs/fu_model_all_feature_release.aar"
)
+ implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.7.0-alpha03'
- implementation 'androidx.constraintlayout:constraintlayout:2.2.0-alpha13'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
implementation 'androidx.core:core-ktx:1.13.1'
+ implementation "com.squareup.okhttp3:okhttp:4.12.0"
+
+ implementation 'androidx.constraintlayout:constraintlayout:2.2.0-alpha13'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.activity:activity-ktx:1.9.2'
@@ -102,7 +84,6 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.4'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
- implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation "io.coil-kt:coil:2.6.0"
testImplementation 'junit:junit:4.13.2'
diff --git a/example/libs/Kiwi.aar b/example/libs/Kiwi.aar
deleted file mode 100644
index 63042fe..0000000
Binary files a/example/libs/Kiwi.aar and /dev/null differ
diff --git a/example/libs/ijkplayer-cmake-release.aar b/example/libs/ijkplayer-cmake-release.aar
deleted file mode 100644
index 88e3792..0000000
Binary files a/example/libs/ijkplayer-cmake-release.aar and /dev/null differ
diff --git a/example/libs/libwebrtc.aar b/example/libs/libwebrtc.aar
deleted file mode 100644
index f2df838..0000000
Binary files a/example/libs/libwebrtc.aar and /dev/null differ
diff --git a/example/libs/sellycloudsdk-1.0.0.aar b/example/libs/sellycloudsdk-1.0.0.aar
index 51f2925..1a9471c 100644
Binary files a/example/libs/sellycloudsdk-1.0.0.aar and b/example/libs/sellycloudsdk-1.0.0.aar differ
diff --git a/example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt b/example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt
index 776ab03..e1dfd11 100644
--- a/example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt
+++ b/example/src/main/java/com/demo/SellyCloudSDK/interactive/InteractiveLiveActivity.kt
@@ -173,7 +173,7 @@ class InteractiveLiveActivity : AppCompatActivity() {
val token = getString(R.string.signaling_token).takeIf { it.isNotBlank() }
val kiwiRsName = getString(R.string.signaling_kiwi_rsname).trim()
beautyRenderer = FURenderer(this).also { it.setup() }
- fuFrameInterceptor = beautyRenderer?.let { FuVideoFrameInterceptor(it).apply {
+ fuFrameInterceptor = beautyRenderer?.let { FuVideoFrameInterceptor(it).apply {
setFrontCamera(isFrontCamera)
setEnabled(beautyEnabled)
} }
diff --git a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt
index 133aca9..5cc313d 100644
--- a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt
+++ b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt
@@ -125,14 +125,6 @@ class LivePlayActivity : AppCompatActivity() {
}
updatePreviewVisibility()
logEvent("状态变更: ${formatState(state)}")
- if (state == SellyPlayerState.Playing && firstVideoFrameElapsedMs == null) {
- val startMs = playAttemptStartElapsedMs
- firstVideoFrameElapsedMs = SystemClock.elapsedRealtime()
- if (startMs != null) {
- firstVideoFrameCostMs = firstVideoFrameElapsedMs!! - startMs
- logEvent("首帧视频耗时=${firstVideoFrameCostMs}ms")
- }
- }
}
}
@@ -140,6 +132,11 @@ class LivePlayActivity : AppCompatActivity() {
runOnUiThread {
hasFirstVideoFrameRendered = true
updatePreviewVisibility()
+ if (firstVideoFrameElapsedMs != null) return@runOnUiThread
+ val startMs = playAttemptStartElapsedMs ?: return@runOnUiThread
+ firstVideoFrameElapsedMs = SystemClock.elapsedRealtime()
+ firstVideoFrameCostMs = firstVideoFrameElapsedMs!! - startMs
+ logEvent("首帧视频耗时=${firstVideoFrameCostMs}ms")
}
}
diff --git a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePushActivity.kt b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePushActivity.kt
index cbb189f..947eb82 100644
--- a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePushActivity.kt
+++ b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePushActivity.kt
@@ -220,8 +220,10 @@ class LivePushActivity : AppCompatActivity() {
try {
val videoConfig = buildVideoConfig(settings)
+ pusherClient.setVideoConfiguration(videoConfig)
pusherClient.attachPreview(binding.previewContainer)
pusherClient.startRunning(currentFacing, videoConfig, null)
+ applyStreamConfig(settings)
} catch (t: Throwable) {
Toast.makeText(this, "初始化预览失败: ${t.message}", Toast.LENGTH_LONG).show()
}
diff --git a/gradle.properties b/gradle.properties
index 84ed79a..edb550f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,6 @@
android.useAndroidX=true
android.enableJetifier=false
+android.experimental.fusedLibrarySupport=true
# Increase Gradle daemon heap to avoid OOM during packaging large AAR/assets
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dkotlin.daemon.jvm.options="-Xmx2g"
diff --git a/settings.gradle b/settings.gradle
index 7a48d7e..270d4e9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,9 +6,6 @@ pluginManagement {
}
}
-def sdkDir = file('SellyCloudSDK')
-def hasLocalSdk = sdkDir.exists()
-
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
@@ -16,11 +13,8 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
- // Local AARs for the demo or the SDK when present
+ // Local AARs for the demo
flatDir {
- if (hasLocalSdk) {
- dirs sdkDir.toPath().resolve('libs').toFile()
- }
dirs file('example/libs')
}
}
@@ -28,7 +22,3 @@ dependencyResolutionManagement {
rootProject.name = "SellyCLoudSDKExample"
include ':example'
-// Use the SDK module only when it exists locally; otherwise rely on the published/prebuilt AAR.
-if (hasLocalSdk) {
- include ':SellyCloudSDK'
-}