帮你快速理解、总结文档立即下载

视频通话

最近更新时间:2026-09-11 16:05:32
本文档已由 AI 辅助审校
我的收藏
本文将介绍腾讯云物联网(IoT)应用端 SDK 的视频通话能力(TXIoTCallSession):如何在 App 或小程序中呼叫已绑定的设备,建立双向音视频通话,并在通话中控制摄像头、麦克风与音频播放设备。视频通话基于腾讯云实时音视频(TRTC)链路实现,相比传统 IoT 场景常用的 P2P 直连方案,在接通成功率、接通速度、回声消除与弱网表现等方面具备明显优势,详情参见 方案优势

功能概述

视频通话指应用端主动呼叫已绑定的设备,设备侧振铃并接听后,双方建立一路双向的实时音视频通话。与 监控拉流 只单向拉取设备画面不同,视频通话中应用端同时上行本地麦克风与摄像头,形成真正的双向交互。
TXIoTCallSession 提供的能力包括:发起与挂断呼叫、支持纯音频与音视频两种通话类型、渲染远端画面、开关本地麦克风与摄像头、前后摄像头切换、扬声器与听筒切换、通话双方网络质量监控。
视频通话适用于需要「人与人、人与宠物实时对话」的消费类设备场景:


方案优势

TXIoTCallSession 底层复用腾讯云实时音视频(TRTC)的媒体网络与音频处理能力:应用端与设备分别就近接入 TRTC 媒体网络,在同一房间内互相推流与订阅,不依赖手机与设备之间的端到端直连。对通话这类强交互场景,这一架构选择带来的差异尤为明显。

与传统 IoT P2P 方案对比



前提条件

在使用视频通话能力前,请确保已完成以下准备工作:
已开通腾讯云物联网相关服务,并在控制台完成实例、应用和设备的准备工作。可参见 开通服务
已在客户端工程中集成 IoT 应用端 SDK 并完成登录。可参见 Android 集成与登录iOS 集成与登录
目标设备已绑定到当前登录账户名下,或已被分享给当前账户。可参见 设备绑定
目标设备为支持音视频通话的设备,且当前处于在线状态。
已在工程中申请并获取麦克风权限;进行音视频通话时还需获取摄像头权限。

使用建议

调用顺序:获取通话会话对象 → addListener 注册回调 → callDevice 发起呼叫 → startRemoteView 绑定远端渲染视图 → 收到 onCallBegin 后开启本地麦克风与摄像头 → 结束时 hangup
提前申请权限:在调用 callDevice 之前完成麦克风与摄像头权限申请。若在通话中才发现权限缺失,SDK 会回调错误并结束本次通话。
提前绑定远端视图startRemoteView 可以在 onCallBegin 之前调用。SDK 会暂存该视图,待设备进入房间后自动绑定,无需等待回调再设置。
区分音频与视频的接收方式:远端音频在通话建立后自动播放,无需额外调用;远端视频必须调用 startRemoteView 才会渲染画面。
尽早注册监听:在 callDevice 之前完成 addListener(iOS 为 addDelegate),避免错过 onCallBeginonCallRejected 等关键回调。
统一处理呼叫失败:设备拒接、无应答、忙线、离线各有独立回调,不都走 onError,请分别处理并给出差异化提示。详见「呼叫结果与超时机制」。
及时释放:退出通话界面时调用 hangup 并移除监听,避免资源泄漏与重复回调。
警告:
通话会话对象为单例,同一时刻只允许存在一路通话。若当前已有通话在进行中(已调用 callDevice 且尚未结束),再次调用 callDevice 会通过 onError 回调 ERR_FAILED(iOS 为 TXIoTErrorCodeFailed)并携带「通话已在进行中」的错误信息,原有通话不受影响。切换通话对象前必须先 hangup
视频通话的优先级高于监控拉流。若目标设备当前正被监控拉流,对该设备发起视频通话会抢占设备的推流能力,正在进行的监控会话会被中断,详见 监控拉流

接入步骤

步骤 1:获取通话会话对象

登录成功后,通过 TXIoTEngine 实例调用 getCallSession() 获取通话会话对象。若返回 null(iOS 为 nil),通常是 SDK 尚未登录或登录已过期。
Android
iOS
TXIoTCallSession session = TXIoTEngine.getInstance(context).getCallSession();
if (session == null) {
// SDK 未登录或登录已过期,请先完成登录
return;
}
TXIoTCallSession *session = [[TXIoTEngine getInstance] getCallSession];
if (session == nil) {
// SDK 未登录或登录已过期,请先完成登录
return;
}

