【Rokid AR录屏功能逆向分析:通过蓝牙HCI抓包实现CXR SDK未提供的AR录屏功能】

举报
极客小云 发表于 2026/01/06 13:09:50 2026/01/06
【摘要】 Rokid AR眼镜录屏功能开发探索 本文详细介绍了在没有官方API支持的情况下,如何通过蓝牙HCI数据包分析实现Rokid AR眼镜的录屏功能。作者通过抓取和分析Rokid AI App的蓝牙通信数据,逆向工程发现了控制AR录屏的关键指令格式:"Scene_Control!{"name":"mix_record","open&qu

背景介绍

作为一名Rokid Glasses开发者,我最近在开发一个需要AR录屏功能的应用。然而,Rokid官方提供的CXR SDK中并没有直接封装AR录屏的功能。在查阅官方文档和API后,我意识到需要自己探索实现方案。

经过深入研究,我发现了通过蓝牙HCI数据包分析并结合CXR API的方法,成功实现了AR录屏功能。本文将详细介绍我的探索过程和最终解决方案。

问题分析

1. 官方SDK的限制

Rokid CXR SDK提供了丰富的AR眼镜控制功能,但在录屏方面存在以下限制:

  • 没有直接的AR录屏API
  • 现有API主要面向常规应用控制
  • 文档中未提及录屏相关功能

2. 技术思路

由于没有直接的API,我决定从以下角度入手:

  1. 分析官方应用:Rokid AI App如何实现AR录屏?
  2. 蓝牙通信分析:眼镜与手机之间通过蓝牙传输哪些控制指令?
  3. 协议逆向:能否找到录屏的控制协议?

技术探索过程

步骤1:蓝牙HCI数据包抓取

首先,我开启了Android设备的蓝牙HCI日志功能:

# 开启蓝牙HCI日志
adb shell setprop persist.bluetooth.btsnoopenable true
adb shell setprop persist.bluetooth.btsnooplogmode full
adb shell stop bluetooth
adb shell start bluetooth

# 从设备拉取日志
adb pull /data/misc/bluetooth/logs/btsnoop_hci.log

步骤2:使用Wireshark分析数据包

将抓取到的HCI日志用Wireshark打开,并使用过滤器查看关键数据:

# 过滤RFCOMM协议
btrfcomm

# 查看Scene_Control相关的包
frame contains "Scene_Control"

步骤3:发现关键数据包

经过仔细分析,我发现了录屏控制的关键数据包:

开始录屏指令:

02 33 00 52 00 4e 00 53 00 23 ff 93 01 00 00 00 49 05 04 75 75 53 4f 81 20 41 03 53 79 73 00 00 00 38 05 02 53 53 0d 53 63 65 6e 65 5f 43 6f 6e 74 72 6f 6c 21 7b 22 6e 61 6d 65 22 3a 22 6d 69 78 5f 72 65 63 6f 72 64 22 2c 22 6f 70 65 6e 22 3a 74 72 75 65 7d 46

停止录屏指令:

02 33 00 52 00 4e 00 53 00 23 ff 93 01 00 00 00 49 05 04 75 75 53 4f 81 20 41 03 53 79 73 00 00 00 38 05 02 53 53 0d 53 63 65 6e 65 5f 43 6f 6e 74 72 6f 6c 21 7b 22 6e 61 6d 65 22 3a 22 6d 69 78 5f 72 65 63 6f 72 64 22 2c 22 6f 70 65 6e 22 3a 66 61 6c 73 65 7d 46

步骤4:解析协议格式

通过分析数据包,我发现了协议的结构:

Scene_Control!{"name":"mix_record","open":true/false}

这是一个简单的JSON格式控制指令:

  • name: 场景名称,AR录屏对应mix_record
  • open: 控制开关,true开始录屏,false停止录屏

解决方案实现

基于以上分析,我利用CXR SDK现有的CxrApi构建了AR录屏控制功能:

1. 核心场景控制函数

