实时音视频
  • iOS
  • Android
  • macOS
  • Windows
  • HarmonyOS
  • Linux
  • Web
  • 小程序
  • Flutter
  • Electron
  • Unreal Engine
  • Unity3D : C#
  • uni-app
  • React Native
  • Cocos Creator
  • 产品简介
  • 下载
  • 快速开始
    • 跑通示例源码
    • 集成 SDK
    • 实现视频通话
    • 实时音视频 SDK 与实时语音 SDK 差异
    • 场景化音视频配置
  • 通信能力
  • 房间能力
  • 音频能力
  • 视频能力
  • 直播能力
  • 其他能力
  • 客户端 API
  • 服务端 API
  • 常见错误码
  • 常见问题
  • 文档中心
  • 实时音视频
  • 快速开始
  • 实现视频通话

实现视频通话

更新时间:2023-12-07 15:10

1 功能简介

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

相关概念解释:

  • ZEGO Express SDK:由 ZEGO 提供的实时音视频 SDK,能够为开发者提供便捷接入、高清流畅、多平台互通、低延迟、高并发的音视频服务。
  • 推流:把采集阶段封包好的音视频数据流推送到 ZEGO 实时音视频云的过程。
  • 拉流:从 ZEGO 实时音视频云将已有音视频数据流拉取播放的过程。
  • 房间:是 ZEGO 提供的音视频空间服务,用于组织用户群,同一房间内的用户可以互相收发实时音视频及消息。
    1. 用户需要先登录某个房间,才能进行推流、拉流操作。
    2. 用户只能收到自己所在房间内的相关消息(用户进出、音视频流变化等)。

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

2 前提条件

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

SDK 同时也支持 Token 鉴权,若您对项目安全性有更高要求,建议您升级鉴权方式,详情请参考 如何从 AppSign 鉴权升级为 Token 鉴权

3 使用步骤

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

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

/Pics/Common/ZegoExpressEngine/common_usage.png

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

3.1 创建引擎

1. 创建界面(可选)

添加界面元素

在开始之前,推荐开发者添加以下界面元素,方便实现基本的实时音视频功能。

  • 本地预览窗口
  • 远端视频窗口
  • 结束按钮
界面图

2. 创建引擎

调用 CreateEngine 接口,将申请到到 AppID 和 AppSign 传入参数 “appId” 和 “appSign”,创建引擎单例对象。

// 定义 SDK 引擎对象
ZegoExpressEngine engine;

ZegoEngineProfile profile = new ZegoEngineProfile();
profile.appID = appID; // 请通过官网注册获取,格式为 123456789
profile.appSign = appSign; // 请通过官网注册获取,格式为"0123456789012345678901234567890123456789012345678901234567890123",64个字符
profile.scenario = ZegoScenario.HighQualityVideoCall; // 高品质音视频通话场景接入(请根据实际情况选择合适的场景)
// 初始化SDK
engine = ZegoExpressEngine.CreateEngine(profile);

3.2 登录房间

1. 登录

创建 ZegoUser 用户对象,设置用户信息 “userID” 和 “userName” 后,调用 LoginRoom,传入房间 ID 参数 “roomId” 和用户参数 “user”,登录房间。如果房间不存在,调用该接口时会创建并登录此房间。

  • 同一个 AppID 内,需保证 “roomId” 全局唯一。
  • 同一个 AppID 内,需保证 “userId” 全局唯一,建议开发者将其设置成一个有意义的值,可将 “userId” 与自己业务账号系统进行关联。
  • “userId” 与 “userName” 不能为空,否则会导致登录房间失败。
  • 在 WebGL 环境中运行时,仅支持 Token 鉴权,Token 不能为空,请参考 使用 Token 鉴权
// 创建用户
ZegoUser user = new ZegoUser();
user.userId="xxx";
user.userName="xxxx";
// 只有传入 “isUserStatusNotify” 参数取值为 “true” 的 ZegoRoomConfig,才能收到 onRoomUserUpdate 回调。
ZegoRoomConfig roomConfig = new ZegoRoomConfig();
// 如果您使用 AppSign 的方式鉴权,Token 参数不需填写(除 WebGL 平台外);如果需要使用更加安全的 鉴权方式:Token 鉴权,请参考[如何从 AppSign 鉴权升级为 Token 鉴权](https://doc-zh.zego.im/faq/token_upgrade?product=ExpressVideo&platform=all)
// 在WebGL平台上,“token” 不能为空
roomConfig.token = "xxxx";
roomConfig.isUserStatusNotify = true;
// 登录房间
engine.LoginRoom("123666", user, roomConfig);

