基于 ZEGO SDK 实现 iOS 一对一音视频聊天应用

1 准备环境

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

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

2 项目准备

2.1 创建项目

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

App ID的获取方式很简单,只需3~5分钟,在即构官网-我的项目-创建即可。创建的项目信息可用于SDK的集成和配置;

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”。


    image.png
  1. 填写表单并选取各个选项来配置项目,完成后,单击 “Next”。
    必须提供 “Product Name” 和 “Organization Identifier”,用于创建 App 的唯一标识 “Bundle Identifier”。


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


    image.png

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 的音视频流(拉流)。

image.png

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

image.png

4.1 初始化

1. 创建界面

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

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


    image.png

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 回调实时监控自己在本房间内的连接状态。

3.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"];
}

3.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开发者帐号,快速开始。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容