diff --git a/README.md b/README.md
index 791f007..df9fe0a 100644
--- a/README.md
+++ b/README.md
@@ -1,643 +1 @@
-SellyCloudSDK
-
-SellyRTC Android SDK 接入文档
-
-本文档介绍如何在 Android 中使用 SellyRTC 快速集成一对一或多人音视频通话功能,包括:
-- 基本接入
-- 音视频控制
-- 数据处理(如美颜)
-- 事件回调
-- 通话统计
-- Token 生成与更新机制
-
----
-
-## 目录
-1. 准备工作
-2. 快速开始
- - 创建引擎
- - 设置本地/远端画面
- - 配置视频参数
- - 加入频道
- - 结束通话
-3. 常用功能
- - 开关本地音视频
- - 切换摄像头
- - 静音远端音视频
- - 音频输出控制(扬声器 / 听筒)
- - 发送自定义消息
- - 美颜开关
-4. 视频帧处理(美颜等)
-5. 事件回调 (InteractiveRtcEngineEventHandler)
-6. 通话统计
-7. Token 过期机制
-8. 常见问题
-
----
-
-# 1. 准备工作
-
-## 1.1 集成 SellyCloudSDK
-
-或如果目前是通过本地 AAR 集成(demo 方式):
-
-```gradle
-dependencies {
- implementation files("libs/sellycloudsdk-release.aar")
-}
-```
-
-> 注意:如果你的业务侧还依赖 WebRTC、ijkplayer、美颜等第三方库,请保持与 SDK Demo 中的依赖版本一致。
-
-## 1.2 必要权限
-
-在 `AndroidManifest.xml` 中声明音视频必需权限:
-
-```xml
-
-
-```
-
-在 Android 6.0+ 设备上,运行时还需要动态申请权限,示例见后文(Demo 中的 `requiredPermissions` + `ActivityResultContracts.RequestMultiplePermissions` 已经实现)。
-
-## 1.3 获取 AppId / Secret / Token
-
-从 SellyCloud 控制台获取:
-
-- `signaling_app_id`
-- `signaling_secret`(用于服务端生成 Token)
-- 或直接配置一个测试用的 `signaling_token`
-
-在 Demo 中,这些值通常配置在 `res/values/strings.xml`:
-
-```xml
-your-app-id
-your-secret
-
-```
-
-> 生产环境建议:
-> 不要在 App 里写 secret,而是在你们自己的业务服务器上生成 Token,App 只向服务器请求 Token。
-
----
-
-# 2. 快速开始
-
-以下示例基于 Demo 中的 `InteractiveLiveActivity`,展示最小接入流程。
-
-## 2.1 创建引擎 InteractiveRtcEngine
-
-在 `Activity` 中创建并配置 RTC 引擎:
-
-```kotlin
-private var rtcEngine: InteractiveRtcEngine? = null
-private var beautyRenderer: FURenderer? = null
-private var fuFrameInterceptor: FuVideoFrameInterceptor? = null
-@Volatile private var isFrontCamera = true
-@Volatile private var beautyEnabled: Boolean = true
-
-private fun initRtcEngine() {
- val appId = getString(R.string.signaling_app_id)
- val token = getString(R.string.signaling_token).takeIf { it.isNotBlank() }
-
- // 可选:初始化美颜
- beautyRenderer = FURenderer(this).also { it.setup() }
- fuFrameInterceptor = beautyRenderer?.let {
- FuVideoFrameInterceptor(it).apply {
- setFrontCamera(isFrontCamera)
- setEnabled(beautyEnabled)
- }
- }
-
- rtcEngine = InteractiveRtcEngine.create(
- InteractiveRtcEngineConfig(
- context = applicationContext,
- appId = appId,
- defaultToken = token
- )
- ).apply {
- // 设置回调
- setEventHandler(rtcEventHandler)
-
- // 角色:主播/观众,Demo 里默认主播(BROADCASTER)
- setClientRole(InteractiveRtcEngine.ClientRole.BROADCASTER)
-
- // 配置视频参数(可选,见下一节)
- setVideoEncoderConfiguration(
- InteractiveVideoEncoderConfig(
- width = 640,
- height = 480,
- fps = 20,
- minBitrateKbps = 150,
- maxBitrateKbps = 350
- )
- )
-
- // 默认走扬声器
- setDefaultAudioRoutetoSpeakerphone(true)
-
- // 视频采集前拦截(用于美颜等)
- setCaptureVideoFrameInterceptor { frame ->
- if (!beautyEnabled) return@setCaptureVideoFrameInterceptor frame
- fuFrameInterceptor?.process(frame) ?: frame
- }
- }
-}
-```
-
-生命周期注意:
-在 `onDestroy` 中记得 `leaveChannel()` 并销毁引擎,避免内存泄漏:
-
-```kotlin
-override fun onDestroy() {
- super.onDestroy()
- rtcEngine?.setCaptureVideoFrameInterceptor(null)
- leaveChannel()
- InteractiveRtcEngine.destroy(rtcEngine)
- rtcEngine = null
- // 释放 renderer / 美颜资源...
-}
-```
-
-## 2.2 设置本地 & 远端画面
-
-SellyRTC 使用 `InteractiveVideoCanvas + SurfaceViewRenderer` 来承载视频画面。
-
-### 初始化本地与远端渲染 View
-
-```kotlin
-private var localRenderer: SurfaceViewRenderer? = null
-private val remoteRendererMap = mutableMapOf()
-
-private fun createRenderer(): SurfaceViewRenderer =
- SurfaceViewRenderer(this).apply {
- setZOrderMediaOverlay(false)
- }
-
-private fun setupVideoSlots() {
- // 本地 slot
- if (localRenderer == null) {
- localRenderer = createRenderer()
- }
- localRenderer?.let { renderer ->
- // Demo 中使用自定义的 VideoReportLayout 来承载
- binding.flLocal.attachRenderer(renderer)
- }
-
- // 远端 slot 见 Demo 中的 remoteSlots / ensureRemoteRenderer
-}
-```
-
-### 绑定本地视频
-
-在加入频道前/时,设置本地视频 canvas:
-
-```kotlin
-val renderer = localRenderer ?: createRenderer().also { localRenderer = it }
-rtcEngine?.setupLocalVideo(InteractiveVideoCanvas(renderer, localUserId))
-```
-
-### 绑定远端视频
-
-在 `onUserJoined` 或业务逻辑中,为某个 `userId` 分配一个远端窗口:
-
-```kotlin
-private fun ensureRemoteRenderer(userId: String): SurfaceViewRenderer {
- return remoteRendererMap[userId] ?: createRenderer().also { renderer ->
- remoteRendererMap[userId] = renderer
- rtcEngine?.setupRemoteVideo(InteractiveVideoCanvas(renderer, userId))
- }
-}
-```
-
-> 多人会议:为不同的 `userId` 分配不同的 View / slot,即可实现多路画面显示。
-
-## 2.3 配置视频参数(可选)
-
-视频编码参数需要在加入频道前配置:
-
-```kotlin
-rtcEngine?.setVideoEncoderConfiguration(
- InteractiveVideoEncoderConfig(
- width = 640,
- height = 480,
- fps = 20,
- minBitrateKbps = 150,
- maxBitrateKbps = 350
- )
-)
-// 不设置则使用 SDK 默认配置
-```
-
-## 2.4 加入频道 / 发起通话
-
-### 1)准备 CallType 等入会参数
-
-```kotlin
-val options = InteractiveChannelMediaOptions(
- callType = if (isP2P) CallType.ONE_TO_ONE else CallType.GROUP
-)
-```
-
-其中:
-
-- `CallType.ONE_TO_ONE`:一对一视频通话
-- `CallType.GROUP`:多人会议 / 互动直播
-
-### 2)生成 Token
-
-Demo 中的策略(简化):
-
-```kotlin
-private val defaultTokenTtlSeconds = InteractiveCallConfig.DEFAULT_TOKEN_TTL_SECONDS
-
-private fun buildToken(appId: String, callId: String, userId: String): TokenBundle? {
- val manualToken = getString(R.string.signaling_token).takeIf { it.isNotBlank() }
- if (manualToken != null) {
- return TokenBundle(
- token = manualToken,
- expiresAtSec = parseExprTime(manualToken),
- secret = null
- )
- }
-
- val secret = getString(R.string.signaling_secret)
- if (secret.isBlank()) {
- Toast.makeText(
- this,
- "请在 strings.xml 配置 signaling_secret 用于生成 token,或直接填写 signaling_token",
- Toast.LENGTH_LONG
- ).show()
- return null
- }
-
- return try {
- val generated = TokenGenerator.generate(
- appId = appId,
- userId = userId,
- callId = callId,
- secret = secret,
- ttlSeconds = defaultTokenTtlSeconds
- )
- TokenBundle(
- token = generated.token,
- expiresAtSec = generated.expiresAtSec,
- secret = secret
- )
- } catch (t: Throwable) {
- Toast.makeText(this, "生成 token 失败: ${t.message}", Toast.LENGTH_LONG).show()
- null
- }
-}
-```
-
-> 生产环境建议:
-> 将 `TokenGenerator` 放在你的业务服务器,客户端只请求业务服务器获取 Token。
-
-### 3)调用 joinChannel
-
-```kotlin
-rtcEngine?.joinChannel(
- token = request.token,
- callId = request.callId,
- userId = request.userId,
- options = request.options, // CallType 等
- tokenSecret = request.tokenSecret, // 可为空
- tokenExpiresAtSec = request.tokenExpiresAtSec,
- tokenTtlSeconds = request.tokenTtlSeconds
-)
-```
-
-成功后,会回调:
-
-```kotlin
-override fun onJoinChannelSuccess(channel: String, userId: String, code: Int) {
- // 已成功加入频道,可更新 UI 状态
-}
-```
-
-## 2.5 结束通话
-
-业务结束通话时调用:
-
-```kotlin
-private fun leaveChannel() {
- rtcEngine?.leaveChannel()
- resetUiAfterLeave() // 清 UI、清理 renderer 等
-}
-```
-
-SDK 会通过:
-
-```kotlin
-override fun onLeaveChannel(durationSeconds: Int) {
- // 通话结束时长(秒)
-}
-```
-
-通知已经离开频道。
-
----
-
-# 3. 常用功能
-
-以下示例同样来自 Demo,可直接复用。
-
-## 3.1 开/关本地视频
-
-```kotlin
-private var isLocalVideoEnabled = true
-private var isLocalPreviewEnabled = true
-
-binding.btnToggleCamera.setOnClickListener {
- isLocalVideoEnabled = !isLocalVideoEnabled
- rtcEngine?.enableLocalVideo(isLocalVideoEnabled)
- isLocalPreviewEnabled = isLocalVideoEnabled
- updateControlButtons()
-}
-```
-
-## 3.2 开/关本地音频采集
-
-```kotlin
-private var isLocalAudioEnabled = true
-
-binding.btnToggleMic.setOnClickListener {
- isLocalAudioEnabled = !isLocalAudioEnabled
- rtcEngine?.enableLocalAudio(isLocalAudioEnabled)
- updateControlButtons()
-}
-```
-
-## 3.3 切换前后摄像头
-
-```kotlin
-binding.btnSwitchCamera.setOnClickListener {
- isFrontCamera = !isFrontCamera
- fuFrameInterceptor?.setFrontCamera(isFrontCamera)
- rtcEngine?.switchCamera()
-}
-```
-
-## 3.4 静音远端音视频
-
-按用户静音远端音频 / 视频:
-
-```kotlin
-private fun muteRemoteUserAudio(targetUserId: String, muted: Boolean) {
- rtcEngine?.muteRemoteAudioStream(targetUserId, muted)
-}
-
-private fun muteRemoteUserVideo(targetUserId: String, muted: Boolean) {
- rtcEngine?.muteRemoteVideoStream(targetUserId, muted)
-}
-```
-
-## 3.5 控制音频输出(扬声器 / 听筒)
-
-```kotlin
-private var isSpeakerOn = true
-
-binding.btnToggleAudioRoute.setOnClickListener {
- isSpeakerOn = !isSpeakerOn
- rtcEngine?.setDefaultAudioRoutetoSpeakerphone(isSpeakerOn)
- updateControlButtons()
-}
-```
-
-## 3.6 发送自定义消息
-
-```kotlin
-binding.btnSendMessage.setOnClickListener {
- val text = binding.etMessage.text?.toString()?.trim().orEmpty()
- if (text.isEmpty()) {
- Toast.makeText(this, "请输入消息内容", Toast.LENGTH_SHORT).show()
- } else if (currentCallId == null) {
- Toast.makeText(this, "请先加入频道", Toast.LENGTH_SHORT).show()
- } else {
- rtcEngine?.sendMessage(text) { error ->
- runOnUiThread {
- if (error != null) {
- Toast.makeText(this, "发送失败: ${error.message}", Toast.LENGTH_SHORT).show()
- } else {
- Toast.makeText(this, "已发送", Toast.LENGTH_SHORT).show()
- binding.etMessage.text?.clear()
- binding.tvMessageLog.text = "我: $text"
- }
- }
- }
- }
-}
-```
-
-收到消息的回调见后文 `onMessageReceived`。
-
-## 3.7 美颜开关
-
-```kotlin
-binding.btnToggleBeauty.setOnClickListener {
- beautyEnabled = !beautyEnabled
- fuFrameInterceptor?.setEnabled(beautyEnabled)
- updateControlButtons()
-}
-```
-
----
-
-# 4. 视频帧处理(美颜等)
-
-SellyRTC 提供视频采集前拦截接口,可以在推流前做美颜、滤镜等处理。
-
-在创建引擎后设置:
-
-```kotlin
-rtcEngine?.setCaptureVideoFrameInterceptor { frame ->
- if (!beautyEnabled) return@setCaptureVideoFrameInterceptor frame
- fuFrameInterceptor?.process(frame) ?: frame
-}
-```
-
-其中 `FuVideoFrameInterceptor` 内部使用 `FURenderer` 做实际美颜处理。
-
-> 你也可以替换为自己的处理逻辑:
-> - 对 `frame` 做 GPU 或 CPU 处理
-> - 返回处理后的帧给 SDK 继续编码和发送
-
----
-
-# 5. 事件回调 (InteractiveRtcEngineEventHandler)
-
-实现 `InteractiveRtcEngineEventHandler`,监听通话过程中发生的事件:
-
-```kotlin
-private val rtcEventHandler = 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 onError(code: String, message: String) { ... }
-
- override fun onLocalVideoStats(stats: InteractiveStreamStats) { ... }
-
- override fun onRemoteVideoStats(stats: InteractiveStreamStats) { ... }
-
- override fun onMessageReceived(message: String, userId: String?) { ... }
-
- override fun onTokenWillExpire(token: String?, expiresAt: Long) { ... }
-
- override fun onTokenExpired(token: String?, expiresAt: Long) { ... }
-
- 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?
- ) { ... }
-}
-```
-
-**常见事件说明:**
-
-- `onConnectionStateChanged`:连接状态变化(Disconnected / Connecting / Connected / Reconnecting / Failed)
-- `onUserJoined` / `onUserLeave`:远端用户加入/离开频道
-- `onRemoteVideoEnabled` / `onRemoteAudioEnabled`:远端用户开关音视频
-- `onMessageReceived`:收到自定义消息
-- `onDuration`:通话时长更新(秒)
-- `onError`:错误回调(建议弹窗 + 打日志)
-
----
-
-# 6. 通话统计信息
-
-## 6.1 单路流统计:InteractiveStreamStats
-
-在本地/远端视频统计回调中获取:
-
-```kotlin
-override fun onLocalVideoStats(stats: InteractiveStreamStats) {
- // stats.width / height / fps / videoBitrateKbps / audioBitrateKbps / rttMs 等
-}
-
-override fun onRemoteVideoStats(stats: InteractiveStreamStats) {
- // 针对某个 userId 的码率、分辨率、丢包、RTT 等
-}
-```
-
-你可以将这些信息显示在 UI 上,Demo 中的 `buildStatsLabel` 已经示范了如何构造:
-
-```kotlin
-private fun buildStatsLabel(header: String, stats: InteractiveStreamStats?): String {
- // Res: WxH, FPS, Codec, Video/Audio Kbps, RTT 等
-}
-```
-
-## 6.2 通话结束时长:onLeaveChannel
-
-在 `onLeaveChannel` 中可以拿到本次通话时长(秒),无论是主动离开还是断网/失败结束,只要曾加入成功都会回调:
-
-```kotlin
-override fun onLeaveChannel(durationSeconds: Int) {
- Log.d(TAG, "onLeaveChannel duration=${durationSeconds}s")
-}
-```
-
----
-
-# 7. Token 过期机制
-
-SDK 在 Token 生命周期内会通过事件提醒你续期:
-
-## 7.1 Token 即将过期
-
-```kotlin
-override fun onTokenWillExpire(token: String?, expiresAt: Long) {
- Toast.makeText(
- this@InteractiveLiveActivity,
- "Token 即将过期,请及时续期",
- Toast.LENGTH_LONG
- ).show()
- // 1. 通知业务服务器刷新 Token
- // 2. 拿到新 Token 后调用 rtcEngine?.renewToken(newToken)(具体接口以实际 SDK 为准)
-}
-```
-
-## 7.2 Token 已过期
-
-```kotlin
-override fun onTokenExpired(token: String?, expiresAt: Long) {
- Toast.makeText(
- this@InteractiveLiveActivity,
- "Token 已过期,断线后将无法重连",
- Toast.LENGTH_LONG
- ).show()
-}
-```
-
-> 说明:
-> - Token 过期后,**当前通话不会立刻中断**,但网络异常时自动重连会失败。
-> - 请务必在 `onTokenWillExpire` 阶段就完成续期。
-
----
-
-# 8. 常见问题 (FAQ)
-
-## Q1:多人远端画面如何渲染?
-
-为每一个远端用户(`userId`)分配一个 `SurfaceViewRenderer`,并调用:
-
-```kotlin
-val canvas = InteractiveVideoCanvas(renderer, userId)
-rtcEngine?.setupRemoteVideo(canvas)
-```
-
-在布局层面,你可以将多个 `renderer` 放到不同的容器中(网格布局 / 自定义九宫格等),参考 Demo 中的 `remoteSlots`。
-
----
-
-## Q2:远端画面不显示怎么办?
-
-排查方向:
-
-1. 是否收到了 `onUserJoined` 回调?
-2. 有没有为该 `userId` 调用 `setupRemoteVideo` 并绑定到一个可见的 View?
-3. View 是否被其他控件覆盖?
-4. 远端用户是否已开启视频(可监听 `onRemoteVideoEnabled` 回调)?
-
----
-
-## Q3:如何实现画中画 / 小窗布局?
-
-这是布局层面的工作,与 SDK 解耦:
-
-- 将远端大画面放在父容器(如 `FrameLayout`)中
-- 再将本地小窗 View 作为子 View 添加在右下角,并设置合适的 `layoutParams`
-- SDK 会把视频渲染到对应的 View 上,你只需要控制 View 的大小和位置即可
-
----
-
-## Q4:如何在后台保持通话?
-
-Demo 中使用了一个前台 Service:
-
-```kotlin
-InteractiveForegroundService.start(this)
-// 离开频道后记得 stop
-InteractiveForegroundService.stop(this)
-```
+文档请参考doc目录下
\ No newline at end of file