1 功能简介

连麦是指在直播中,主播与观众一起直播。

主要的应用场景如下:

  1. 在大型直播中,主播需要和观众互动时,主播邀请观众一起直播。
  2. 多主播同台,主播中需要两个及以上的主播同时进行直播,比如双师课堂。

注意:

此文档作为示例,所描述的主播端、观众端实现连麦的流程不涉及主播邀请观众连麦和观众申请连麦的信令交互,只要观众端在主播房间推流成功(不需要征求主播的同意),主播就拉取观众流,实现连麦。

对于主播邀请观众连麦和观众申请连麦的信令交互,开发者可根据实际的业务需求,做以下处理:

  1. 增加观众向主播申请连麦信令的收发处理,比如观众向主播发送申请连麦的信令后,观众在收到主播的同意连麦信令后才能开始推流,否则不能推流。
  2. 增加主播邀请观众连麦信令的收发处理,比如主播向观众发送邀请连麦的信令后,观众收到主播的邀请连麦信令,同意连麦之后才进行推流,实现观众与主播连麦。

主播、观众说明:

由于观众端也可以推流成为主播,为了避免歧义,下文中的观众均指登录房间时 RoomRoleAudience 的角色,主播均指登录房间时 RoomRoleAnchor 的角色。

2 下载示例 Demo

示例 Demo 源码下载地址:互动视频示例 Demo

互动视频示例 Demo 包含了基础专题和进阶专题,展示了如何使用 SDK API,开发者可参考其用法来实现自己的业务。互动视频示例 Demo 涵盖了 SDK 的大部分功能,开发者也可在示例专题 Demo 中查找 ZEGO SDK 更多的进阶功能,测试其功能和性能,以实现特定的需求。

连麦模块源码请查看 LiveRoomPlayground/Topics/JoinLive 目录,UI界面查看 LiveRoomPlayground/LiveRoomPlayground-iOS/JoinLiveUI 目录下的源文件,该模块展示了使用 ZEGO SDK 实现连麦所需调用的关键 API 接口。

注意:该示例专题的 Demo 只展示了观众观看主播,也可以选择与主播一起直播;未展示主播邀请观众连麦的邀请操作,以及观众向主播申请连麦的申请操作。

3 连麦系统架构图&时序图

3.1 连麦系统架构图

3.2 主播观众连麦交互时序图

4 主播端调用流程

4.1 初始化 SDK

请参考文档:快速开始-初始化

4.2 监听房间流新增与减少的回调

在直播连麦过程中,可能会有其他观众在直播中途连麦成为主播(在主播房间推流)或已连麦观众在直播中途结束连麦(在主播房间停止推流),所以需要监听流变化的回调以处理相关的流操作(拉流/停止拉流)。

此示例代码会拉取主播自己所在房间所有正在推的流(连麦观众流),用户可根据实际业务需求,限制拉流数量即连麦者的数量。

JoinLiveDemo.m

/**
 * 在初始化 api 后,设置房间代理,可以收到房间内的一些信息回调。
 */
[api setRoomDelegate:self];
JoinLiveDemo.m

/**
 * 开发者可以按自己的需求在回调里实现自己的业务,这里演示监听 onStreamUpdated 方法来感知房间内的流变化,以实现相关的流操作(如拉流或停止拉流) 
 */
- (void)onStreamUpdated:(int)type streams:(NSArray<ZegoStream *> *)streamList roomID:(NSString *)roomID {
    BOOL isTypeAdd = type == ZEGO_STREAM_ADD;//流变更类型:增加/删除
    if (![roomID isEqualToString:self.joinRoomID]) {
        return;
    }

    if (isTypeAdd) {
        [self addRemoteUserStreams:streamList];
    }
    else {
        [self removeRemoteUserStreams:streamList];
    }

    NSArray<NSString *> *userIDs = [[streamList valueForKeyPath:@"userID"] copy];
    if (userIDs.count > 0) {
        if ([self.delegate respondsToSelector:@selector(demo:remoteUserOnLiveUpdated:withUserIDs:)]) {
            [self.delegate demo:self remoteUserOnLiveUpdated:isTypeAdd withUserIDs:userIDs];
        }
    }
}

4.3 登录房间

请参考文档:快速开始-登录房间

4.4 推流

登录房间成功后开始推流。

请注意,需使用连麦模式推流,即指定 -startPublishing:title:flag: 中的 flag 为 ZEGO_JOIN_PUBLISH

ZGJoinLiveViewController.m

/**
 登录直播房间。
 对于主播,加入后直接开启直播(推流);
 对于观众,加入后播放房间内存在的直播(拉流);
 */