fun controlScene(sceneType: String, enable: Boolean, extraArgs: String = ""): ValueUtil.CxrStatus {
    Log.i("CxrApi", "controlScene sceneType:$sceneType, enable:$enable, extraArgs:$extraArgs")
    var status = ValueUtil.CxrStatus.REQUEST_FAILED
    return try {
        // 构建JSON参数
        val json = JSONObject().apply {
            put("name", sceneType)
            put("open", enable)
            if (extraArgs.isNotBlank()) {
                put("param", extraArgs)
            }
        }
        val jsonString = json.toJSONString()
        Log.i("CxrApi", "sceneJson: $jsonString")
        
        // 创建Caps对象并写入数据
        val caps = Caps().apply {
            write("Scene_Control")
            write(jsonString)
        }
        
        // 发送控制请求
        status = CxrController.getInstance().request(CxrApi.getInstance().w, "Sys", caps, null)
        status
    } catch (e: Exception) {
        Log.e("CxrApi", "controlScene error: ${e.message}")
        ValueUtil.CxrStatus.REQUEST_FAILED
    }
}

2. 封装的AR录屏功能

// 封装的AR录屏功能
fun controlSystemMixRecord(toOpen: Boolean) {
    when(controlScene("mix_record", toOpen)) {
        ValueUtil.CxrStatus.REQUEST_SUCCEED -> {
            Log.d(TAG, "Video record ${if (toOpen) "started" else "stopped"}")
        }
        ValueUtil.CxrStatus.REQUEST_FAILED -> {
            Log.e(TAG, "Failed to ${if (toOpen) "start" else "stop"} video record")
        }
        ValueUtil.CxrStatus.REQUEST_WAITING -> {
            Log.e(TAG, "Requested but Glasses is not ready")
        }
        else -> {
            Log.e(TAG, "Unknown error")
        }
    }
}

// 启动AR录屏
fun startARRecording() {
    controlSystemMixRecord(true)
}

// 关闭AR录屏
fun stopARRecording() {
    controlSystemMixRecord(false)
}

3. 完整的使用示例

class ARRecordingActivity : AppCompatActivity() {
    
    private lateinit var arRecordingController: ARRecordingController
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_ar_recording)
        
        arRecordingController = ARRecordingController()
        
        // 开始AR录屏
        findViewById<Button>(R.id.btn_start_record).setOnClickListener {
            arRecordingController.startARRecording()
        }
        
        // 停止AR录屏
        findViewById<Button>(R.id.btn_stop_record).setOnClickListener {
            arRecordingController.stopARRecording()
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 确保停止录屏
        arRecordingController.stopARRecording()
    }
}

技术细节说明

1. 协议栈分析

通过HCI日志分析,我了解到完整的通信协议栈:

应用层: Scene_Control{"name":"mix_record","open":true}
    ↓
传输层: RFCOMM (蓝牙串口仿真协议)
    ↓
数据链路层: L2CAP
    ↓
物理层: 蓝牙HCI

2. 关键发现

  • 场景名称: mix_record 表示混合录制(可能同时录制AR画面和音频)
  • 控制参数: 只需要简单的开关控制
  • 系统目标: 发送到Sys系统服务

3. 错误处理

代码中完整处理了CXR API返回的各种状态:

  • REQUEST_SUCCEED: 请求成功
  • REQUEST_FAILED: 请求失败
  • REQUEST_WAITING: 设备未就绪

使用注意事项

1. 权限要求

在AndroidManifest.xml中添加必要权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />

2. 设备连接状态

确保设备已正确连接:

if (CxrApi.getInstance().isConnected()) {
    // 设备已连接,可以控制
} else {
    // 设备未连接,需要先连接
    Toast.makeText(this, "请先连接Rokid眼镜", Toast.LENGTH_SHORT).show()
}

3. 录屏文件位置

录制的视频默认保存在:

/sdcard/ScreenRecorder/vid-{timestamp}.mp4

总结与展望

通过这次探索,我成功实现了以下目标:

✅ 已实现功能

  1. 通过蓝牙HCI日志分析找到了AR录屏的控制协议
  2. 利用现有CXR API实现了AR录屏控制
  3. 提供了完整的开始/停止录屏接口
  4. 完善了错误处理和状态反馈

🔮 未来展望

  1. 探索更多AR场景控制功能
  2. 研究视频流实时处理
  3. 开发AR内容创作工具链
  4. 构建AR应用开发框架

致谢

感谢Rokid提供的硬件平台和CXR SDK,虽然某些功能没有直接提供API,但通过深入的技术探索,我们仍然能够实现所需的功能。

希望这篇文章能够帮助到其他需要AR录屏功能的开发者。如果你有任何问题或改进建议,欢迎在评论区留言交流!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。