Camera SDK 集成指南
Camera SDK(sdk-camera)提供 Insta360 相机的连接管理、拍摄控制、实时预览、文件管理和固件升级能力,帮助开发者快速将 Insta360 相机接入自有 Android 应用。
目录
1. SDK 初始化
在 Application.onCreate() 中完成初始化,其他模块依赖此步骤,必须最先执行。
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
InstaCameraSDK.init(this) {
fileDir = filesDir.absolutePath
cacheDir = cacheDir.absolutePath
logLevel = LogLevel.DEBUG
}
}
}2. 设备连接
2.1 创建 CameraDevice
每种连接类型独立创建一个实例。建议在持有连接生命周期的 ViewModel 中创建,并在 onCleared() 里调用 release()。
// 按需选择连接类型
val device = CameraDevice.get(ConnectType.WIFI)
val device = CameraDevice.get(ConnectType.BLE)
val device = CameraDevice.get(ConnectType.USB)2.2 Wi-Fi 连接
相机和手机须已处于同一 Wi-Fi 网络,或手机已连接到相机热点。
// 协程方式
viewModelScope.launch {
device.connect()
.onSuccess { /* 连接成功,保存 device 实例供后续使用 */ }
.onFailure { e ->
// NativeException 包含底层错误码,可用于问题定位
val msg = if (e is NativeException) "[${e.nativeErrorCode}] ${e.message}" else e.message
}
}
// 也可以指定 networkId
device.connect(connectHint = networkId)2.3 USB 连接
设备通过 USB 连接手机时使用。
viewModelScope.launch {
device.connect()
.onSuccess { /* 连接成功 */ }
.onFailure { /* 处理错误 */ }
}2.4 BLE 连接
BLE 连接分为扫描和连接两步。
扫描
// SDK 内置扫描:回调中的 BleDeviceCore 可直接用于后续连接
device.scan(
timeoutMs = 10_000L,
bleScanCallback = bleScanCallback {
started { /* 扫描开始,可清空设备列表 */ }
scanning { bleDeviceCore ->
// 建议按设备名过滤,只展示相机类设备
}
finished { devices -> /* 扫描结束 */ }
error { e -> /* 扫描失败 */ }
}
)
// 不再需要扫描结果时主动停止
device.stopScan()连接(使用 SDK BleDeviceCore)
// bleDeviceCore 来自 bleScanCallback.scanning 回调
viewModelScope.launch {
device.connect(bleDeviceCore)
.onSuccess { /* 连接成功 */ }
.onFailure { /* 处理错误 */ }
}连接(使用系统 BluetoothDevice)
如果已通过系统 BLE API 扫描得到 BluetoothDevice,可直接传入:
// bluetoothDevice 来自系统 BluetoothLeScanner 回调
viewModelScope.launch {
device.connectBle(bluetoothDevice)
.onSuccess { /* 连接成功 */ }
.onFailure { /* 处理错误 */ }
}2.5 通过蓝牙引导 Wi-Fi 连接
适用于相机 Wi-Fi 未开启、或 SSID/密码未知的场景。BLE 连接建立后,从相机读取 Wi-Fi 凭证,再通过系统 Wi-Fi API 连入相机热点,最终切换为 Wi-Fi 连接。
Wi-Fi 连接延迟低、支持实时预览;纯 BLE 连接不支持预览流。
业务流程
1. BLE 扫描 → 选择设备 → BLE 连接(参见 2.4 节)
2. 通过 BLE 读取相机 Wi-Fi 凭证(SSID / 密码)
3. 调用系统 Wi-Fi API 连接到相机热点,获取 Network 对象
4. 将进程网络绑定到该 Network(否则 HTTP 请求仍走手机默认网络)
5. 断开 BLE,用 Wi-Fi networkHandle 发起 SDK Wi-Fi 连接
6. 断开时解除进程网络绑定主要 API
// Step 2:通过 BLE 读取相机 Wi-Fi 凭证
val wifiData = bleDevice.system.getWifiData().getOrNull()
// wifiData.ssid — 相机热点 SSID
// wifiData.pwd — 相机热点密码
// Step 3:系统 Wi-Fi 连接(Android 10+ 用 WifiNetworkSpecifier)
// 在 ConnectivityManager.NetworkCallback.onAvailable 中获得 Network 对象
val network: Network? = connectSystemWifi(ssid = wifiData.ssid, password = wifiData.pwd)
// Step 4:绑定进程网络,使后续 HTTP/SDK 通信走相机 Wi-Fi
connectivityManager.bindProcessToNetwork(network)
// Step 5:断开 BLE,切换为 Wi-Fi 连接
bleDevice.disconnect() // suspend fun,返回 Result<Unit>
bleDevice.release()
val wifiDevice = CameraDevice.get(ConnectType.WIFI)
wifiDevice.connect(network.networkHandle) // 传入 networkHandle 指定网络
// Step 6:断开时解除绑定
connectivityManager.bindProcessToNetwork(null)注意:
bindProcessToNetwork会影响整个进程的网络路由,断开连接后必须及时调用bindProcessToNetwork(null)恢复,否则应用其他网络请求也会被路由到相机热点。
2.6 蓝牙唤醒
相机处于关机状态时,可通过 BLE 广播唤醒,唤醒成功后再发起连接。
// 需要知道目标相机型号和设备名
device.bleWakeUp(
cameraType = CameraType.X4,
deviceName = "Insta360 X4 XXXX",
listener = object : BleWakeUpListener {
override fun onWakeUpSuccess() {
// 唤醒成功,此时可发起 BLE 连接
}
override fun onWakeUpError(errCode: Int) {
// 唤醒失败
}
}
)2.7 断开连接与资源释放
// 主动断开(协程方式,返回 Result<Unit>)
device.disconnect()
// 主动断开(回调方式)
device.disconnect(callback)
// 页面/ViewModel 销毁时释放资源
override fun onCleared() {
device.release()
}2.8 监听意外断连
断连监听建议在连接成功后立即注册:
val disconnectListener = object : DisconnectListener {
override fun onDisconnect(throwable: Throwable?) {
// throwable 为 null 表示主动断开,非 null 表示意外断开
// 建议清除本地持有的 device 引用,并提示用户重新连接
}
}
device.registerDisconnectListener(disconnectListener)
// 不再需要时注销,防止泄漏
device.unregisterDisconnectListener(disconnectListener)3. 系统状态监听
前置条件:已完成设备连接,持有有效的
CameraDevice实例。
3.1 注册与注销时机
建议在进入业务页面时注册、离开时注销,以获取实时状态推送。
// 注册
device.registerDisconnectListener(disconnectListener)
device.system.registerBatteryListener(batteryListener)
device.system.registerStorageStatusListener(storageListener)
device.system.registerTemperatureListener(temperatureListener)
device.system.registerChargeBoxStatusListener(chargeBoxListener)
// 注销(与注册位置对称)
device.unregisterDisconnectListener(disconnectListener)
device.system.unregisterBatteryListener(batteryListener)
device.system.unregisterStorageStatusListener(storageListener)
device.system.unregisterTemperatureListener(temperatureListener)
device.system.unregisterChargeBoxStatusListener(chargeBoxListener)3.2 电量监听
val batteryListener = object : BatteryListener {
override fun onBatteryLevelChange(batteryData: BatteryData) {
val percent = if (batteryData.scale > 0)
(batteryData.level * 100f / batteryData.scale).roundToInt()
else batteryData.level
// 更新电量显示
}
override fun onLowBatteryWarning() {
// 建议弹出提示,告知用户尽快充电
}
}3.3 存储状态监听
val storageListener = object : StorageStateListener {
override fun onStorageStateChanged(storageData: StorageData) {
when (storageData.state) {
StorageData.State.PASS -> { /* 正常:可显示已用/总容量 */ }
StorageData.State.NO_CARD -> { /* 未插 SD 卡 */ }
StorageData.State.NO_SPACE -> { /* 空间已满 */ }
StorageData.State.INVALID_FORMAT -> { /* 格式异常,建议提示格式化 */ }
StorageData.State.WP_CARD -> { /* 写保护 */ }
StorageData.State.OTHER_ERROR -> { /* 其他错误 */ }
}
// 容量信息(字节),total/free 可能为 0(未插卡时)
val usedBytes = storageData.total - storageData.free
}
}3.4 温度监听
val temperatureListener = object : TemperatureListener {
override fun onTemperatureUpdate(tempState: TempState) {
when (tempState) {
TempState.HIGH -> {
// 建议提示用户暂停拍摄并为设备降温
}
TempState.HIGH_SHUTDOWN -> {
// 相机可能即将自动关机,建议立即停止操作
}
else -> { /* 温度正常 */ }
}
}
}3.5 主动拉取数据
除了监听推送,也可以在需要时主动拉取最新值:
viewModelScope.launch {
// fetch 系列方法从相机拉取最新数据;get 系列方法读取本地缓存
val battery = device.system.fetchBatteryData().getOrNull()
val storage = device.system.fetchStorageData().getOrNull()
val firmware = device.system.fetchFirmwareRevision().getOrNull()
val serialNum = device.system.fetchSerialNumber().getOrNull()
val cameraType = device.system.fetchCameraType().getOrNull()
}4. 拍摄控制与参数
前置条件:已连接相机,持有
CameraDevice.capture。
4.1 加载参数配置
连接相机后必须先调用 loadJson(),SDK 才能获知当前相机支持哪些参数。
// 建议在连接成功的回调中调用,后续参数操作才能正常工作
viewModelScope.launch {
device.capture.loadJson()
}4.2 查询支持的参数
不同型号的相机支持不同的参数组合,建议先查询再操作:
viewModelScope.launch {
// 获取当前相机支持的所有参数
val supportedParams: List<CameraParam<*>> = device.capture.getSupportParam()
// 查询拍摄模式支持列表
val modes: List<FunctionMode> = device.capture.functionMode.getSupported().getOrNull() ?: emptyList()
// 查询视频分辨率支持列表
val resolutions = device.capture.videoResolution.getSupported().getOrNull() ?: emptyList()
}4.3 读写拍摄参数
所有参数通过 CameraParam<T> 统一操作:
viewModelScope.launch {
val capture = device.capture
// 读取当前值(从本地缓存)
val currentMode = capture.functionMode.getValue().getOrNull()
val currentRes = capture.videoResolution.getValue().getOrNull()
// 从相机拉取最新值
val latestMode = capture.functionMode.fetchValue().getOrNull()
// 写入新值(会同步通知相机)
capture.functionMode.setValue(FunctionMode.NORMAL_VIDEO)
capture.videoResolution.setValue(RecordResolution.RES_5_7K_30FPS)
capture.hdrSwitch.setValue(true)
capture.exposureISO.setValue(400)
capture.whiteBalance.setValue(6500)
}参数变化监听(适用于多端同步场景):
// 注册
val listener: (RecordResolution) -> Unit = { newValue ->
updateResolutionLabel(newValue)
}
device.capture.videoResolution.addListener(listener)
// 注销
device.capture.videoResolution.removeListener(listener)4.4 开始与停止拍摄
viewModelScope.launch {
// 拍摄行为由当前 functionMode 决定(录像/拍照/延时等)
device.capture.startCapture()
// 停止拍摄(录像模式)
device.capture.stopCapture()
// 查询是否正在拍摄
val working = device.capture.isWorking()
// 获取当前模式剩余拍摄量(录像为剩余秒数,拍照为剩余张数)
val remaining = device.capture.getRemaining().getOrNull()
}4.5 监听拍摄状态
val captureListener = object : CaptureStatusListener {
override fun onCaptureStarting(functionMode: FunctionMode) {
// 拍摄即将开始,建议禁用参数修改 UI
}
override fun onCaptureWorking(functionMode: FunctionMode) {
// 拍摄正式进入工作状态
}
override fun onCaptureStopping(functionMode: FunctionMode) {
// 拍摄正在停止中
}
override fun onCaptureFinish(functionMode: FunctionMode, filePaths: List<String>) {
// 拍摄完成,filePaths 为相机端生成的文件路径
}
override fun onCaptureError(functionMode: FunctionMode, throwable: Throwable) {
// 拍摄发生错误
}
override fun onCaptureTimeChanged(functionMode: FunctionMode, captureTime: Long) {
// 录制时长更新(录像模式),captureTime 单位为秒
}
override fun onCaptureCountChanged(functionMode: FunctionMode, captureCount: Int) {
// 已拍张数更新(连拍/间隔拍摄模式)
}
override fun onCaptureSubStatusChanged(functionMode: FunctionMode, subStatus: CameraCaptureStatus.SubStatus) {
// 拍摄子状态变化(如 HDR 处理、预录制等)
}
}
device.capture.registerCaptureStatusListener(captureListener)
// 不再使用时注销
device.capture.unregisterCaptureStatusListener(captureListener)4.6 GPS 数据注入
在录像时可以将 GPS 坐标嵌入媒体文件,由调用方自行管理定位数据源,在开始/停止拍摄时传入,录制过程中如需更新再调用 setGpsInfo。
val gpsInfo = GpsInfo(
latitude = currentLatitude,
longitude = currentLongitude,
altitude = currentAltitude,
timestampMs = System.currentTimeMillis()
)
// 开始拍摄时携带 GPS 信息
device.capture.startCapture(gpsInfo)
// 录制过程中更新 GPS 信息(如每秒一次)
device.capture.setGpsInfo(gpsInfo)
// 停止拍摄时携带最新 GPS 信息
device.capture.stopCapture(gpsInfo)5. 实时预览
方式一:使用 InstaCapturePlayerView(推荐)
SDK 提供的 InstaCapturePlayerView 封装了解码和渲染,可快速接入。
接入流程
注册 CameraStreamListener
→ onOpened() 时调用 previewView.prepare() + play()
→ PlayerViewListener.onLoadingFinish() 时将 pipeline 注入相机
→ onParamsChanged() 时同步分辨率/偏移量/裁剪信息到播放器实现要点
// 1. 创建播放器视图(通常在 onCreate() 中)
val previewView = InstaCapturePlayerView(context)
// 2. 注册流状态监听
val streamListener = object : CameraStreamListener {
override fun onOpened() {
// 流已就绪,初始化播放器
previewView.prepare(PreviewParams())
previewView.play()
}
override fun onParamsChanged(paramsUpdate: PreviewStreamParamsUpdate) {
// 同步偏移量(用于全景拼接)
paramsUpdate.offsetData?.let { offset ->
previewView.setOffset(
OffsetData(offset.offsetV1, offset.offsetV2, offset.offsetV3),
paramsUpdate.stabOffset.orEmpty()
)
}
// 同步分辨率和帧率
if (paramsUpdate.previewWidth > 0 && paramsUpdate.previewHeight > 0) {
previewView.setPreviewResolution(paramsUpdate.previewWidth, paramsUpdate.previewHeight)
previewView.setFps(paramsUpdate.previewFps)
}
// 同步窗口裁剪信息(用于全景拼接)
paramsUpdate.windowCropInfo?.let { crop ->
previewView.setWindowCropInfo(
MediaWindowCropInfo(crop.src_width, crop.src_height, crop.dst_width, crop.dst_height, crop.crop_offset_x, crop.crop_offset_y)
)
}
}
override fun onOpening() {}
override fun onIdle() {}
}
// 3. 注册播放器视图监听,在 onLoadingFinish 时建立 Pipeline 连接
// Pipeline 是相机预览流数据传递到媒体侧渲染的通道
val playerViewListener = object : PlayerViewListener {
override fun onLoadingFinish() {
// 将播放器的渲染管线注入相机预览,完成数据通路绑定
val pipeline = previewView.getPipeline() ?: return
device.preview.setPipeline(pipeline)
}
override fun onReleaseCameraPipeline() {
// 播放器释放管线时,通知相机侧同步清除
device.preview.setPipeline(null)
}
override fun onFail(exception: InstaException) { /* 处理错误 */ }
override fun onLoadingStatusChanged(isLoading: Boolean) {}
override fun onFirstFrameRendered() {}
}
previewView.setListener(playerViewListener)
// 4. 注册姿态监听,根据相机朝向自动旋转预览画面
val postureListener = object : CameraPostureUpdate {
override fun updatePosture(cameraPosture: CameraPosture) {
val deg = when (cameraPosture) {
CameraPosture.CAMERA_POSTURE_ROTATE_90 -> 90
CameraPosture.CAMERA_POSTURE_ROTATE_180 -> 180
CameraPosture.CAMERA_POSTURE_ROTATE_270 -> 270
else -> 0
}
previewView.updateRotate(deg, 0, cameraPosture.nativeValue, cameraPosture.nativeValue)
previewView.redetectCameraRotation()
}
}
// 5. 开启预览流
fun startPreview() {
device.preview.init(application)
device.preview.registerCameraStreamListener(streamListener)
device.preview.registerPostureListener(postureListener)
device.preview.startStream()
}
// 6. 关闭预览流(建议在 onStop/onDestroy 时调用)
fun stopPreview() {
device.preview.unregisterCameraStreamListener(streamListener)
device.preview.unregisterPostureListener(postureListener)
device.preview.stopStream()
previewView.destroy()
}方式二:接收原始流数据(自定义渲染)
适合需要自定义解码、截帧或二次处理的场景。
接入流程
注册 CameraStreamListener.onStreamDataNotify
→ 将 PreviewStreamFrame 放入 Channel 缓冲
→ 后台协程消费:聚合同一 timestamp 的分片 → 完整帧送入解码器
→ MediaCodec 解码 → 渲染到 SurfaceView重要:分片聚合
相机推流时,同一帧数据可能通过多次 onStreamDataNotify 回调分批下发,这些分片共享相同的 timestamp。必须将同一 timestamp 的所有分片拼合成完整帧,再送入解码器;直接喂单个分片会导致绿屏/花屏。
// 1. 用 Channel 缓冲原始帧(避免在回调线程执行耗时操作)
val frameChannel = Channel<PreviewStreamFrame>(Channel.UNLIMITED)
val streamListener = object : CameraStreamListener {
override fun onStreamDataNotify(streamData: PreviewStreamFrame) {
frameChannel.trySend(streamData)
}
override fun onOpening() {}
override fun onOpened() {}
override fun onIdle() {}
override fun onParamsChanged(paramsUpdate: PreviewStreamParamsUpdate) {}
}
// 2. 后台协程消费:聚合分片,完整帧送入解码器
lifecycleScope.launch(Dispatchers.IO) {
val buffer = ByteArrayOutputStream(64 * 1024)
var currentTs: Long? = null
for (frame in frameChannel) {
if (!frame.type.isVideo) continue // 只处理视频帧
val ts = frame.timestamp
if (currentTs != null && ts != currentTs) {
// timestamp 变化 → 上一帧已完整,送入解码器
// 时间戳从毫秒转微秒(MediaCodec 要求)
decoder.offer(buffer.toByteArray(), currentTs!! * 1000L)
buffer.reset()
}
currentTs = ts
buffer.write(frame.data)
}
}
// 3. 开启 / 停止预览
fun startPreview() {
device.preview.registerCameraStreamListener(streamListener)
device.preview.startStream()
// 建议立即请求一帧关键帧,加快首屏出画面速度
device.preview.requestStreamIframe()
}
fun stopPreview() {
device.preview.unregisterCameraStreamListener(streamListener)
device.preview.stopStream()
}H264/H265 编码识别
预览流的编码类型(H264/H265)可能因相机型号或固件版本不同而有差异,建议运行时查询:
// 主动查询当前编码类型
viewModelScope.launch {
val encode = device.system.fetchVideoEncodeType().getOrNull()
val isH265 = encode == VideoEncode.ENCODE_H265
// 据此初始化对应的 MediaCodec(video/avc 或 video/hevc)
// 或通知 SDK 解码侧
device.preview.setStreamEncode(isH265)
}也可以解析码流中的 NAL 参数集自动判断:H264 含 SPS(nalType=7)/PPS(nalType=8),H265 含 VPS(nalType=32)/SPS(nalType=33)/PPS(nalType=34)。
动态调整预览码率
viewModelScope.launch {
device.preview.setVideoBitrate(4 * 1024 * 1024) // 4 Mbps,单位 bps
}直播推流
前置条件:相机已连接并持有
CameraDevice.preview;必须先将拍摄模式切换为FunctionMode.VIDEO_LIVE,再开启预览流并等待其打开(CameraStreamListener.onOpened),否则startLive返回失败。
1. 注册直播状态监听
val liveListener = object : CameraLiveListener {
override fun onStarted() { /* 推流已建立 */ }
override fun onFps(fps: Int) { /* 实时推流帧率 */ }
override fun onStopped() { /* 推流已停止 */ }
override fun onFailed(errorCode: Int, message: String?) { /* 推流失败 */ }
}
device.preview.registerCameraLiveListener(liveListener)2. 切到直播模式并开启预览流
// 切换拍摄模式(参数读写参见 4.3 节)
device.capture.functionMode.setValue(FunctionMode.VIDEO_LIVE)
// 开启预览流(参见 5.1 节方式一/方式二)
device.preview.startStream()3. 发起推流
viewModelScope.launch {
device.preview.startLive(
CameraLiveParams(
rtmpUrl = "rtmp://your-server/live/stream-key",
width = 1920,
height = 960,
fps = 30,
bitrate = 4, // 单位 Mbps
// netId 默认 -1;绑定指定网络(如蜂窝)时传入对应 network id
)
).onSuccess {
// 请求已受理,实际推流状态由 liveListener.onStarted 驱动
}.onFailure { /* 处理推流请求失败 */ }
}4. 停止推流
viewModelScope.launch {
device.preview.stopLive()
}
// 页面退出时注销监听
device.preview.unregisterCameraLiveListener(liveListener)6. 文件管理
前置条件:相机已连接,持有
CameraDevice.file。Wi-Fi 连接下文件地址通过 HTTP 访问,BLE/USB 连接下不支持文件下载。
6.1 获取文件列表
viewModelScope.launch {
val file = device.file
// 获取全量列表,返回文件 URI 列表
val uris: List<String> = file.listMediaFiles().getOrNull() ?: return@launch
// 按类型过滤
val videoUris = file.listMediaFiles(MediaFileType.VIDEO).getOrNull() ?: emptyList()
// 分页获取(文件多时推荐)
// 返回值:Pair<当前页 URI 列表, 总文件数>
val (pageUris, total) = file.listMediaFiles(
start = 0, limit = 20
).getOrNull() ?: return@launch
}6.2 下载文件
下载时需拼接完整 URL:getEndpoint() 返回类似 http://192.168.42.1:80/ 的前缀。
viewModelScope.launch {
val fullUrl = device.file.getEndpoint() + relativeUri
device.file.downloadMediaFile(
url = fullUrl,
targetDir = targetDirPath,
progressCallback = { downloaded, total ->
val percent = (downloaded * 100f / total).toInt()
// 更新进度 UI
}
).onSuccess { localPath ->
// localPath 为下载后的本地绝对路径
}.onFailure { e ->
// 处理下载失败
}
}建议:使用
Channel或队列串行处理多个下载任务,避免并发下载导致内存压力或网络拥塞。
6.3 删除文件
viewModelScope.launch {
// 支持批量删除
device.file.deleteMediaFiles(uri1, uri2, uri3)
.onSuccess { /* 删除成功,刷新文件列表 */ }
.onFailure { /* 处理失败 */ }
}6.4 下载相机日志
用于故障排查,日志文件以压缩包形式下载。
viewModelScope.launch {
device.file.downloadCameraLogFile(
targetDir = cacheDir.absolutePath,
progressCallback = { downloaded, total -> /* 更新进度 */ }
).onSuccess { logPath ->
// 可通过系统分享或上传到服务器
}.onFailure { /* 处理失败 */ }
}7. 固件升级
前置条件:相机已连接,持有
CameraDevice.firmware。升级过程中请勿断开连接。
7.1 查询当前版本
val version = device.firmware.getVersion().getOrNull()7.2 执行升级
固件文件通常从应用服务器下载后保存到本地,扩展名为 .bin 或 .pkg。
// 回调方式
device.firmware.upgradeFirmware(
filePath = localFirmwarePath,
callback = progressCallback {
success {
// 升级成功,相机可能自动重启
}
progress { p ->
// p 为 0.0 ~ 1.0 的进度值
updateProgress((p * 100).toInt())
}
throwable { e ->
// 升级失败,建议提示用户重试
}
}
)
// 协程方式
viewModelScope.launch {
device.firmware.upgradeFirmware(
filePath = localFirmwarePath,
progress = { p -> updateProgress((p * 100).toInt()) }
).onSuccess { /* 升级成功 */ }
.onFailure { /* 升级失败 */ }
}8. Wi-Fi 设置
前置条件:相机已连接(通常为 BLE 连接后操作 Wi-Fi),持有
CameraDevice.system。
8.1 查询 Wi-Fi 信息
viewModelScope.launch {
val wifiData = device.system.fetchWifiData().getOrNull()
val channelList = device.system.fetchWifiChannelList().getOrNull()
// wifiData 包含 ssid、密码、当前信道等信息
// channelList 包含当前地区可用信道列表
}8.2 开启 / 关闭 Wi-Fi
viewModelScope.launch {
// 开启 Wi-Fi,可指定信道(0 表示自动选择)
device.system.openCameraWiFi(channel = 6)
.onSuccess {
// Wi-Fi 重启需要一定时间,建议等待约 10 秒后再查询最新状态
}
// 关闭 Wi-Fi
device.system.closeCameraWiFi()
}8.3 切换信道 / 重启 Wi-Fi
viewModelScope.launch {
device.system.resetCameraWiFi(channel = 11)
.onSuccess {
// 同样需等待约 10 秒后再拉取新的信道数据
// delay(10_000)
val updated = device.system.fetchWifiData().getOrNull()
}
}8.4 设置 Wi-Fi 国家码
国家码影响可用信道范围。更改后需重启 Wi-Fi 生效。
viewModelScope.launch {
device.system.setWiFiCountry("US")
.onSuccess {
// 建议紧接着重启 Wi-Fi
device.system.resetCameraWiFi()
}
}9. 设备管理
前置条件:相机已连接,持有
CameraDevice.system。
9.1 激活相机
部分功能在相机未激活状态下受限,首次接入时需激活。
viewModelScope.launch {
device.system.activeCamera(
appId = "your_app_id",
secretKey = "your_secret_key"
).onSuccess { /* 激活成功 */ }
.onFailure { /* 激活失败,检查 appId/secretKey 是否正确 */ }
}9.2 屏幕锁定
viewModelScope.launch {
device.system.setLockScreenState(LockScreenState.LOCK)
.onSuccess { /* 锁屏已设置 */ }
.onFailure { /* 处理失败 */ }
}9.3 关机
// 关机为单向指令,无返回值
// 相机收到指令后自行关机,连接会随之断开
device.system.shutdown()9.4 格式化存储
此操作不可逆,调用前请提示用户确认。
formatSdCard已废弃,请使用formatStorage。
viewModelScope.launch {
device.system.formatStorage(FileLocation.CAMERA)
.onSuccess { /* 格式化完成 */ }
.onFailure { /* 格式化失败 */ }
}