一、功能概述
监控拉流指应用端主动向已绑定的设备发起一次实时观看请求,设备按请求的通道与清晰度推送音视频流,应用端订阅并渲染,全过程由 TXIoTMonitorSession 统一管理。典型能力包括:实时画面播放、高清/流畅清晰度切换、远端音频控制、双向语音对讲、多通道同时观看、画面截图与本地录制、PTZ 云台控制。
监控拉流广泛应用于家庭安防、可视门铃、看护陪护、门店巡检等消费类音视频设备场景:
二、方案优势
TXIoTMonitorSession 底层复用腾讯云实时音视频(TRTC)的媒体网络与传输能力:设备与应用端分别就近接入 TRTC 媒体网络并在同一房间内完成推流与订阅,不依赖设备与手机之间的端到端直连。这一架构选择带来的差异如下。
四、前提条件
在使用监控拉流能力前,请确保已完成以下准备工作:
·已开通腾讯云物联网相关服务,并在控制台完成实例、应用和设备的准备工作。可参见 开通服务。
·已在客户端工程中集成 IoT 应用端 SDK 并完成登录。可参见 Android 集成与登录、iOS 集成与登录、小程序集成与登录、鸿蒙 SDK 集成与登录。
·目标设备已绑定到当前登录账户名下,或已被分享给当前账户。可参见 设备绑定。
·目标设备为支持音视频推流的设备,且当前处于在线状态。
·若需要使用语音对讲,已在工程中申请并获取麦克风权限。
五、使用建议
·调用顺序:获取监控会话对象 → addListener 注册回调 → startRemoteView 绑定渲染视图 → startSession 开始拉流 → 收到 onSessionEstablished 后画面稳定播放 → 结束时 stopRemoteView + stopSession。
·先绑定视图再开始会话:startRemoteView 可以在 startSession 之前调用,可省去一次通道协商,出图更快。
·尽早注册监听:在 startSession 之前完成 addListener(iOS 为 addDelegate),避免错过 onSessionEstablished 等早期回调。
·及时释放:不再需要监控时调用 stopSession 并移除监听,避免资源泄漏与重复回调。
·统一错误处理:在 onError 中集中处理错误码,结合错误码给出用户可理解的提示。
注意:警告:
注意:监控会话对象为单例,同一时刻只存在一个监控会话。若已有会话正在运行中(已调用 startSession 且尚未 stopSession),再次调用 startSession 会被直接忽略,不会打断或切换已有会话。切换观看设备前必须先调用 stopSession。
七、步骤 1:获取监控会话对象
登录成功后,通过 TXIoTEngine 实例调用 getMonitorSession() 获取监控会话对象。若返回空值(Android、小程序与鸿蒙为 null,iOS 为 nil),通常是 SDK 尚未登录或登录已过期。
AndroidiOS小程序鸿蒙TXIoTMonitorSession session = TXIoTEngine.getInstance(context).getMonitorSession();if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return;}TXIoTMonitorSession *session = [[TXIoTEngine getInstance] getMonitorSession];if (session == nil) { // SDK 未登录或登录已过期,请先完成登录 return;}const { TXIoTEngine } = requirePlugin('tx-iot-sdk');// getInstance 为异步方法,首次调用会等待内部模块加载完成。const iotEngine = await TXIoTEngine.getInstance();const session = iotEngine.getMonitorSession();if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return;}import { getIoTEngineInstance, TXIoTEngine } from '@tencentcloud/liteavsdk_iot'const iotEngine: TXIoTEngine = getIoTEngineInstance()const session = iotEngine.getMonitorSession()if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return}
八、步骤 2:注册监听回调
监控会话的状态变化、首帧渲染、播放状态与错误均通过监听接口返回。请在 startSession 之前完成注册。
AndroidiOS小程序鸿蒙TXIoTMonitorSessionListener monitorListener = new TXIoTMonitorSessionListener() { @Override public void onSessionEstablished() { // 会话建立成功,所有已请求通道进入播放状态 } @Override public void onRenderFirstFrame(int channelId) { // 指定通道首帧已渲染,此时可隐藏加载动画 } @Override public void onPlayStateChanged(int channelId, TXIoTPlayState state) { // PLAYING / LOADING / STOPPED,用于驱动 loading 与占位图 } @Override public void onSessionReconnecting() { // 链路中断,SDK 正在自动重连 } @Override public void onSessionRecovery() { // 重连成功,画面恢复 } @Override public void onError(int channelId, TXIoTErrorCode errCode, String errMsg) { // 统一处理错误码与用户提示 } // 其他回调按需实现…};session.addListener(monitorListener);@interface MonitorViewController () <TXIoTMonitorSessionDelegate>@end@implementation MonitorViewController- (void)onSessionEstablished { // 会话建立成功,所有已请求通道进入播放状态}- (void)onRenderFirstFrame:(NSInteger)channelId { // 指定通道首帧已渲染,此时可隐藏加载动画}- (void)onPlayStateChanged:(NSInteger)channelId state:(TXIoTPlayState)state { // Playing / Loading / Stopped,用于驱动 loading 与占位图}- (void)onSessionReconnecting { // 链路中断,SDK 正在自动重连}- (void)onSessionRecovery { // 重连成功,画面恢复}- (void)onError:(NSInteger)channelId errorCode:(TXIoTErrorCode)errorCode errorMessage:(NSString *)errorMessage { // 统一处理错误码与用户提示}@end// 注册代理[session addDelegate:self];// 退出时注销// [session removeDelegate:self];const monitorListener = { onSessionEstablished() { // 会话建立成功,所有已请求通道进入播放状态 }, onRenderFirstFrame(channelId) { // 指定通道首帧已渲染,此时可隐藏加载动画 }, onPlayStateChanged(channelId, state) { // PLAYING / LOADING / STOPPED,用于驱动 loading 与占位图 }, onSessionReconnecting() { // 链路中断,SDK 正在自动重连 }, onSessionRecovery() { // 重连成功,画面恢复 }, onError(channelId, errCode, errMsg) { // 统一处理错误码与用户提示 }, // 其他回调按需实现…};session.addListener(monitorListener);import { TXIoTMonitorSessionListener } from '@tencentcloud/liteavsdk_iot'const monitorListener: TXIoTMonitorSessionListener = { onSessionEstablished: () => { // 会话建立成功,所有已请求通道进入播放状态 }, onRenderFirstFrame: (channelId) => { // 指定通道首帧已渲染,此时可隐藏加载动画 }, onPlayStateChanged: (channelId, state) => { // PLAYING / LOADING / STOPPED,用于驱动 loading 与占位图 }, onSessionReconnecting: () => { // 链路中断,SDK 正在自动重连 }, onSessionRecovery: () => { // 重连成功,画面恢复 }, onError: (channelId, errCode, errMsg) => { // 统一处理错误码与用户提示 } // 其他回调按需实现…}session.addListener(monitorListener)
九、步骤 3:绑定渲染视图并开始拉流
先通过 startRemoteView 为目标通道绑定渲染视图并指定清晰度,再调用 startSession 与设备建立会话。
| 方法 | 说明 |
| startRemoteView | 为 channelId 通道绑定渲染视图并播放实时画面。channelId 表示设备的视频通道,每个通道对应设备的一个摄像头,需与设备端通道编号对齐,单通道设备使用 0;streamType 为码流类型(HD 高清 / SD 流畅)。小程序端不传视图对象,需先用 setIoTPlayer 绑定 <iot-player> 组件。鸿蒙端 view 传 ArkUI XComponent 组件的 id(需设置 type: XComponentType.SURFACE、libraryname: 'liteavsdk')。 |
| startSession | 与目标设备建立监控会话,deviceId 为目标设备标识(productId + deviceName)。 |
AndroidiOS小程序鸿蒙// remoteView 为布局中的 TXCloudVideoViewTXCloudVideoView remoteView = findViewById(R.id.remote_view);int channelId = 0; // 单通道设备使用 0// 先绑定渲染视图,再开始会话session.startRemoteView(channelId, TXIoTStreamType.HD, remoteView);TXIoTDeviceId deviceId = new TXIoTDeviceId();deviceId.productId = "已绑定到当前账户的 productId";deviceId.deviceName = "已绑定到当前账户的 deviceName";session.startSession(deviceId);// remoteView 为布局中的 UIViewUIView *remoteView = self.remoteVideoView;NSInteger channelId = 0; // 单通道设备使用 0// 先绑定渲染视图,再开始会话[session startRemoteView:channelId streamType:TXIoTStreamTypeHD view:remoteView];TXIoTDeviceId *deviceId = [[TXIoTDeviceId alloc] init];deviceId.productId = @"已绑定到当前账户的 productId";deviceId.deviceName = @"已绑定到当前账户的 deviceName";[session startSession:deviceId];const { TXIoTStreamType, TXIoTDeviceId } = requirePlugin('tx-iot-sdk');// WXML 中需放置 <iot-player component-id="remote-player"> 组件const channelId = 0; // 单通道设备使用 0// 先把通道绑定到 <iot-player> 组件,再订阅视频流session.setIoTPlayer(channelId, 'remote-player');// 先绑定渲染视图,再开始会话session.startRemoteView(channelId, TXIoTStreamType.HD);const deviceId = new TXIoTDeviceId('已绑定到当前账户的 productId', '已绑定到当前账户的 deviceName');session.startSession(deviceId);import { TXIoTStreamType, TXIoTDeviceId } from '@tencentcloud/liteavsdk_iot'// remote-view 为页面中 XComponent 组件的 id// 需设置 type: XComponentType.SURFACE、libraryname: 'liteavsdk'const channelId = 0 // 单通道设备使用 0// 先绑定渲染视图,再开始会话session.startRemoteView(channelId, TXIoTStreamType.HD, 'remote-view')const deviceId = new TXIoTDeviceId()deviceId.productId = '已绑定到当前账户的 productId'deviceId.deviceName = '已绑定到当前账户的 deviceName'session.startSession(deviceId)
注意:说明:
注意:即使设备没有视频画面(例如纯语音对讲场景),小程序端仍需通过 setIoTPlayer 绑定 <iot-player> 组件,音频通过该组件播放。
十、步骤 4:停止拉流并释放资源
退出监控页面时,先停止通道播放,再停止会话并移除监听。
AndroidiOS小程序鸿蒙session.stopRemoteView(0);session.stopSession();session.removeListener(monitorListener);[session stopRemoteView:0];[session stopSession];[session removeDelegate:self];// 一般在页面 onHide / onUnload 中调用session.stopRemoteView(0);session.stopSession();session.removeListener(monitorListener);// 一般在页面 aboutToDisappear / onPageHide 中调用session.stopRemoteView(0)session.stopSession()session.removeListener(monitorListener)
十二、切换视频清晰度
观看过程中可动态切换通道画面的清晰度,无需重新建立会话。应用端切换后,SDK 会将清晰度要求同步给设备端,由设备推送对应清晰度的视频流。
注意:不同清晰度可能对应不同的计费标准,请确保应用端实际使用的清晰度与用户购买的套餐一致,避免因清晰度与计费套餐不匹配引发计费争议。
AndroidiOS小程序鸿蒙// 切换为流畅流session.switchRemoteStream(0, TXIoTStreamType.SD);// 切换为高清流session.switchRemoteStream(0, TXIoTStreamType.HD);// 切换为流畅流[session switchRemoteStream:0 streamType:TXIoTStreamTypeSD];// 切换为高清流[session switchRemoteStream:0 streamType:TXIoTStreamTypeHD];// 切换为流畅流session.switchRemoteStream(0, TXIoTStreamType.SD);// 切换为高清流session.switchRemoteStream(0, TXIoTStreamType.HD);// 切换为流畅流session.switchRemoteStream(0, TXIoTStreamType.SD)// 切换为高清流session.switchRemoteStream(0, TXIoTStreamType.HD)
十三、远端音频控制与语音对讲
muteRemoteAudio 控制单个通道的远端音频,muteAllRemoteAudio 控制所有通道。开启语音对讲时调用 startLocalAudio 采集并上行本地音频,结束时调用 stopLocalAudio;对讲期间可用 muteLocalAudio 临时停止上行。
AndroidiOS小程序鸿蒙// 静音 0 号通道的远端声音session.muteRemoteAudio(0, true);// 静音全部通道session.muteAllRemoteAudio(true);// 开启语音对讲(需先获取麦克风权限)session.startLocalAudio();// 对讲过程中临时停止上行session.muteLocalAudio(true);// 结束对讲session.stopLocalAudio();// 静音 0 号通道的远端声音[session muteRemoteAudio:0 mute:YES];// 静音全部通道[session muteAllRemoteAudio:YES];// 开启语音对讲(需先获取麦克风权限)[session startLocalAudio];// 对讲过程中临时停止上行[session muteLocalAudio:YES];// 结束对讲[session stopLocalAudio];// 静音 0 号通道的远端声音session.muteRemoteAudio(0, true);// 静音全部通道session.muteAllRemoteAudio(true);// 开启语音对讲(需先获取麦克风权限)session.startLocalAudio();// 对讲过程中临时停止上行session.muteLocalAudio(true);// 结束对讲session.stopLocalAudio();// 静音 0 号通道的远端声音session.muteRemoteAudio(0, true)// 静音全部通道session.muteAllRemoteAudio(true)// 开启语音对讲(需先获取麦克风权限)session.startLocalAudio()// 对讲过程中临时停止上行session.muteLocalAudio(true)// 结束对讲session.stopLocalAudio()
十四、多通道同时拉流
枪球一体机、多目摄像机等设备包含多个视频通道。为每个通道分别调用 startRemoteView 绑定独立视图即可同时观看,SDK 会在一次会话内统一订阅这些通道。同时拉流的通道数量上限为 4 个。
注意:说明:
注意:建议在调用 startSession 之前为全部需要观看的通道都完成 startRemoteView;会话建立后新增通道会触发一次通道协商,画面出图相对更慢。所有已请求通道均进入播放状态后才会回调 onSessionEstablished。
AndroidiOS小程序鸿蒙// 同时观看 0、1 两个通道session.startRemoteView(0, TXIoTStreamType.HD, remoteView0);session.startRemoteView(1, TXIoTStreamType.HD, remoteView1);session.startSession(deviceId);// 仅关闭 1 号通道,0 号通道继续播放session.stopRemoteView(1);// 同时观看 0、1 两个通道[session startRemoteView:0 streamType:TXIoTStreamTypeHD view:self.remoteView0];[session startRemoteView:1 streamType:TXIoTStreamTypeHD view:self.remoteView1];[session startSession:deviceId];// 仅关闭 1 号通道,0 号通道继续播放[session stopRemoteView:1];// 同时观看 0、1 两个通道,分别绑定各自的 <iot-player> 组件session.setIoTPlayer(0, 'remote-player-0');session.setIoTPlayer(1, 'remote-player-1');session.startRemoteView(0, TXIoTStreamType.HD);session.startRemoteView(1, TXIoTStreamType.HD);session.startSession(deviceId);// 仅关闭 1 号通道,0 号通道继续播放session.stopRemoteView(1);// 同时观看 0、1 两个通道,分别绑定各自的 XComponentsession.startRemoteView(0, TXIoTStreamType.HD, 'remote-view-0')session.startRemoteView(1, TXIoTStreamType.HD, 'remote-view-1')session.startSession(deviceId)// 仅关闭 1 号通道,0 号通道继续播放session.stopRemoteView(1)
十五、截图与本地录制
takeSnapshot 对指定通道的当前画面截图,结果通过 onSnapshotComplete 返回。startLocalRecording 将指定通道的音视频录制到本地文件,录制过程通过 onLocalRecordBegin、onLocalRecording、onLocalRecordComplete 回调通知。小程序端仅支持截图,不支持本地录制。
注意:TXIoTLocalRecordingParams.filePath 为必填项。若传入空字符串,SDK 会通过 onLocalRecordBegin 回调 ERR_INVALID_PARAMETER(iOS 为 TXIoTErrorCodeInvalidParameter)而不会开始录制。多通道设备的截图与录制均按 channelId 独立进行,如需同时录制多个通道,请对每个通道分别调用。
AndroidiOS小程序鸿蒙// 截图,结果通过 onSnapshotComplete(channelId, bitmap, errCode) 返回session.takeSnapshot(0);// 本地录制TXIoTLocalRecordingParams params = new TXIoTLocalRecordingParams();params.filePath = getExternalFilesDir(null) + "/monitor_record.mp4";session.startLocalRecording(0, params);// 结束录制,完成后回调 onLocalRecordCompletesession.stopLocalRecording(0);// 截图,结果通过 onSnapshotComplete:image:errorCode: 返回[session takeSnapshot:0];// 本地录制TXIoTLocalRecordingParams *params = [[TXIoTLocalRecordingParams alloc] init];params.filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"monitor_record.mp4"];[session startLocalRecording:0 params:params];// 结束录制,完成后回调 onLocalRecordComplete:errorCode:storagePath:[session stopLocalRecording:0];// 截图,结果通过 onSnapshotComplete(channelId, imagePath) 返回session.takeSnapshot(0);// 小程序端不支持本地录制(startLocalRecording / stopLocalRecording)import { TXIoTLocalRecordingParams } from '@tencentcloud/liteavsdk_iot'// 截图,结果通过 onSnapshotComplete(channelId, image) 返回,image 为 image.PixelMapsession.takeSnapshot(0)// 本地录制const params = new TXIoTLocalRecordingParams()params.filePath = getContext().filesDir + '/monitor_record.mp4'session.startLocalRecording(0, params)// 结束录制,完成后回调 onLocalRecordCompletesession.stopLocalRecording(0)
十六、PTZ 云台控制
对支持云台的设备,通过 sendPTZCommand 下发转动与变焦指令,speed 为转动速度。手势拖动等高频场景下 SDK 会自动合并待发送指令并串行下发,业务层无需自行节流。松手时下发 STOP 停止转动。
AndroidiOS小程序鸿蒙// 向左转动session.sendPTZCommand(0, TXIoTPTZCommand.LEFT, 5);// 松手停止session.sendPTZCommand(0, TXIoTPTZCommand.STOP, 0);// 画面放大session.sendPTZCommand(0, TXIoTPTZCommand.ZOOM_IN, 5);// 向左转动[session sendPTZCommand:0 command:TXIoTPTZCommandLeft speed:5];// 松手停止[session sendPTZCommand:0 command:TXIoTPTZCommandStop speed:0];// 画面放大[session sendPTZCommand:0 command:TXIoTPTZCommandZoomIn speed:5];// 向左转动session.sendPTZCommand(0, TXIoTPTZCommand.LEFT, 5);// 松手停止session.sendPTZCommand(0, TXIoTPTZCommand.STOP, 0);// 画面放大session.sendPTZCommand(0, TXIoTPTZCommand.ZOOM_IN, 5);import { TXIoTPTZCommand } from '@tencentcloud/liteavsdk_iot'// 向左转动session.sendPTZCommand(0, TXIoTPTZCommand.LEFT, 5)// 松手停止session.sendPTZCommand(0, TXIoTPTZCommand.STOP, 0)// 画面放大session.sendPTZCommand(0, TXIoTPTZCommand.ZOOM_IN, 5)
十七、状态与回调通知
监控会话通过监听接口(Android 与鸿蒙:TXIoTMonitorSessionListener,iOS:TXIoTMonitorSessionDelegate,小程序:addListener 回调对象)回调状态与错误:
| 回调 | 触发时机 | 关键参数 |
| onSessionEstablished | 监控会话建立成功,所有已请求通道均进入播放状态 | 无。 |
| onSessionReconnecting | 会话中断,SDK 正在自动重连 | 无。 |
| onSessionRecovery | 会话重连成功,画面恢复 | 无。 |
| onRenderFirstFrame | 指定通道首帧已渲染,可用于隐藏加载动画 | channelId |
| onPlayStateChanged | 播放状态变化,用于驱动 loading 与占位图 | channelId, state(PLAYING / LOADING / STOPPED) |
| onSnapshotComplete | 截图完成 | channelId 与截图结果(Android 为 Bitmap,iOS 为 TXImage,小程序为图片路径) |
| onLocalRecordBegin | 本地录制开始,或因参数非法未能开始 | channelId, errCode, storagePath |
| onLocalRecording | 录制进度更新 | channelId, durationMs, storagePath |
| onLocalRecordComplete | 本地录制结束 | channelId, errCode, storagePath |
| onError | 会话或某通道发生错误 | channelId, errCode, errMsg |
注意:会话中断时会先触发 onSessionReconnecting,SDK 自动尝试重连;重连成功触发 onSessionRecovery,无需业务层主动重建会话。若重连仍然失败,最终通过 onError 回调错误。
十九、调用 startSession 后一直没有画面,如何排查?
按以下顺序排查:
1. 确认监听在 startSession 之前注册,否则可能错过 onSessionEstablished 与早期的 onError。
2. 检查是否收到 onError。若为 ERR_DEVICE_OFFLINE,说明设备已离线;若为 ERR_UNAUTHORIZED_OPERATION,说明当前账户没有该设备的权限,请先完成 设备绑定。
3. 确认 startRemoteView 传入的 channelId 与设备端通道编号一致。通道编号不匹配时设备不会推送对应通道的流,onPlayStateChanged 会长期停留在 LOADING。
4. 确认渲染视图已正确加载且尺寸不为0(小程序端确认 setIoTPlayer 传入的 component-id 与 <iot-player> 一致)。
5. 若通道长时间处于 LOADING,SDK 会判定为加载超时并自动重试一次;仍失败则通过 onError 回调,此时应引导用户检查设备网络与在线状态。
20、监控过程中会被视频通话打断吗?
会。监控拉流的优先级低于视频通话。若设备正在被监控,此时对该设备发起视频通话,视频通话会抢占设备的推流能力:应用端会收到 onError 回调 ERR_DEVICE_SWITCH_TO_VOIP(iOS 为 TXIoTErrorCodeDeviceSwitchToVoIP),且当前监控会话会自动停止。业务层应在收到该错误后更新界面状态,不需要再调用 stopSession。视频通话相关能力参见 视频通话。
21、网络抖动导致画面中断,需要业务层自己重连吗?
不需要。SDK 内置自动重连:链路中断后先回调 onSessionReconnecting,重连成功回调 onSessionRecovery。建议在 onSessionReconnecting 中显示重连提示,在 onSessionRecovery 中恢复正常界面。仅当 SDK 自动重连失败时才会通过 onError 回调错误,此时再由业务层决定是否引导用户手动重试。
22、可以同时监控多台设备吗?
不可以。监控会话对象为单例,同一时刻只存在一个监控会话。切换观看设备时,必须先调用 stopSession 停止当前会话,再对新设备调用 startSession;未先停止会话时,新的 startSession 调用会被直接忽略。
23、一台设备有多个摄像头,如何同时查看?
为每个通道分别调用 startRemoteView 并绑定独立的渲染视图,SDK 会在同一会话内订阅这些通道,参见 多通道同时拉流。建议在 startSession 之前完成全部通道的绑定。
24、截图与录制可以对多个通道同时进行吗?
可以。takeSnapshot、startLocalRecording、stopLocalRecording 均以 channelId 为维度独立生效,需要对多个通道同时操作时,对每个通道分别调用即可,各通道的回调通过参数中的 channelId 区分。小程序端仅支持截图,不支持本地录制。
25、接口参考
监控拉流相关接口的完整定义参见:
·TXIoTMonitorSession(Android)
·TXIoTMonitorSession(iOS)
·TXIoTMonitorSession(小程序)
·TXIoTMonitorSession(鸿蒙)
·TXIoTEngineDef(数据结构与错误码)
安防摄像头场景下的云台控制、语音对讲、录制与截图的进阶用法,参见 云台控制、语音对讲、录制与截图。