2. 监听登录房间后的事件回调

根据实际需要,在登录房间后监听想要关注的事件通知,比如房间状态更新、用户状态更新、流状态更新等。

  • OnRoomStateUpdate:房间状态更新回调。登录房间后,当房间连接状态发生变更(如出现房间断开,登录认证失败等情况),SDK 会通过该回调通知。

  • OnRoomUserUpdate:用户状态更新回调。登录房间后,当房间内有用户新增或删除时,SDK 会通过该回调通知。

    只有调用 loginRoom 接口登录房间时传入 ZegoRoomConfig 配置,且 “isUserStatusNotify” 参数取值为 “true” 时,用户才能收到 onRoomUserUpdate 回调。

  • OnRoomStreamUpdate:流状态更新回调。登录房间后,当房间内有用户新推送或删除音视频流时,SDK 会通过该回调通知。

事件回调皆为 ZegoExpressEngine 的委托,开发者直接将自己实现的回调处理函数赋值给 ZegoExpressEngine 对应的委托,即可收到回调并进行处理。

  • 只有调用 loginRoom 接口登录房间时传入 ZegoRoomConfig 配置,且 “isUserStatusNotify” 参数取值为 “true” 时,用户才能收到 onRoomUserUpdate 回调。
  • 通常情况下,如果某个用户想要播放其他用户推送的视频,可以在收到流状态更新(新增)的回调中,调用 startPlayingStream 接口拉取远端推送的音视频流。
// 房间状态更新回调
public void OnRoomStateUpdate(string roomId, ZegoRoomState state, int errorCode, string extendedData) {
    // 根据需要实现事件回调      
}

// 用户状态更新回调
private void OnRoomUserUpdate(string roomId, ZegoUpdateType updateType, List<ZegoUser> userList, uint userCount) {
    // 根据需要实现事件回调
}

// 流状态更新回调
private void OnRoomStreamUpdate(string roomId, ZegoUpdateType updateType, List<ZegoStream> streamInfoList, uint streamInfoCount) {
    // 根据需要实现事件回调    
}

// 赋值给 ZegoExpressEngine 对应的事件委托
engine.onRoomStateUpdate = OnRoomStateUpdate;
engine.onRoomUserUpdate = OnRoomUserUpdate;
engine.onRoomStreamUpdate = OnRoomStreamUpdate;

3.3 推流

1. 开始推流

调用 StartPublishingStream 接口,传入流 ID 参数 “streamId”,向远端用户发送本端的音视频流。

同一个 AppID 内,需保证 “streamId” 全局唯一。如果同一个 AppID 内,不同用户各推了一条 “streamId” 相同的流,会导致后推流的用户推流失败。

// 开始推流
engine.StartPublishingStream("stream1");

2. 启用本地预览(可选)

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

开发者如果希望看到自己本端的画面,可调用 StartPreview 接口启动本地预览。

由于 Android 设备和 iOS 设备的屏幕方向存在 Portrait、PortraitUpsideDown、LandscapeLeft、LandscapeRight 四种方向,为保证推流预览和拉流的显示界面始终在正确的方向,需要先添加推流预览和拉流的显示界面自适应横竖屏代码,请参考 快速开始 - 集成 中的 “4 界面自适应”。

Unity 支持通过 RawImage 和 Renderer 两种渲染器进行预览。

  • 方式一:RawImage

创建 RawImage。

预览显示到 RawImage 上。

RawImageVideoSurface localVideoSurface = null;
GameObject mainLocalVideoPlane = null;

mainLocalVideoPlane = GameObject.Find("MainPreViewRawImage");

if (mainLocalVideoPlane != null && localVideoSurface == null)
{
    // 添加一个 RawImageVideoSurface 组件,渲染器为 RawImage
    localVideoSurface = mainLocalVideoPlane.AddComponent<RawImageVideoSurface>();
    // 设置为本地预览画面
    localVideoSurface.SetCaptureVideoInfo();
    // 设置视频源为engine
    localVideoSurface.SetVideoSource(engine);
}

