文档中心
ExpressVideoSDK 实时音视频
文档中心
体验 App
SDK 中心
API 中心
常见问题
代码市场
进入控制台
立即注册
登录
中文站 English
  • 文档中心
  • 实时音视频
  • 快速开始
  • 实现视频通话

实现视频通话

更新时间:2025-03-12 11:46

1 功能简介

本文将介绍如何快速实现一个简单的实时音视频通话。

相关概念解释:

  • ZEGO Express SDK:由 ZEGO 提供的实时音视频 SDK,能够为开发者提供便捷接入、高清流畅、多平台互通、低延迟、高并发的音视频服务。
  • 流:指一组按指定编码格式封装,不断发送中的音视频数据。一个用户可以同时推多条流(例如一条推摄像头数据,一条推屏幕共享数据)也可以同时拉多条流。每条流都由一个流 ID(streamID)标识。
  • 推流:把封包好的音视频数据流推送到 ZEGO 实时音视频云的过程。
  • 拉流:从 ZEGO 实时音视频云将已有音视频数据流拉取播放的过程。
  • 房间:是 ZEGO 提供的音视频空间服务,用于组织用户群,同一房间内的用户可以互相收发实时音视频及消息。
    1. 用户需要先登录某个房间,才能进行推流、拉流操作。
    2. 用户只能收到自己所在房间内的相关消息(用户进出、音视频流变化等)。
    3. 每个房间由一个 ApplD 内唯一的 roomlD 标识。所有使用同一个 roomID 登录房间的用户即属于同房间。

更多相关概念请参考 术语说明。

2 前提条件

在实现基本的实时音视频功能之前,请确保:

  • 已在项目中集成 ZEGO Express SDK,详情请参考 快速开始 - 集成。
  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理 中的“项目信息”。

3 实现流程

用户通过 ZEGO Express SDK 进行视频通话的基本流程为:

用户 A、B 加入房间,用户 B 预览并将音视频流推送到 ZEGO 云服务(推流),用户 A 收到用户 B 推送音视频流的通知之后,在通知中播放用户 B 的音视频流(拉流)。

/Pics/Common/ZegoExpressEngine/common_usage.png

3.1 创建引擎

1. 创建界面

根据场景需要,为您的项目创建视频通话的用户界面。我们推荐您在项目中添加如下元素:

  • 本地视频窗口
  • 远端视频窗口
  • 结束通话按钮

/Pics/QuickStart/express_quickstart_video_call.png

添加视频通话界面的代码
Row()
{
    XComponent({ id: 'view_id', type: XComponentType.SURFACE, libraryname: 'ZegoExpressEngine' })
    .onLoad(() => {
        hilog.info(0x0000, LogTag, 'XComponent on load');
    }).width('240').height('360')
}

Row()
{
    XComponent({ id: 'remote_view_id', type: XComponentType.SURFACE, libraryname: 'ZegoExpressEngine' })
    .onLoad(() => {
        hilog.info(0x0000, LogTag, 'XComponent on load');
    }).width('240').height('360')
}

2. 创建引擎

调用 createEngine 方法,将 前提条件 中申请到的 AppID 传入参数 “appID”,创建引擎单例对象。

如果需要将日志信息输出到 DevEco Studio 的 HiLog 窗口,可参考 常见问题 中的“鸿蒙系统如何打印调试日志?”实现。

// 定义 SDK 引擎对象
ZegoExpressInstance: ZegoExpressEngine| null = null

// 填写 appID 和 appSign
let appID = xxx;  // 请通过官网注册获取,格式为:1234567890
let appSign = '';  //请通过官网注册获取,格式为:@"0123456789012345678901234567890123456789012345678901234567890123"(共64个字符)

// 在 ability 中获取 Context,然后导出,并且具体的 entry page 中引入该 Context 对象
// 全局定义 Context 和 LogPath
export let Context: object
export let logPath: string

// 在 ability 的 onWindowStageCreate 方法中获取 Context
let applicationContext: common.Context;
applicationContext = this.context.getApplicationContext();
logPath = applicationContext.filesDir.toString();

// 创建引擎,通用场景接入,传入 Context 对象,并根据业务需求注册相关回调
// set log config
let logConfig = new ZegoLogConfig();
logConfig.logPath = logPath;
ZegoExpressEngine.setLogConfig(logConfig)