- (void)joinVideoLiveRoom {
    BOOL isAnchor = [self currentUserIsRoomAnchor];
    [ZegoHudManager showNetworkLoading];
    Weakify(self);
    BOOL result = [self.joinLiveDemo joinLiveRoom:self.roomID userID:self.currentUserID isAnchor:isAnchor callback:^(int errorCode, NSArray<NSString *> *joinTalkUserIDs) {
        [ZegoHudManager hideNetworkLoading];
        Strongify(self);
        if (errorCode != 0) {
            [ZegoHudManager showMessage:[NSString stringWithFormat:@"加入房间失败。errorCode:%d", errorCode]];
            return;
        }

        // 房间创建者,在进入房间后,直接开启直播
        if ([self currentUserIsRoomAnchor]) {
            [self addLocalUserLiveRenderViewRelation];
            [self.joinLiveDemo startLocalUserLive];
        }

        // 刷新数据源
        if (joinTalkUserIDs.count > 0) {
            [self addRemoteUserLivesIfNeed:joinTalkUserIDs];
        }
    }];
    if (!result) {
        [ZegoHudManager hideNetworkLoading];
        [ZegoHudManager showMessage:@"加入房间失败,请查看日志"];
    }
}
JoinLiveDemo.m

/**
 开始本地用户直播(即推流)
 */
- (void)startLocalUserLive {
    if (![self checkApiInitialized]) {
        return;
    }

    NSString *streamID = [self.dataSource localUserLiveStreamID:self];
    if (streamID.length == 0) {
        ZGLogWarn(@"开始本地直播推流,但是 streamID 为空,本次发起推流无效");
        return;
    }

    [self internalStartLocalUserLivePreview];
    if ([self.zegoApi startPublishing:streamID title:nil flag:ZEGO_JOIN_PUBLISH]) {
        self.localLiveStreamID = streamID;
    }
}

推流的详细介绍请参考文档:快速开始-推流

5 观众端调用流程

5.1 初始化 SDK

请参考文档:快速开始-初始化

5.2 监听房间流新增与减少的回调

在直播连麦过程中,可能会有其他观众在直播中途连麦成为主播(在主播房间推流)或已连麦观众在直播中途结束连麦(在主播房间停止推流),所以需要监听流变化的回调以处理相关的流操作(拉流/停止拉流)。

此示例代码在进入主播房间后拉取中途连麦者的流,或者停止拉取中途退出的连麦者对应的流,用户可根据实际业务需求,限制拉流数量。

JoinLiveDemo.m

/**
 * 在初始化 api 后,设置房间代理,可以收到房间内的一些信息回调。
 */
[api setRoomDelegate:self];
JoinLiveDemo.m

/**
 * 开发者可以按自己的需求在回调里实现自己的业务,这里演示监听 onStreamUpdated 方法来感知房间内的流变化,以实现相关的流操作(如拉流或停止拉流) 
 */
- (void)onStreamUpdated:(int)type streams:(NSArray<ZegoStream *> *)streamList roomID:(NSString *)roomID {
    BOOL isTypeAdd = type == ZEGO_STREAM_ADD;//流变更类型:增加/删除
    if (![roomID isEqualToString:self.joinRoomID]) {
        return;
    }

    if (isTypeAdd) {
        [self addRemoteUserStreams:streamList];
    }
    else {
        [self removeRemoteUserStreams:streamList];
    }

    NSArray<NSString *> *userIDs = [[streamList valueForKeyPath:@"userID"] copy];
    if (userIDs.count > 0) {
        if ([self.delegate respondsToSelector:@selector(demo:remoteUserOnLiveUpdated:withUserIDs:)]) {
            [self.delegate demo:self remoteUserOnLiveUpdated:isTypeAdd withUserIDs:userIDs];
        }
    }
}

5.3 登录房间

请参考文档:快速开始-登录房间

注意:必须登录主播所在的房间。 因为连麦操作的前提是主播正在推流,观众在与主播同一个房间里拉流观看。

5.4 拉流

登录房间成功后拉取房间内的流。

此示例代码会拉取主播房间所有正在推的流(主播流+连麦观众流),用户可根据实际业务需求,限制拉流数量。

ZGJoinLiveViewController.m

/**
 登录直播房间。
 对于主播,加入后直接开启直播(推流);
 对于观众,加入后播放房间内存在的直播(拉流);
 */