// 调用开始预览接口
engine.StartPreview();
  • 方式二:Renderer

创建带 Renderer 的 Plane。

预览到 Plane 的 Renderer 上。

GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane);//代码创建的Plane

if (go == null)
{
    return;
}
go.name = "preview-render";
// 保证画面能够在屏幕显示
go.transform.Rotate(90.0f, -180.0f, 0.0f);
go.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
go.transform.localScale = new Vector3(0.36f, 1f, 0.64f);

// 添加一个 RendererVideoSurface 组件,渲染器为 Renderer
RendererVideoSurface renderer = go.AddComponent<RendererVideoSurface>();
// 设置为本地预览画面
renderer.SetCaptureVideoInfo();
// 设置视频源为engine
renderer.SetVideoSource(engine);

// 调用开始预览接口
engine.StartPreview();

3. 监听推流后的事件回调

根据实际应用需要,在推流后监听想要关注的事件通知,比如推流状态更新、推流质量等。

  • OnPublisherStateUpdate:推流状态更新回调。调用推流接口成功后,当推流状态发生变更(如出现网络中断导致推流异常等情况),SDK 在重试推流的同时,会通过该回调通知。
  • OnPublisherQualityUpdate:推流质量回调。调用推流接口成功后,定时回调音视频流质量数据(如分辨率、帧率、码率等)。

事件回调皆为 ZegoExpressEngine 的委托,开发者直接将自己实现的回调处理函数赋值给 ZegoExpressEngine 对应的委托,即可收到回调并进行处理。

// 推流状态更新回调
public void OnPublisherStateUpdate(string streamId, ZegoPublisherState state, int errorCode, string extendedData)
{
    // 根据需要实现事件回调
}

// 推流质量回调
private void OnPublisherQualityUpdate(string streamId, ZegoPublishStreamQuality quality)
{
    // 根据需要实现事件回调
}


// 赋值给 ZegoExpressEngine 对应的事件委托
engine.onPublisherStateUpdate = OnPublisherStateUpdate;
engine.onPublisherQualityUpdate = OnPublisherQualityUpdate;
engine.StartPublishingStream("stream1");

如果您需要了解 Express 的摄像头/视频/麦克风/音频/扬声器相关接口,请参考 常见问题 - 如何实现开关摄像头/视频画面/麦克风/音频/扬声器?

3.4 拉流

1. 开始拉流

调用 StartPlayingStream 接口,根据传入的流 ID 参数 “streamID”,拉取远端推送的音视频流。

远端用户推送的 “streamID” 可以从 ZegoExpressEngine 委托中的 OnRoomStreamUpdate 回调中获得。

由于 Android 设备和 iOS 设备的屏幕方向存在 Portrait、PortraitUpsideDown、LandscapeLeft、LandscapeRight 四种方向,为保证推流预览和拉流的显示界面始终在正确的方向,需要先添加推流预览和拉流的显示界面自适应横竖屏代码,请参考 快速开始 - 集成 中的 “4 界面自适应”。

Unity3D 支持通过 RawImage 和 Renderer 两种渲染器进行拉流渲染。

  • 方式一:RawImage

创建 RawImage。

预览显示到 RawImage 上。

private const float Offset = 100;

GameObject go = new GameObject();

if (go == null)
{
    return;
}

go.name = "play-rawimage";
// 动态创建RawImage
go.AddComponent<RawImage>();
GameObject canvas = GameObject.Find("Canvas");
if (canvas != null)
{
    go.transform.parent = canvas.transform;
}
float xPos = UnityEngine.Random.Range(Offset - Screen.width / 2f, Screen.width / 2f - Offset);
float yPos = UnityEngine.Random.Range(Offset, Screen.height / 2f - Offset);
go.transform.localPosition = new Vector3(xPos, yPos, 0f);
go.transform.localScale = new Vector3(3f, 4f, 1f);