// create engine
let profile = new ZegoEngineProfile(KeyCenter.appID, KeyCenter.appSign, ZegoScenario.General, Context);
ZegoExpressEngine.createEngine(profile).then((engineInst) => {
    // 得到 engine 实例
    this.ZegoExpressInstance = engineInst;
})

3. 设置回调

创建引擎后,您可以通过引擎实例的on方法注册回调。

为避免错过事件通知,建议在在创建引擎后立即注册回调

常见通知回调

用户自己在房间内的连接状态变化通知

onRoomStateUpdate:本地调用 loginRoom 接口加入房间时,您可通过监听 onRoomStateUpdate 回调实时监控自己在本房间内的连接状态。

您可以在回调中根据不同状态处理业务逻辑。

this.ZegoExpressInstance.on('onRoomStateUpdate', (roomID: string, state: ZegoRoomState, errorCode: number, extendedData: string) => {
})

ZegoRoomState 状态含义如下:

状态 枚举值 含义
Disconnected
0
未连接状态,在登录房间前/退出房间后进入该状态。如果登录房间的过程中出现稳态异常,比如 AppID 和 AppSign 不正确,或者有相同用户名在其他地方登录导致本端被 KickOut,都会进入该状态。
Connecting
1
正在请求连接状态,登录房间动作执行成功后会进入该状态。通常情况下,可通过该状态进行 UI 界面的展示。如果是因为网络质量不佳产生的中断,SDK 内部会进行重试,也会进入正在请求连接状态。
Connected
2
连接成功状态,成功登录房间后进入该状态。此时,用户可以正常收到房间内的用户和流信息增删变化的回调通知。

详情可参考 房间连接状态说明 。

其他用户进出房间的通知

只有在调用 loginRoom 接口登录房间时传的 ZegoRoomConfig 中的 “isUserStatusNotify” 参数为 “true” 时,用户才能收到 onRoomUserUpdate 回调。

用户自己加入房间时,不会触发此回调。

onRoomUserUpdate :同一房间内的其他用户进出房间时,您可通过此回调收到通知。

回调中的 ZegoUpdateType 参数为 “Add” 时,表示有用户进入了房间;为 “Delete” 时,表示有用户退出了房间。

this.ZegoExpressInstance.on('onRoomUserUpdate', (roomID: string, updateType: ZegoUpdateType, userList: ZegoUser[]) =>{
    // 您可以在回调中根据用户的进出/退出情况,处理对应的业务逻辑
    if(updateType == ZegoUpdateType.Add)
    {
        // 用户进入了房间
    }
    else
    {
        // 用户退出了房间
    }
});

用户推送音视频流的状态通知

onPublisherStateUpdate:根据实际应用需要,用户推送音视频流之后,当推送视频流的状态发生变更时(例如出现网络中断导致推流异常等情况),您会收到此回调,同时 SDK 会进行自动进行重试。

this.ZegoExpressInstance.on('onPublisherStateUpdate', (streamID: string, state: ZegoPublisherState, errorCode: number, extendedData: string) => {
    if (errorCode != 0) {
        // 推流状态出错
    } else {
        if(state ==ZegoPublisherState.Publishing)
        {
            // 正在推流
        }
        else if(state ==ZegoPublisherState.NoPublish)
        {
            // 没有推流
        }
        else if(state ==ZegoPublisherState.PublishRequesting)
        {
            // 正在请求推流
        }
    }
 })

用户拉取音视频流的状态通知

onPlayerStateUpdate:根据实际应用需要,用户拉取音视频流之后,当拉取的音视频流的状态发生变更时(例如出现网络中断导致拉流异常等情况),您会收到该回调,同时 SDK 会进行自动进行重试。

this.ZegoExpressInstance.on('onPlayerStateUpdate', (streamID: string, state: ZegoPlayerState, errorCode: number, extendedData: string) => {
    if (errorCode != 0) {
        // 拉流状态出错
    } else {
        if(state ==ZegoPlayerState.Playing)
        {
             // 正在拉流中
        }
        else if(state ==ZegoPlayerState.PlayRequesting)
        {
            // 正在请求拉流中
        }
        else if(state ==ZegoPlayerState.NoPlay)
        {
            // 未进行拉流
        }
    }
})