步骤 2:注册监听回调

呼叫结果、通话开始与结束、远端流状态、网络质量均通过监听接口返回。请在 callDevice 之前完成注册。
Android
iOS
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];

步骤 3:呼叫设备

调用 callDevice 发起呼叫,callType 指定通话类型:AUDIO 为纯音频通话,VIDEO 为音视频通话。
方法
说明
callDevice
呼叫目标设备。deviceId 为设备标识(productId + deviceName),callType 取值见 TXIoTCallMediaType。呼叫结果通过 onCallBeginonCallRejectedonCallNoResponseonCallLineBusyonCallUserOffline 等回调返回。
hangup
挂断当前通话并释放资源,本端挂断后会收到 onCallEnd,结束原因为 LOCAL_HANGUP
Android
iOS
TXIoTDeviceId deviceId = new TXIoTDeviceId();
deviceId.productId = "已绑定到当前账户的 productId";
deviceId.deviceName = "已绑定到当前账户的 deviceName";

// 发起音视频通话;纯语音通话传 TXIoTCallMediaType.AUDIO
session.callDevice(deviceId, TXIoTCallMediaType.VIDEO);
TXIoTDeviceId *deviceId = [[TXIoTDeviceId alloc] init];
deviceId.productId = @"已绑定到当前账户的 productId";
deviceId.deviceName = @"已绑定到当前账户的 deviceName";

// 发起音视频通话;纯语音通话传 TXIoTCallMediaTypeAudio
[session callDevice:deviceId callType:TXIoTCallMediaTypeVideo];

步骤 4:渲染远端画面

通过 startRemoteView 为远端用户绑定渲染视图。远端用户由 TXIoTCallUser 标识,其中的 deviceId 即被呼叫的设备。
注意:
远端音频在通话建立后自动播放,无需调用任何接口;远端视频必须调用 startRemoteView 才会渲染。若只听到声音看不到画面,请首先检查是否调用了 startRemoteView
Android
iOS
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];
说明:
纯音频通话场景下小程序端同样需要通过 setIoTPlayer 绑定 <iot-player> 组件,远端音频通过该组件播放。

步骤 5:开启本地麦克风与摄像头

通话开始后,调用 openMicrophone 上行本地音频;音视频通话还需调用 openCamera 上行本地画面并显示本地预览。
Android
iOS
// 开启麦克风上行
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];

步骤 6:挂断通话

结束通话时调用 hangup,随后会收到 onCallEnd 回调;退出界面时同时移除监听。
Android
iOS
session.hangup();
session.removeListener(callListener);
[session hangup];
[session removeDelegate:self];

常用能力

切换摄像头与音频播放设备

通话过程中可切换前后摄像头,并在扬声器与听筒之间切换音频播放通路。
说明:
音频播放设备枚举在各端命名略有差异:Android 为 SPEAKER_PHONE / EAR_PIECE,iOS 为 TXIoTAudioPlaybackDeviceSpeakerphone / TXIoTAudioPlaybackDeviceEarpiece,小程序为 SPEAKERPHONE / EARPIECE
Android
iOS
// 切换到后置摄像头
session.switchCamera(TXIoTCamera.BACK);

// 切换到听筒播放(贴耳通话)
session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.EAR_PIECE);
// 切换回扬声器(免手持)
session.selectAudioPlaybackDevice(TXIoTAudioPlaybackDevice.SPEAKER_PHONE);
// 切换到后置摄像头
[session switchCamera:TXIoTCameraBack];

// 切换到听筒播放(贴耳通话)
[session selectAudioPlaybackDevice:TXIoTAudioPlaybackDeviceEarpiece];
// 切换回扬声器(免手持)
[session selectAudioPlaybackDevice:TXIoTAudioPlaybackDeviceSpeakerphone];

网络质量监控

onCallNetworkQualityChanged 同时回传本端质量与远端质量列表,质量等级取值见 TXIoTNetworkQualityEXCELLENTGOODPOORBADVERY_BADDOWN)。该回调仅在质量等级发生变化时触发,不是周期性回调,可直接用于驱动界面提示,无需业务层再做去重。
Android
iOS
@Override
public 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 标识对应设备,可提示“对方网络不佳”
}
}

呼叫结果与超时机制

