一、功能概述
视频通话指应用端主动呼叫已绑定的设备,设备侧振铃并接听后,双方建立一路双向的实时音视频通话。与 监控拉流 只单向拉取设备画面不同,视频通话中应用端同时上行本地麦克风与摄像头,形成真正的双向交互。
TXIoTCallSession 提供的能力包括:发起与挂断呼叫、支持纯音频与音视频两种通话类型、渲染远端画面、开关本地麦克风与摄像头、前后摄像头切换、扬声器与听筒切换、通话双方网络质量监控。
视频通话适用于需要「人与人、人与宠物实时对话」的消费类设备场景:
二、方案优势
TXIoTCallSession 底层复用腾讯云实时音视频(TRTC)的媒体网络与音频处理能力:应用端与设备分别就近接入 TRTC 媒体网络,在同一房间内互相推流与订阅,不依赖手机与设备之间的端到端直连。对通话这类强交互场景,这一架构选择带来的差异尤为明显。
四、前提条件
在使用视频通话能力前,请确保已完成以下准备工作:
·已开通腾讯云物联网相关服务,并在控制台完成实例、应用和设备的准备工作。可参见 开通服务。
·已在客户端工程中集成 IoT 应用端 SDK 并完成登录。可参见 Android 集成与登录、iOS 集成与登录、小程序集成与登录、鸿蒙 SDK 集成与登录。
·目标设备已绑定到当前登录账户名下,或已被分享给当前账户。可参见 设备绑定。
·目标设备为支持音视频通话的设备,且当前处于在线状态。
·已在工程中申请并获取麦克风权限;进行音视频通话时还需获取摄像头权限。
五、使用建议
·调用顺序:获取通话会话对象 → addListener 注册回调 → callDevice 发起呼叫 → startRemoteView 绑定远端渲染视图 → 收到 onCallBegin 后开启本地麦克风与摄像头 → 结束时 hangup。
·提前申请权限:在调用 callDevice 之前完成麦克风与摄像头权限申请。若在通话中才发现权限缺失,SDK 会回调错误并结束本次通话。
·提前绑定远端视图:startRemoteView 可以在 onCallBegin 之前调用。SDK 会暂存该视图,待设备进入房间后自动绑定,无需等待回调再设置。
·区分音频与视频的接收方式:远端音频在通话建立后自动播放,无需额外调用;远端视频必须调用 startRemoteView 才会渲染画面。
·尽早注册监听:在 callDevice 之前完成 addListener(iOS 为 addDelegate),避免错过 onCallBegin、onCallRejected 等关键回调。
·统一处理呼叫失败:设备拒接、无应答、忙线、离线各有独立回调,不都走 onError,请分别处理并给出差异化提示。详见「呼叫结果与超时机制」。
·及时释放:退出通话界面时调用 hangup 并移除监听,避免资源泄漏与重复回调。
注意:警告:
七、步骤 1:获取通话会话对象
登录成功后,通过 TXIoTEngine 实例调用 getCallSession() 获取通话会话对象。若返回空值(Android、小程序与鸿蒙为 null,iOS 为 nil),通常是 SDK 尚未登录或登录已过期。
AndroidiOS小程序鸿蒙TXIoTCallSession session = TXIoTEngine.getInstance(context).getCallSession();if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return;}TXIoTCallSession *session = [[TXIoTEngine getInstance] getCallSession];if (session == nil) { // SDK 未登录或登录已过期,请先完成登录 return;}const { TXIoTEngine } = requirePlugin('tx-iot-sdk');// getInstance 为异步方法,首次调用会等待内部模块加载完成。const iotEngine = await TXIoTEngine.getInstance();const session = iotEngine.getCallSession();if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return;}import { getIoTEngineInstance, TXIoTEngine } from '@tencentcloud/liteavsdk_iot'const iotEngine: TXIoTEngine = getIoTEngineInstance()const session = iotEngine.getCallSession()if (session == null) { // SDK 未登录或登录已过期,请先完成登录 return}
八、步骤 2:注册监听回调
呼叫结果、通话开始与结束、远端流状态、网络质量均通过监听接口返回。请在 callDevice 之前完成注册。
AndroidiOS小程序鸿蒙TXIoTCallListener callListener = new TXIoTCallListener() { @Override public void onCallBegin(TXIoTCallMediaType mediaType) { // 通话正式开始,切换到通话中界面 } @Override public void onCallEnd(TXIoTCallMediaType mediaType, TXIoTCallEndReason reason) { // 通话结束,根据 reason 区分本端挂断、远端挂断、权限不足、网络异常 } @Override public void onCallRejected(TXIoTCallUser callUser) { // 设备端用户拒接 } @Override public void onCallNoResponse(TXIoTCallUser callUser) { // 呼叫超时,设备端无人接听 } @Override public void onCallLineBusy(TXIoTCallUser callUser) { // 设备正忙(已在另一路通话中) } @Override public void onCallUserOffline(TXIoTCallUser callUser) { // 设备离线 } @Override public void onCallUserVideoAvailable(TXIoTCallUser callUser, boolean available) { // 远端视频流可用状态变化,可据此显示占位图 } @Override public void onCallUserAudioAvailable(TXIoTCallUser callUser, boolean available) { // 远端音频流可用状态变化 } @Override public void onCallNetworkQualityChanged( TXIoTQuality localQuality, List<TXIoTQuality> remoteQualityList) { // 网络质量等级变化,可提示“对方网络不佳” } @Override public void onError(TXIoTErrorCode code, String msg) { // 统一处理错误码与用户提示 }};session.addListener(callListener);@interface CallViewController () <TXIoTCallDelegate>@end@implementation CallViewController- (void)onCallBegin:(TXIoTCallMediaType)mediaType { // 通话正式开始,切换到通话中界面}- (void)onCallEnd:(TXIoTCallMediaType)mediaType reason:(TXIoTCallEndReason)reason { // 通话结束,根据 reason 区分本端挂断、远端挂断、权限不足、网络异常}- (void)onCallRejected:(TXIoTCallUser *)callUser { // 设备端用户拒接}- (void)onCallNoResponse:(TXIoTCallUser *)callUser { // 呼叫超时,设备端无人接听}- (void)onCallLineBusy:(TXIoTCallUser *)callUser { // 设备正忙}- (void)onCallUserOffline:(TXIoTCallUser *)callUser { // 设备离线}- (void)onCallUserVideoAvailable:(TXIoTCallUser *)callUser available:(BOOL)available { // 远端视频流可用状态变化}- (void)onCallNetworkQualityChanged:(TXIoTQuality *)localQuality remoteQualityList:(NSArray<TXIoTQuality *> *)remoteQualityList { // 网络质量等级变化}- (void)onError:(TXIoTErrorCode)errorCode errorMessage:(NSString *)errorMessage { // 统一处理错误码与用户提示}@end// 注册代理[session addDelegate:self];// 退出时注销// [session removeDelegate:self];const callListener = { onCallBegin(mediaType) { // 通话正式开始,切换到通话中界面 }, onCallEnd(mediaType, reason) { // 通话结束,根据 reason 区分本端挂断、远端挂断、权限不足、网络异常 }, onCallRejected(callUser) { // 设备端用户拒接 }, onCallNoResponse(callUser) { // 呼叫超时,设备端无人接听 }, onCallLineBusy(callUser) { // 设备正忙(已在另一路通话中) }, onCallUserOffline(callUser) { // 设备离线 }, onCallUserVideoAvailable(callUser, available) { // 远端视频流可用状态变化,可据此显示占位图 }, onCallUserAudioAvailable(callUser, available) { // 远端音频流可用状态变化 }, onError(code, msg) { // 统一处理错误码与用户提示 },};session.addListener(callListener);import { TXIoTCallListener } from '@tencentcloud/liteavsdk_iot'const callListener: TXIoTCallListener = { onCallBegin: (mediaType) => { // 通话正式开始,切换到通话中界面 }, onCallEnd: (mediaType, reason) => { // 通话结束,根据 reason 区分本端挂断、远端挂断、权限不足、网络异常 }, onCallRejected: (callUser) => { // 设备端用户拒接 }, onCallNoResponse: (callUser) => { // 呼叫超时,设备端无人接听 }, onCallLineBusy: (callUser) => { // 设备正忙(已在另一路通话中) }, onCallUserOffline: (callUser) => { // 设备离线 }, onCallUserVideoAvailable: (callUser, available) => { // 远端视频流可用状态变化,可据此显示占位图 }, onCallUserAudioAvailable: (callUser, available) => { // 远端音频流可用状态变化 }, onCallNetworkQualityChanged: (localQuality, remoteQualityList) => { // 网络质量等级变化,可提示“对方网络不佳” }, onError: (code, msg) => { // 统一处理错误码与用户提示 }}session.addListener(callListener)
九、步骤 3:呼叫设备
调用 callDevice 发起呼叫,callType 指定通话类型:AUDIO 为纯音频通话,VIDEO 为音视频通话。
| 方法 | 说明 |
| callDevice | 呼叫目标设备。deviceId 为设备标识(productId + deviceName),callType 取值见 TXIoTCallMediaType。呼叫结果通过 onCallBegin、onCallRejected、onCallNoResponse、onCallLineBusy、onCallUserOffline 等回调返回。 |
| hangup | 挂断当前通话并释放资源,本端挂断后会收到 onCallEnd,结束原因为 LOCAL_HANGUP。 |
AndroidiOS小程序鸿蒙TXIoTDeviceId deviceId = new TXIoTDeviceId();deviceId.productId = "已绑定到当前账户的 productId";deviceId.deviceName = "已绑定到当前账户的 deviceName";// 发起音视频通话;纯语音通话传 TXIoTCallMediaType.AUDIOsession.callDevice(deviceId, TXIoTCallMediaType.VIDEO);TXIoTDeviceId *deviceId = [[TXIoTDeviceId alloc] init];deviceId.productId = @"已绑定到当前账户的 productId";deviceId.deviceName = @"已绑定到当前账户的 deviceName";// 发起音视频通话;纯语音通话传 TXIoTCallMediaTypeAudio[session callDevice:deviceId callType:TXIoTCallMediaTypeVideo];const { TXIoTCallMediaType, TXIoTDeviceId } = requirePlugin('tx-iot-sdk');const deviceId = new TXIoTDeviceId('已绑定到当前账户的 productId', '已绑定到当前账户的 deviceName');// 发起音视频通话;纯语音通话传 TXIoTCallMediaType.AUDIOsession.callDevice(deviceId, TXIoTCallMediaType.VIDEO);import { TXIoTCallMediaType, TXIoTDeviceId } from '@tencentcloud/liteavsdk_iot'const deviceId = new TXIoTDeviceId()deviceId.productId = '已绑定到当前账户的 productId'deviceId.deviceName = '已绑定到当前账户的 deviceName'// 发起音视频通话;纯语音通话传 TXIoTCallMediaType.AUDIOsession.callDevice(deviceId, TXIoTCallMediaType.VIDEO)
十、步骤 4:渲染远端画面
通过 startRemoteView 为远端用户绑定渲染视图。远端用户由 TXIoTCallUser 标识,其中的 deviceId 即被呼叫的设备。
注意:远端音频在通话建立后自动播放,无需调用任何接口;远端视频必须调用 startRemoteView 才会渲染。若只听到声音看不到画面,请首先检查是否调用了 startRemoteView。
AndroidiOS小程序鸿蒙TXIoTCallUser callUser = new TXIoTCallUser();callUser.deviceId = deviceId;// remoteView 为布局中的 TXCloudVideoView,可在 onCallBegin 之前调用TXCloudVideoView remoteView = findViewById(R.id.remote_view);session.startRemoteView(callUser, remoteView);// 需要关闭远端画面时// session.stopRemoteView(callUser);TXIoTCallUser *callUser = [[TXIoTCallUser alloc] init];callUser.deviceId = deviceId;// remoteView 为布局中的 UIView,可在 onCallBegin 之前调用[session startRemoteView:callUser view:self.remoteVideoView];// 需要关闭远端画面时// [session stopRemoteView:callUser];const { TXIoTCallUser } = requirePlugin('tx-iot-sdk');const callUser = new TXIoTCallUser(deviceId);// WXML 中需放置 <iot-player component-id="remote-player"> 组件// 先绑定组件,再观看远端画面;均可在 onCallBegin 之前调用session.setIoTPlayer(callUser, 'remote-player');session.startRemoteView(callUser);// 需要关闭远端画面时// session.stopRemoteView(callUser);import { TXIoTCallUser } from '@tencentcloud/liteavsdk_iot'const callUser = new TXIoTCallUser()callUser.deviceId = deviceId// remote-view 为页面中 XComponent 组件的 id(需设置 type: XComponentType.SURFACE、libraryname: 'liteavsdk'),// 可在 onCallBegin 之前调用session.startRemoteView(callUser, 'remote-view')// 需要关闭远端画面时// session.stopRemoteView(callUser)
注意:说明:
注意:纯音频通话场景下小程序端同样需要通过 setIoTPlayer 绑定 <iot-player> 组件,远端音频通过该组件播放。
十一、步骤 5:开启本地麦克风与摄像头
通话开始后,调用 openMicrophone 上行本地音频;音视频通话还需调用 openCamera 上行本地画面并显示本地预览。
AndroidiOS小程序鸿蒙// 开启麦克风上行session.openMicrophone();// 开启摄像头上行并在 localView 中预览TXCloudVideoView localView = findViewById(R.id.local_view);session.openCamera(TXIoTCamera.FRONT, localView);// 通话中关闭麦克风(静音自己)session.closeMicrophone();// 通话中关闭摄像头(转为纯语音)session.closeCamera();// 开启麦克风上行[session openMicrophone];// 开启摄像头上行并在 localView 中预览[session openCamera:TXIoTCameraFront view:self.localVideoView];// 通话中关闭麦克风(静音自己)[session closeMicrophone];// 通话中关闭摄像头(转为纯语音)[session closeCamera];const { TXIoTCamera } = requirePlugin('tx-iot-sdk');// 开启麦克风上行session.openMicrophone();// 开启摄像头上行session.openCamera(TXIoTCamera.FRONT);// 通话中关闭麦克风(静音自己)session.closeMicrophone();// 通话中关闭摄像头(转为纯语音)session.closeCamera();import { TXIoTCamera } from '@tencentcloud/liteavsdk_iot'// 开启麦克风上行session.openMicrophone()// 开启摄像头上行并在 local-view 中预览(local-view 为 XComponent 的 id)session.openCamera(TXIoTCamera.FRONT, 'local-view')// 通话中关闭麦克风(静音自己)session.closeMicrophone()// 通话中关闭摄像头(转为纯语音)session.closeCamera()
十二、步骤 6:挂断通话
结束通话时调用 hangup,随后会收到 onCallEnd 回调;退出界面时同时移除监听。
AndroidiOS小程序鸿蒙session.hangup();session.removeListener(callListener);[session hangup];[session removeDelegate:self];session.hangup();session.removeListener(callListener);session.hangup()session.removeListener(callListener)
十四、切换摄像头与音频播放设备
通话过程中可切换前后摄像头,并在扬声器与听筒之间切换音频播放通路。
注意:说明:
注意:音频播放设备枚举在各端命名略有差异:
AndroidiOS小程序鸿蒙// 切换到后置摄像头session.switchCamera(TXIoTCamera.BACK);// 切换到听筒播放(贴耳通话)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.EAR_PIECE);// 切换回扬声器(免手持)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.SPEAKER_PHONE);// 切换到后置摄像头[session switchCamera:TXIoTCameraBack];// 切换到听筒播放(贴耳通话)[session selectAudioPlaybackDevice:TXIoTAudioPlaybackDeviceEarpiece];// 切换回扬声器(免手持)[session selectAudioPlaybackDevice:TXIoTAudioPlaybackDeviceSpeakerphone];// 切换到后置摄像头session.switchCamera(TXIoTCamera.BACK);// 切换到听筒播放(贴耳通话)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.EARPIECE);// 切换回扬声器(免手持)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.SPEAKERPHONE);import { TXIoTCamera, TXIoTAudioPlaybackDevice } from '@tencentcloud/liteavsdk_iot'// 切换到后置摄像头session.switchCamera(TXIoTCamera.BACK)// 切换到听筒播放(贴耳通话)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.EAR_PIECE)// 切换回扬声器(免手持)session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.SPEAKER_PHONE)
十五、网络质量监控
onCallNetworkQualityChanged 同时回传本端质量与远端质量列表,质量等级取值见 TXIoTNetworkQuality(EXCELLENT、GOOD、POOR、BAD、VERY_BAD、DOWN)。该回调仅在质量等级发生变化时触发,不是周期性回调,可直接用于驱动界面提示,无需业务层再做去重。
AndroidiOS鸿蒙@Overridepublic void onCallNetworkQualityChanged( TXIoTQuality localQuality, List<TXIoTQuality> remoteQualityList) { if (localQuality.quality == TXIoTNetworkQuality.BAD || localQuality.quality == TXIoTNetworkQuality.VERY_BAD) { // 提示用户“当前网络不佳” } for (TXIoTQuality remote : remoteQualityList) { // remote.callUser 标识对应设备,可提示“对方网络不佳” }}- (void)onCallNetworkQualityChanged:(TXIoTQuality *)localQuality remoteQualityList:(NSArray<TXIoTQuality *> *)remoteQualityList { if (localQuality.quality == TXIoTNetworkQualityBad || localQuality.quality == TXIoTNetworkQualityVeryBad) { // 提示用户“当前网络不佳” } for (TXIoTQuality *remote in remoteQualityList) { // remote.callUser 标识对应设备,可提示“对方网络不佳” }}import { TXIoTNetworkQuality, TXIoTQuality } from '@tencentcloud/liteavsdk_iot'const callListener = { onCallNetworkQualityChanged: (localQuality: TXIoTQuality, remoteQualityList: TXIoTQuality[]) => { if (localQuality.quality === TXIoTNetworkQuality.BAD || localQuality.quality === TXIoTNetworkQuality.VERY_BAD) { // 提示用户“当前网络不佳” } for (const remote of remoteQualityList) { // remote.callUser 标识对应设备,可提示“对方网络不佳” } }}
十六、呼叫结果与超时机制
呼叫失败的各种情形有独立的回调,不都通过 onError 返回,建议按下表分别处理并给出差异化提示。
| 情形 | SDK 行为 | 建议的界面处理 |
| 设备端用户拒接 | 回调 onCallRejected,随后回调 onCallEnd。 | 提示「对方已拒接」并退出通话界面。 |
| 设备端无人接听 | 呼叫发出后约 30 秒无应答,SDK 自动挂断,回调 onCallNoResponse 与 onCallEnd。 | 提示「对方无应答」,无需业务层自行计时。 |
| 设备忙线 | 回调 onCallLineBusy,随后回调 onCallEnd。不会触发 onError。 | 提示「对方正忙」,引导稍后重试。 |
| 设备离线 | 回调 onCallUserOffline,随后回调 onCallEnd。不会触发 onError。通话中设备掉线同样回调该方法。 | 提示「设备不在线」,引导检查设备网络。 |
| 麦克风或摄像头未授权 | 回调 onError(ERR_MIC_NOT_AUTHORIZED / ERR_CAMERA_NOT_AUTHORIZED),并以 CALL_PERMISSION_DENIED 为原因结束通话。 | 引导用户到系统设置开启权限。 |
| 通话中网络短时中断 | 已接通状态下 SDK 保留约 15 秒的恢复窗口;窗口内恢复则通话继续,超时未恢复则以 NETWORK_ERROR 为原因结束通话。 | 中断期间显示「网络不稳定,正在恢复」,不要立即销毁界面。 |
| 已有通话进行中再次呼叫 | 回调 onError(ERR_FAILED),原有通话不受影响。 | 先 hangup 再发起新呼叫。 |
十七、状态与回调通知
通话会话通过监听接口(Android 与鸿蒙:TXIoTCallListener,iOS:TXIoTCallDelegate,小程序:addListener 回调对象)回调状态与错误:
| 回调 | 触发时机 | 关键参数 |
| onCallBegin | 设备已接听且已进入房间,通话正式开始。 | mediaType(AUDIO / VIDEO) |
| onCallEnd | 通话结束(含正常挂断与异常结束)。 | mediaType, reason(LOCAL_HANGUP / REMOTE_HANGUP / CALL_PERMISSION_DENIED / NETWORK_ERROR / UNKNOWN) |
| onCallRejected | 设备端用户拒接本次呼叫。 | callUser |
| onCallNoResponse | 呼叫超时,设备端无人接听。 | callUser |
| onCallLineBusy | 设备正忙,已在另一路通话中。 | callUser |
| onCallUserOffline | 设备离线,或通话过程中设备掉线。 | callUser |
| onCallUserAudioAvailable | 远端音频流可用状态变化。 | callUser, available |
| onCallUserVideoAvailable | 远端视频流可用状态变化,可据此显示占位图。 | callUser, available |
| onCallNetworkQualityChanged | 本端或远端网络质量等级发生变化(小程序端暂不支持)。 | localQuality, remoteQualityList |
| onError | 发生错误,例如参数非法、设备权限缺失、重复呼叫。 | errCode, errMsg |
十九、呼叫后一直没有接通,如何排查?
按以下顺序排查:
1. 确认监听在 callDevice 之前注册,否则可能错过 onCallRejected、onCallNoResponse 等回调。
2. 检查是否收到了 onCallUserOffline(设备离线)或 onCallLineBusy(设备忙线)。这两种情形不会触发 onError,只看 onError 会漏判。
3. 确认 productId 与 deviceName 正确,且该设备已绑定或分享给当前登录账户。若无权限,onCallEnd 的结束原因为 CALL_PERMISSION_DENIED。
4. 确认设备端已正确实现呼叫接听逻辑,参见 视频通话设备端接入。
5. 若约30秒后收到 onCallNoResponse,说明呼叫已到达但设备端无人接听。
20、通话中只听到声音,看不到对方画面?
远端音频在通话建立后自动播放,远端视频需要业务层显式调用 startRemoteView 绑定渲染视图才会出画面。请确认:已调用 startRemoteView 并传入了正确的 TXIoTCallUser;渲染视图已加载且尺寸不为0;小程序端 setIoTPlayer 传入的 component-id 与 <iot-player> 一致。此外可结合 onCallUserVideoAvailable 判断远端是否确实在推送视频。
21、通话过程中切换 Wi-Fi 与移动网络会断线吗?
不会立即断线。已接通状态下,SDK 对短时网络中断保留约15秒的恢复窗口,窗口内网络恢复则通话继续,不会回调 onCallEnd。建议在此期间显示「网络不稳定,正在恢复」的提示,而不是立即销毁通话界面。若超过恢复窗口仍未恢复,SDK 会以 NETWORK_ERROR 为原因结束通话。
22、可以同时和多台设备通话吗?
不可以。通话会话对象为单例,同一时刻只允许一路通话。若当前通话尚未结束就再次调用 callDevice,SDK 会回调 onError(ERR_FAILED)并保持原有通话不变。切换通话对象前必须先调用 hangup。
23、视频通话会影响设备正在进行的监控拉流吗?
会。视频通话的优先级高于监控拉流。对正在被监控的设备发起视频通话时,通话会抢占设备的推流能力,该设备上正在进行的监控会话会被中断。建议在业务上避免同一设备同时触发两种能力,详见 监控拉流。
24、纯音频通话需要申请摄像头权限吗?
不需要。callType 传 AUDIO 时只上行音频,仅需麦克风权限;此时不要调用 openCamera。若传 VIDEO 并调用 openCamera,则必须同时具备麦克风与摄像头权限,缺失任一权限都会导致 onError 并以 CALL_PERMISSION_DENIED 结束通话。
25、接口参考
视频通话相关接口的完整定义参见:
·TXIoTCallSession(Android)
·TXIoTCallSession(iOS)
·TXIoTCallSession(小程序)
·TXIoTCallSession(鸿蒙)
·TXIoTEngineDef(数据结构与错误码)
设备端的呼叫接听实现参见 视频通话设备端接入。若只需单向查看设备画面而不需要双向对话,请使用 监控拉流。