3.2 登录房间

1. 登录

传入用户 ID 参数 “userID” 创建 ZegoUser 用户对象后,调用 loginRoom 接口,传入房间 ID 参数 “roomID” 和用户参数 “user”,登录房间。

“roomID” 和 “user” 的参数由您本地生成,但是需要满足以下条件:

  • 同一个 AppID 内,需保证 “roomID” 全局唯一。
  • 同一个 AppID 内,需保证 “userID” 全局唯一,建议开发者将其设置成一个有意义的值,可将 “userID” 与自己业务账号系统进行关联。

ZegoUser 的构造方法 public ZegoUser(String userID) 会将 “userName” 设为与传的参数 “userID” 一样。“userID” 不能为 “null”,否则会导致登录房间失败。

// 创建用户
let user = new ZegoUser();
user.userID = this.currentUserID;
user.userName = this.currentUserName;

// 登录房间
let roomConfig = new ZegoRoomConfig();
roomConfig.isUserStatusNotify = true;
this.ZegoExpressInstance.loginRoom('room_id', user, roomConfig);

调用登录房间接口之后,您可通过监听 onRoomStateUpdate 回调实时监控自己在本房间内的连接状态,详情请参考 房间内的连接状态变化通知。

只有当房间状态是连接成功时,才能进行推流(startPublishingStream )、拉流(startPlayingStream )等操作。

this.ZegoExpressInstance.on('onRoomStateUpdate', (roomID: string, state: ZegoRoomState, errorCode: number, extendedData: string) => {
    this.logInfo('onRoomStateUpdate. roomID: ' + roomID + ", state: " + state + ", code: "+ errorCode)
    // 更新本地登录状态
    this.currentLoginState = state;

    // 已登录房间
    if(state == ZegoRoomState.Connected)
    {
        // todo 业务逻辑,只有当房间状态是连接成功时,才能进行推流(startPublishingStream)、拉流(startPlayingStream)等操作
    }
    // 房间连接中
    else if(state == ZegoRoomState.Connecting)
    {
        // todo 业务逻辑
    }
    // 房间连接断开
    else
    {
        // todo 业务逻辑,这里可以关注回调参数 errorCode 分析具体失败的原因
    }   
})

3.3 预览自己的画面,并推送到 ZEGO 实时音视频云

1. (可选)预览自己的画面

设置预览视图并启动本地预览

如果希望看到本端的画面,可调用 startPreview 接口设置预览视图,并启动本地预览。

无论是否调用 startPreview 预览,都可以将自己的音视频流推送到 ZEGO 音视频云。

开启预览后,如果无法正常看到本地预览画面,可以检查设置的本地预览视图类型是否正确。

// 设置本地预览视图并启动预览,视图模式采用 SDK 默认的模式,等比缩放填充整个 View
let view = new ZegoView();
view.view = "view_id";
this.ZegoExpressInstance.startPreview(view, ZegoPublishChannel.Main);

2. 将自己的音视频流推送到 ZEGO 实时音视频云

在用户登录房间成功之后,调用 startPublishingStream 接口,传入 “streamID”,将自己的音视频流推送到 ZEGO 实时音视频云。

“streamID” 由您本地生成,但是需要保证: 同一个 AppID 下,“streamID” 全局唯一。如果同一个 AppID 下,不同用户各推了一条 “streamID” 相同的流,会导致后推流的用户推流失败。

此处在登录房间成功后(即 onRoomStateUpdate 回调通知开发者当前用户的连接状态为 “Connected”),立即进行推流。在实现具体业务时,您可选择其他时机进行推流,只要保证当前房间连接状态是连接成功的即可。

this.ZegoExpressInstance.on('onRoomStateUpdate', (roomID: string, state: ZegoRoomState, errorCode: number, extendedData: string) => {
    this.logInfo('onRoomStateUpdate. roomID: ' + roomID + ", state: " + state + ", code: "+ errorCode)
    // 更新本地登录状态
    this.currentLoginState = state;

    // 已登录房间
    if(state == ZegoRoomState.Connected)
    {
        // todo 业务逻辑, 推流
        let publisherConfig = new ZegoPublisherConfig();
        publisherConfig.roomID = this.currentRoomID;
        this.ZegoExpressInstance.startPublishingStream(this.currentStreamID, publisherConfig, ZegoPublishChannel.Main);
    }
})

