1 准备环境

在开始集成 ZEGO Express SDK 前,请确保开发环境满足以下要求:

  • Xcode 12.0 或以上版本。
  • iOS 9.0 或以上版本且支持音视频的 iOS 设备。
  • iOS 设备已经连接到 Internet。

2 项目准备

2.1 创建项目

进入即构官网,【ZEGO控制台】创建项目,并申请有效的 AppID,这一步很关键,appid为应用的唯一标识,如身份证号,是应用的身份证明,用于明确你的项目及组织身份。zego提供的服务也是基于APP ID;

2.2 Token 鉴权

  • 登录房间时必须 使用 Token 鉴权 ,可参考 Token 鉴权教程
  • 为了方便开发阶段的调试,开发者可直接在 ZEGO 控制台获取临时 Token(有效期为 24 小时) 来使用,详情请参考 控制台(新版) - 项目管理 中的 “项目信息”。

3 集成

3.1 项目设置

开始集成前,可参考如下步骤设置你的项目;

如已有项目,本步骤可忽略。

如需新建项目,可按照以下步骤创建你的新项目:

1. 启动 Xcode,在 “Welcome to Xcode” 窗口中单击 “Create a new Xcode project” 或选择 “File > New > Project” 菜单。在出现的表单中,选择 iOS 平台,并在 “Application” 下选择 “App”。

2. 填写表单并选取各个选项来配置项目,完成后,单击 “Next”。

必须提供 “Product Name” 和 “Organization Identifier”,用于创建 App 的唯一标识 “Bundle Identifier”。

3. 选择项目存储路径,单击 “Create” 创建项目。

3.2 导入 SDK

使用 CocoaPods 自动集成

  1. 安装 CocoaPods
  2. 打开终端,进入项目根目录,执行 pod init 命令创建 Podfile 文件。
  3. 打开 Podfile 文件,在 “target” 下添加 po``d``'``Z``ego``E``xpress``Engine/V``ideo',需要将 “MyProject” 替换为开发者的 Target 名称。
target 'MyProject' do
    use_frameworks!
    pod 'ZegoExpressEngine/Video'
end
  1. 执行 pod repo update 命令更新本地索引,确保能安装最新版本的 SDK,最新版本号请参考 下载 SDK 包 中的发布历史。
  2. 执行 pod install 命令安装 SDK。

4 实现流程

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

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

整个音视频通话推拉流过程的 API 调用时序如下图:

4.1 初始化

1. 创建界面

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

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

2.引入头文件,准备基础工作

// 引入 ZegoExpressEngine.h 头文件
#import <ZegoExpressEngine/ZegoExpressEngine.h>

@interface ViewController ()<ZegoEventHandler>
//拉取播放其他用户音视频流的 view
@property (strong, nonatomic) UIView *remoteUserView;
//开始视频通话的按钮
@property (strong, nonatomic) UIButton *startVideoTalkButton;
//停止视频通话的按钮
@property (strong, nonatomic) UIButton *stopVideoTalkButton;

@end
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
}