- (void)joinVideoLiveRoom {
    BOOL isAnchor = [self currentUserIsRoomAnchor];
    [ZegoHudManager showNetworkLoading];
    Weakify(self);
    BOOL result = [self.joinLiveDemo joinLiveRoom:self.roomID userID:self.currentUserID isAnchor:isAnchor callback:^(int errorCode, NSArray<NSString *> *joinTalkUserIDs) {
        [ZegoHudManager hideNetworkLoading];
        Strongify(self);
        if (errorCode != 0) {
            [ZegoHudManager showMessage:[NSString stringWithFormat:@"加入房间失败。errorCode:%d", errorCode]];
            return;
        }

        // 房间创建者,在进入房间后,直接开启直播
        if ([self currentUserIsRoomAnchor]) {
            [self addLocalUserLiveRenderViewRelation];
            [self.joinLiveDemo startLocalUserLive];
        }

        // 刷新数据源
        if (joinTalkUserIDs.count > 0) {
            [self addRemoteUserLivesIfNeed:joinTalkUserIDs];
        }
    }];
    if (!result) {
        [ZegoHudManager hideNetworkLoading];
        [ZegoHudManager showMessage:@"加入房间失败,请查看日志"];
    }
}

5.5 与主播连麦

与主播进行连麦即观众进行推流。

观众想停止与主播连麦时,停止推流即可。

若有连麦者数量的限制,可在观众推流的地方进行控制,比如已连麦观众数已经达到上限时不允许推流。

JoinLiveDemo.m

/**
 开始本地用户直播(即推流)
 */
- (void)startLocalUserLive {
    if (![self checkApiInitialized]) {
        return;
    }

    NSString *streamID = [self.dataSource localUserLiveStreamID:self];
    if (streamID.length == 0) {
        ZGLogWarn(@"开始本地直播推流,但是 streamID 为空,本次发起推流无效");
        return;
    }

    [self internalStartLocalUserLivePreview];
    if ([self.zegoApi startPublishing:streamID title:nil flag:ZEGO_JOIN_PUBLISH]) {
        self.localLiveStreamID = streamID;
    }
}

6 API 参考列表

方法 描述
- initWithAppID:appSignature:completionBlock: 初始化SDK
– setRoomDelegate: 设置房间信令相关的回调接口
- setIMDelegate: 设置 IM 回调接口,用于接收即时通信内容。
– setPublisherDelegate: 设置推流回调监听
– setPlayerDelegate: 设置拉流回调监听
– setRoomConfig:userStateUpdate: 设置房间配置信息
– loginRoom:role:withCompletionBlock: 登录房间,推拉流之前必须登录房间
– setPreviewViewMode: 设置预览模式
– setPreviewView: 设置本地预览视频的视图
– startPreview 启动本地预览
– startPublishing:title:flag: 开始推流
– startPlayingStream:inView: 开始拉流
– setViewMode:ofStream: 设置用于拉流的View的播放模式
– stopPublishing 停止推流
– stopPlayingStream: 停止拉流
- logoutRoom 退出房间

7 相关文档

8 FAQ

Q1: 使用 ZEGO SDK 实现连麦,最大支持多少路连麦?

  答:由于设备性能的考虑,ZEGO SDK 在互推互拉的场景进行了限制,同个设备最大支持 12 路连麦。开发者如果希望支持更多路连麦,可联系 ZEGO 技术支持获取指定路数的SDK。

Q2: 如何实现主播邀请观众连麦、观众申请连麦?

  答:建议使用开发者自有的 IM 或第三方的 IM 来实现信令交互,也可以使用 ZEGO 的 – sendCustomCommand:content:completion: 接口来实现。可参考 ZEGO 的 连麦 接口来做交互,此套连麦接口只是交互示例,消息收发不一定可靠,若开发者使用 ZEGO 的 IM 接口,须在业务上实现消息可靠性的逻辑。

Q3:A 观众和主播连麦成功后,B 观众也和主播连麦了,A 如何知道 B 的流?

  答:成功登录房间之后房间内的有新增的流时,SDK 都会通过ZegoRoomDelegate– onStreamUpdated:streams:roomID: 接口来通知,当 type == ZEGO_STREAM_ADD 时是新增流相关的信息,开发者可根据业务需求,拉新增的流并做对应 UI 相关的操作。

Q4:观众或者连麦者如何知道主播是否停止直播了?

  答:当所登录房间中有流减少时,SDK 都会通过 ZegoRoomDelegate– onStreamUpdated:streams:roomID: 接口来通知,当 type == ZEGO_STREAM_DELETE 时是删除流相关的信息,可以通过 ZegoStream 类型的参数获取到 userID,判断是否是主播;如果此条删除流是正在拉的流,需要停止拉流,并根据业务需求,做对应的 UI 相关的操作。

Q5: 使用 ZEGO SDK 实现连麦,连麦者多的时候设备发热严重,画面卡顿怎么办?

  答:此为正常现象,当所拉的流数增多,需要更高的带宽更高的性能来处理画面的渲染,当设备性能和带宽不够时,会发生画面卡顿,设备发热严重的现象,建议当开发者在连麦人数增多时适当降低推流分辨率和码率。