3.4 拉取其他用户的音视频流

进行视频通话时,我们需要拉取到其他用户的音视频流。

onRoomStreamUpdate:在同一房间内的其他用户将音视频流推送到 ZEGO 实时音视频云时,我们会在此回调中收到音视频流新增的通知。

我们可以在该回调中,调用 startPlayingStream 接口,传入 “streamID” 拉取播放该用户的音视频流。

拉取播放用户的音视频流后,如果无法正常看到远端画面,可以检查设置的远端拉流渲染视图类型是否正确。

this.ZegoExpressInstance.on('onRoomStreamUpdate', (roomID: string, updateType: ZegoUpdateType, streamList: ZegoStream[], extendedData: string) => {
    this.logInfo('onRoomStreamUpdate. roomID: ' + roomID + ", updateType: " + updateType + ", extendedData: "+ extendedData)
    if(updateType == ZegoUpdateType.Add)
    {
        // 这里示例拉流默认的第一条流,具体取决于业务逻辑
        // 开始拉流,设置远端拉流渲染视图,视图模式采用 SDK 默认的模式,等比缩放填充整个 View
        let stream = streamList[0];
        let view = new ZegoView();
        view.view = "remote_view_id";
        let playerConfig = new ZegoPlayerConfig();
        playerConfig.resourceMode = ZegoStreamResourceMode.Default;
        this.ZegoExpressInstance.startPlayingStream(stream.streamID, view, playerConfig);
    }
})

注意事项

如果您在音视频通话的过程中,遇到相关错误,可查询 错误码。

3.5 在线测试推拉流功能

在真机中运行项目,运行成功后,可以看到本端视频画面。

为方便体验,ZEGO 提供了一个 Web 端调试示例 ,在该页面下,输入相同的 AppID、RoomID,输入不同的 UserID、以及对应的 Token,即可加入同一房间与真机设备互通。当成功开始音视频通话时,可以听到远端的音频,看到远端的视频画面。

3.6 停止音视频通话

3.6.1 停止推送/拉取音视频流

1. 停止推流/预览

调用 stopPublishingStream 接口停止向远端用户发送本端的音视频流。

// 停止推流
this.ZegoExpressInstance.stopPublishingStream();

如果启用了本地预览,调用 stopPreview 接口停止预览。

// 停止本地预览
this.ZegoExpressInstance.stopPreview()

2. 停止拉流

调用 stopPlayingStream 接口停止拉取远端推送的音视频流。

// 停止拉流
this.ZegoExpressInstance.stopPlayingStream(this.currentPlayStreamID);

3.6.2 退出房间

调用 logoutRoom 接口退出房间。

// 退出房间
this.ZegoExpressInstance.logoutRoom(this.currentRoomID);

3.6.3 销毁引擎

如果用户彻底不使用音视频功能时,可调用 destroyEngine 接口销毁引擎,释放麦克风、摄像头、内存、CPU 等资源。

ZegoExpressEngine.destroyEngine();
// reset local engine
this.ZegoExpressInstance = null;

4 API 时序图

整个推拉流过程的 API 调用时序如下图:

时序图

5 常见问题

  1. 调用 logoutRoom 接口退出房间时,能否直接杀掉进程?

    调用 logoutRoom 接口后直接杀掉进程,有一定概率会导致 logoutRoom 信令没发出去,ZEGO 服务端只能等心跳超时后才认为该用户退出了房间,为了确保 logoutRoom 信令发送出去,建议再调用 destroyEngine 接口并收到回调后再杀进程。

  2. 鸿蒙系统如何打印调试日志?

    鸿蒙系统提供了与 Android Log 类似的日志打印工具类 HiLog,具体用法可参考 HiLog 日志打印 。

本篇目录
  • 免费试用
  • 提交工单
    咨询集成、功能及报价等问题
    电话咨询
    400 1006 604
    咨询客服
    微信扫码,24h在线

    联系我们

  • 文档反馈