呼叫失败的各种情形有独立的回调,不都通过 onError 返回,建议按下表分别处理并给出差异化提示。
情形
SDK 行为
建议的界面处理
设备端用户拒接
回调 onCallRejected,随后回调 onCallEnd
提示「对方已拒接」并退出通话界面。
设备端无人接听
呼叫发出后约 30 秒无应答,SDK 自动挂断,回调 onCallNoResponseonCallEnd
提示「对方无应答」,无需业务层自行计时。
设备忙线
回调 onCallLineBusy,随后回调 onCallEnd不会触发 onError
提示「对方正忙」,引导稍后重试。
设备离线
回调 onCallUserOffline,随后回调 onCallEnd不会触发 onError。通话中设备掉线同样回调该方法。
提示「设备不在线」,引导检查设备网络。
麦克风或摄像头未授权
回调 onErrorERR_MIC_NOT_AUTHORIZED / ERR_CAMERA_NOT_AUTHORIZED),并以 CALL_PERMISSION_DENIED 为原因结束通话。
引导用户到系统设置开启权限。
通话中网络短时中断
已接通状态下 SDK 保留约 15 秒的恢复窗口;窗口内恢复则通话继续,超时未恢复则以 NETWORK_ERROR 为原因结束通话。
中断期间显示「网络不稳定,正在恢复」,不要立即销毁界面。
已有通话进行中再次呼叫
回调 onErrorERR_FAILED),原有通话不受影响。
hangup 再发起新呼叫。

状态与回调通知

通话会话通过监听接口(Android:TXIoTCallListener,iOS:TXIoTCallDelegate)回调状态与错误:
回调
触发时机
关键参数
onCallBegin
设备已接听且已进入房间,通话正式开始。
mediaTypeAUDIO / VIDEO
onCallEnd
通话结束(含正常挂断与异常结束)。
mediaType, reasonLOCAL_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 之前注册,否则可能错过 onCallRejectedonCallNoResponse 等回调。
2. 检查是否收到了 onCallUserOffline(设备离线)或 onCallLineBusy(设备忙线)。这两种情形不会触发 onError,只看 onError 会漏判。
3. 确认 productIddeviceName 正确,且该设备已绑定或分享给当前登录账户。若无权限,onCallEnd 的结束原因为 CALL_PERMISSION_DENIED
4. 确认设备端已正确实现呼叫接听逻辑,参见 视频通话设备端接入
5. 若约30秒后收到 onCallNoResponse,说明呼叫已到达但设备端无人接听。

通话中只听到声音,看不到对方画面?

远端音频在通话建立后自动播放,远端视频需要业务层显式调用 startRemoteView 绑定渲染视图才会出画面。请确认:已调用 startRemoteView 并传入了正确的 TXIoTCallUser;渲染视图已加载且尺寸不为0;小程序端 setIoTPlayer 传入的 component-id<iot-player> 一致。此外可结合 onCallUserVideoAvailable 判断远端是否确实在推送视频。

通话过程中切换 Wi-Fi 与移动网络会断线吗?

不会立即断线。已接通状态下,SDK 对短时网络中断保留约15秒的恢复窗口,窗口内网络恢复则通话继续,不会回调 onCallEnd。建议在此期间显示「网络不稳定,正在恢复」的提示,而不是立即销毁通话界面。若超过恢复窗口仍未恢复,SDK 会以 NETWORK_ERROR 为原因结束通话。

可以同时和多台设备通话吗?

不可以。通话会话对象为单例,同一时刻只允许一路通话。若当前通话尚未结束就再次调用 callDevice,SDK 会回调 onErrorERR_FAILED)并保持原有通话不变。切换通话对象前必须先调用 hangup

视频通话会影响设备正在进行的监控拉流吗?

会。视频通话的优先级高于监控拉流。对正在被监控的设备发起视频通话时,通话会抢占设备的推流能力,该设备上正在进行的监控会话会被中断。建议在业务上避免同一设备同时触发两种能力,详见 监控拉流

纯音频通话需要申请摄像头权限吗?

不需要。callTypeAUDIO 时只上行音频,仅需麦克风权限;此时不要调用 openCamera。若传 VIDEO 并调用 openCamera,则必须同时具备麦克风与摄像头权限,缺失任一权限都会导致 onError 并以 CALL_PERMISSION_DENIED 结束通话。

接口参考

视频通话相关接口的完整定义参见:
设备端的呼叫接听实现参见 视频通话设备端接入。若只需单向查看设备画面而不需要双向对话,请使用 监控拉流