// 添加一个 RawImageVideoSurface 组件,渲染器为 RawImage
RawImageVideoSurface videoSurface = go.AddComponent<RawImageVideoSurface>();
// 设置想要显示的视频流id
videoSurface.SetPlayVideoInfo("123");
// 设置视频源为engine
videoSurface.SetVideoSource(engine);

// 调用拉流接口
engine.StartPlayingStream("123");
  • 方式二:Renderer

创建带 Renderer 的 Plane。

预览到 Plane 的 Renderer 上。

remoteVideoPlane = GameObject.Find("PlayRender");
if (remoteVideoPlane != null)
{
    if (remoteVideoSurface == null)
    {
        // 添加一个 RendererVideoSurface 组件,渲染器为 Renderer
        remoteVideoSurface = remoteVideoPlane.AddComponent<RendererVideoSurface>();
        // 保证画面能够在屏幕显示
        remoteVideoSurface.transform.Rotate(90.0f, -180.0f, 0.0f);
        remoteVideoSurface.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
        remoteVideoSurface.transform.localScale = new Vector3(0.36f, 1f, 0.64f);
    }
    if (remoteVideoSurface != null)
    {   
        // 设置想要显示的视频流id
        remoteVideoSurface.SetPlayVideoInfo("123");
        // 设置视频源为 engine
        remoteVideoSurface.SetVideoSource(engine);
    }
}

// 调用拉流接口
engine.StartPlayingStream(“123”);

2. 监听拉流后的事件回调

根据实际应用需要,在拉流后监听想要关注的事件通知,比如拉流状态更新、拉流质量、流媒体事件等。

  • OnPlayerStateUpdate:拉流状态更新回调。调用拉流接口成功后,当拉流状态发生变更(如出现网络中断导致推流异常等情况),SDK 在重试拉流的同时,会通过该回调通知。
  • OnPlayerQualityUpdate:拉流质量回调。拉流成功后每 3 秒会收到此回调,通过该回调可以获取拉取的音视频流的帧率,码率,RTT,丢包率等质量数据,实时监控拉取流的健康情况。
  • OnPlayerMediaEvent:流媒体事件回调。当拉流发生音视频卡顿以及恢复等事件发生时会触发此回调。

事件回调皆为 ZegoExpressEngine 的委托,开发者直接将自己实现的回调处理函数赋值给 ZegoExpressEngine 对应的委托,即可收到回调并进行处理。

// 拉流状态更新回调
public void OnPlayerStateUpdate(string streamId, ZegoPlayerState state, int errorCode, string extendedData)
{
    // 根据需要实现事件回调
}

// 拉流质量回调
private void OnPlayerQualityUpdate(string streamId, ZegoPlayStreamQuality quality)
{
    // 根据需要实现事件回调
}

// 流媒体事件回调;请注意,该回调在 WebGL 平台不支持
private void OnPlayerMediaEvent(string streamId, ZegoPlayerMediaEvent mediaEvent)
{
    // 根据需要实现事件回调
}

// 赋值给 ZegoExpressEngine 对应的事件委托
engine.onPlayerStateUpdate = OnPlayerStateUpdate;

// 请注意,onPlayerMediaEvent 事件回调在 WebGL 平台不支持
engine.onPlayerMediaEvent = OnPlayerMediaEvent;
engine.onPlayerQualityUpdate = OnPlayerQualityUpdate;
engine.StartPlayingStream("123");

3.5 体验实时音视频功能

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

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

3.6 停止推拉流

1. 停止推流/预览

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

// 停止推流
engine.StopPublishingStream();

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

// 停止本地预览
engine.StopPreview();

2. 停止拉流

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

如果开发者通过 OnRoomStreamUpdate 回调收到了音视频流 “减少” 的通知,请及时调用 StopPlayingStream 接口停止拉流,避免拉到空流、产生额外的成本;或者,开发者可以根据自己的业务需求,选择合适的时机,主动调用 StopPlayingStream 接口停止拉流。

// 停止拉流
engine.StopPlayingStream("123");

3.7 退出房间

调用 LogoutRoom 接口退出房间。

// 退出房间
engine.LogoutRoom("123666");

3.8 销毁引擎

调用 DestroyEngine 接口销毁引擎,用于释放 SDK 使用的资源。

// 销毁引擎实例
ZegoExpressEngine.DestroyEngine();
本篇目录