- (void)setupUI {
    self.remoteUserView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 180, 250)];
    self.remoteUserView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.remoteUserView];

    self.startVideoTalkButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.view addSubview:self.startVideoTalkButton];
    self.startVideoTalkButton.frame = CGRectMake(100, self.view.bounds.size.height - 280, 150, 50);
    [self.startVideoTalkButton.titleLabel setFont:[UIFont systemFontOfSize:32]];
    [self.startVideoTalkButton setTitle:@"开始通话" forState:UIControlStateNormal];
    [self.startVideoTalkButton addTarget:self action:@selector(startVideoTalk:) forControlEvents:UIControlEventTouchUpInside];

    self.stopVideoTalkButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.view addSubview:self.stopVideoTalkButton];
    self.stopVideoTalkButton.frame = CGRectMake(100, self.view.bounds.size.height - 200, 150, 50);
    [self.stopVideoTalkButton.titleLabel setFont:[UIFont systemFontOfSize:32]];
    [self.stopVideoTalkButton setTitle:@"停止通话" forState:UIControlStateNormal];
    [self.stopVideoTalkButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [self.stopVideoTalkButton addTarget:self action:@selector(stopVideoTalk:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)startVideoTalk:(UIButton *)button {
    [self createEngine];
    [self loginRoom];
    [self startPublish];
}

3. 创建引擎

调用 createEngineWithProfile 接口,将申请到的 AppID 传入参数 “appID”,创建引擎单例对象。

注册回调,可将实现了 ZegoEventHandler 的对象(例如 “self”)传入参数 “eventHandler”。

- (void)createEngine {
    ZegoEngineProfile *profile = [[ZegoEngineProfile alloc] init];
    // 请通过官网注册获取,格式为:1234567890
    profile.appID = <#appID#>;
    //通用场景接入
    profile.scenario = ZegoScenarioGeneral;
    // 创建引擎,并注册 self 为 eventHandler 回调。不需要注册回调的话,eventHandler 参数可以传 nil,后续可调用 "-setEventHandler:" 方法设置回调
    [ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];
}

6.2 登录房间

调用 loginRoom 接口登录房间。roomID 和 user 的参数由您本地生成,但是需要满足以下条件:

  • 同一个 AppID 内,需保证 “roomID” 全局唯一。
  • 同一个 AppID 内,需保证 “userID” 全局唯一,建议开发者将 “userID” 与自己业务的账号系统进行关联。
- (void)loginRoom {
    // roomID 由您本地生成,需保证 “roomID” 全局唯一。不同用户要登陆同一个房间才能进行通话
    NSString *roomID = @"room1";
    // 创建用户对象,ZegoUser 的构造方法 userWithUserID 会将 “userName” 设为与传的参数 “userID” 一样。“userID” 与 “userName” 不能为 “nil”,否则会导致登录房间失败。
    // userID 由您本地生成,需保证 “userID” 全局唯一。
    ZegoUser *user = [ZegoUser userWithUserID:@"user1"];
    // 只有传入 “isUserStatusNotify” 参数取值为 “true” 的 ZegoRoomConfig,才能收到 onRoomUserUpdate 回调。
    ZegoRoomConfig *roomConfig = [[ZegoRoomConfig alloc] init];
    //token 由用户自己的服务端生成,为了更快跑通流程,也可以通过即构控制台 https://console.zego.im/dashboard 获取临时的音视频 token
    roomConfig.token = @"<#token#>";
    roomConfig.isUserStatusNotify = YES;
    // 登录房间
    [[ZegoExpressEngine sharedEngine] loginRoom:roomID user:user config:roomConfig callback:^(int errorCode, NSDictionary * _Nullable extendedData) {
        // (可选回调) 登录房间结果,如果仅关注登录结果,关注此回调即可
        if (errorCode == 0) {
            NSLog(@"房间登录成功");
        } else {
            // 登录失败,请参考 errorCode 说明 https://doc-zh.zego.im/article/4377
            NSLog(@"房间登录失败");
        }
    }];
}

登录状态(房间连接状态)回调

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

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

1. 预览自己的画面

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

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

在用户调用 loginRoom 接口后,可以直接调用 startPublishingStream 接口,传入 “streamID”,将自己的音视频流推送到 ZEGO 音视频云。您可通过监听 onPublisherStateUpdate 回调知晓推流是否成功。

“streamID” 由您本地生成,但是需要保证:

同一个 AppID 下,“streamID” 全局唯一。如果同一个 AppID 下,不同用户各推了一条 “streamID” 相同的流,后推流的用户推流失败。

- (void)startPublish {
    // 设置本地预览视图并启动预览,视图模式采用 SDK 默认的模式,等比缩放填充整个 View
    [[ZegoExpressEngine sharedEngine] startPreview:[ZegoCanvas canvasWithView:self.view]];
    // 用户调用 loginRoom 之后再调用此接口进行推流
    // 在同一个 AppID 下,开发者需要保证 “streamID” 全局唯一,如果不同用户各推了一条 “streamID” 相同的流,后推流的用户会推流失败。
    [[ZegoExpressEngine sharedEngine] startPublishingStream:@"stream1"];
}

6.4 拉取其他用户的音视频

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

在同一房间内的其他用户将音视频流推送到 ZEGO 音视频云时,我们会在 onRoomStreamUpdate 回调中收到音视频流新增的通知,并可以通过 ZegoStream 获取到某条流的 “streamID”。

我们可以在该回调中,调用 startPlayingStream ,传入 “streamID” 拉取拉取播放该用户的音视频。您可通过监听 onPlayerStateUpdate 回调知晓是否成功拉取音视频。

// 房间内其他用户推流/停止推流时,我们会在这里收到相应流增减的通知
- (void)onRoomStreamUpdate:(ZegoUpdateType)updateType streamList:(NSArray<ZegoStream *> *)streamList extendedData:(NSDictionary *)extendedData roomID:(NSString *)roomID {
    //当 updateType 为 ZegoUpdateTypeAdd 时,代表有音视频流新增,此时我们可以调用 startPlayingStream 接口拉取播放该音视频流
    if (updateType == ZegoUpdateTypeAdd) {
        // 开始拉流,设置远端拉流渲染视图,视图模式采用 SDK 默认的模式,等比缩放填充整个View
        // 如下 remoteUserView 为 UI 界面上 View.这里为了使示例代码更加简洁,我们只拉取新增的音视频流列表中第的第一条流,在实际的业务中,建议开发者循环遍历 streamList ,拉取每一条音视频流
        NSString *streamID = streamList[0].streamID;
        [[ZegoExpressEngine sharedEngine] startPlayingStream:streamID canvas:[ZegoCanvas canvasWithView:self.remoteUserView]];
    }
}

7 运行效果

音视频的基础功能已完成,接下来我们运行下效果,1步快速检验成果。

step1 , 与好友一起安装编译好的App,在手机上可以看到对方并进行通话互动。

完成以上,说明你成功啦!

Enjoy与好友的欢乐时光!

获取更多文档、Demo、技术帮助

获取本文的Demo、开发文档、技术支持。

获取SDK的商务活动、热门产品。

注册即构ZEGO开发者帐号,快速开始。

音视频场景解决方案分享,更多详情可搜索官网(https://zegoguanwang.datasink.sensorsdata.cn/t/pB)