快速实现直播连麦

更新时间: 2024/07/24 13:38:57

本文介绍在单人直播的过程中,观众连麦场景下,NERTC 的实现流程。直播中连麦成功后,观众在房间里与主播进行实时音视频通话,增加直播互动。

功能原理

连麦的架构原理如下图所示。

连麦.png

连麦的业务流程说明如下:

  1. 观众申请上麦,主播同意上麦申请后,观众加入 RTC 房间。
  2. 互动直播服务器将主播和观众的音视频进行混屏转码后,推到 CDN 分发。
  3. 观众端使用 RTMP/HLS/FLV 协议进行拉流观看。

注意事项

只支持网易云信播放器 NELivePlayer 进行拉流,其他播放器暂不兼容。

前提条件

实现在直播的过程中观众连麦前,请确保您已经实现了 单人直播

实现直播中连麦

下文介绍在单人直播的过程中,观众连麦场景下,NERTC 的实现流程。

API 时序图

sequenceDiagram
    title: 实现直播中连麦的 API 时序图
    actor 主播 A
    participant 业务服务器
    participant NERtcSDK as 网易云信 RTC SDK
    actor 连麦者

    Note over 主播 A, 连麦者:开始连麦

    连麦者->>业务服务器: 申请上麦
    Note right of 主播 A: 请自行实现相关业务逻辑
    业务服务器->>主播 A: 申请上麦

    主播 A-->>业务服务器: 同意上麦
    业务服务器-->>连麦者: 主播同意上麦

    连麦者 ->> NERtcSDK: joinChannel

    rect rgb(191, 223, 255)
    主播 A ->> NERtcSDK: addLiveStreamTask 开始旁路推流
    NERtcSDK -->> 主播 A: onNERTCEngineLiveStreamState

    主播 A ->> NERtcSDK: stopPushStreaming   旁路推流成功后,停止推流

    Note over 主播 A, 连麦者:连麦状态下新增连麦
    主播 A ->> NERtcSDK: updateLiveStreamTask 更新旁路推流

    Note over 主播 A, 连麦者:结束连麦
    连麦者 ->> 业务服务器: 下麦
    业务服务器 ->> 主播 A: 下麦

    主播 A ->> NERtcSDK: startPushStreaming 重新开始推流
    NERtcSDK -->> 主播 A: onNERtcEngineStartPushStreamingWithResult:channelId:

    主播 A ->> NERtcSDK: removeLiveStreamTask 移除推流任务
    end

    连麦者 ->> NERtcSDK: leaveChannel

开始连麦

  1. 开启旁路推流任务。

    主播 A 调用 addLiveStreamTask 方法添加旁路推流任务,将主播房间和连麦者的音视频流推送到 CDN 上进行合流。

    Objective-C//设置旁路推流参数
    NERtcLiveStreamTaskInfo *info = [[NERtcLiveStreamTaskInfo alloc] init];
    NERtcLiveConfig *config = [[NERtcLiveConfig alloc] init];
    
    info.streamURL = self.pushStreamingUrl;
    info.taskID = PKTaskId;
    info.lsMode = kNERtcLsModeVideo;
    
    config.channels = 2;
    config.sampleRate = kNERtcLiveStreamAudioSampleRate44100;
    config.audioBitrate = 128;
    config.audioCodecProfile = kNERtcLiveStreamAudioCodecProfileLCAAC;
    
    //如果需要服务器录制
    info.serverRecordEnabled = YES;
    info.config = config;
    
    //设置整体布局
    NERtcLiveStreamLayout *layout = [[NERtcLiveStreamLayout alloc] init];
    layout.width = 720;//整体布局宽度
    layout.height = 1280;//整体布局高度
    info.layout = layout;
    
    //自己
    NERtcLiveStreamUserTranscoding *user1 = [[NERtcLiveStreamUserTranscoding alloc]init];
    user1.uid = myUid;
    user1.audioPush = YES; // 推流是否发布 user1 的音频
    user1.videoPush = YES; // 推流是否发布 user1 的视频
    user1.x = 0; // user1 的视频布局 x 偏移,相对整体布局的左上角
    user1.y = 320; // user1 的视频布局 y 偏移,相对整体布局的左上角
    user1.width = 360; // user1 的视频布局宽度
    user1.height = 640; //user1 的视频布局高度
    user1.adaption = kNERtcLsModeVideoScaleCropFill;
    
    //如果此时知道对方 Uid 可以在这里预先填上,如果不知道,可以先不设置 User2,然后参考第 6 步 update
    NERtcLiveStreamUserTranscoding *user2 = [[NERtcLiveStreamUserTranscoding alloc]init];
    user2.uid = pkUid;
    user2.audioPush = YES; // 推流是否发布 user2 的音频
    user2.videoPush = YES; // 推流是否发布 user2 的视频
    user2.x = 360; // user2 的视频布局 x 偏移,相对整体布局的左上角
    user2.y = 320; // user2 的视频布局 y 偏移,相对整体布局的左上角
    user2.width = 360; // user2 的视频布局宽度
    user2.height = 640; //user2 的视频布局高度
    user2.adaption = kNERtcLsModeVideoScaleCropFill;
    
    NSMutableArray* layoutUsers = [NSMutableArray array];
    [layoutUsers addObject:user1];
    [layoutUsers addObject:user2];
    info.layout.users = layoutUsers;
    
    //结束之前的推流,推荐调用,防止异常
    [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
    }];
    //开启旁路推流
    [[NERtcEngine sharedEngine] addLiveStreamTask:info compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        if (0 != errorCode) {
            //表示开启开启连麦失败,此时仍然处于单人直播状态,此时业务可以根据自身定制。如果想要继续连麦需要重新按照实现连麦直播逻辑继续开始连麦。
        }
    }]
    
  2. 等待旁路推流结果。

    通过 onNERTCEngineLiveStreamState 回调监听旁路推流状态,如果状态为 kNERtcLsStatePushing,表示旁路推流成功。如果旁路推流失败,则调用 removeLiveStreamTask 方法移除推流任务。

    Objective-C    //旁路直播状态回调
    - (void)onNERTCEngineLiveStreamState:(NERtcLiveStreamStateCode)state
                                taskID:(NSString *)taskID
                                    url:(NSString *)url {
        //表示 连麦 成功
        if (state == kNERtcLsStatePushing) {
        //停止单人直播
        [NERtcEngine sharedEngine] stopPushStreaming];
        }else{
            //此时需要按照以下操作回退到单推
            [[NERtcEngine sharedEngine] stopChannelMediaRelay];
            [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
            }];
    
            [[NERtcEngine sharedEngine] startPushStreaming:config];
        }
    }
    
  3. 旁路推流成功后,停止单人直播推流。

    主播 A 调用 stopPushStreaming 方法停止单人直播推流。

    Objective-C
    //在第 3 步骤收到 onNERTCEngineLiveStreamState 回调 kNERtcLsStatePushing 后
    //调用 stopPushStreaming 停止单人直播
    [NERtcEngine sharedEngine] stopPushStreaming];
    

结束连麦

  1. 开始单人直播推流。

    主播 A 调用 startPushStreaming 方法开始单人直播推流。

    Objective-C//设置推流参数,此时 streamingRoomInfo 可以不传
    NERtcPushStreamingConfig *pushConfig = [[NERtcPushStreamingConfig alloc] init];
    pushConfig.streamingUrl = streamingUrl;
    
    //开启推流
    [[NERtcEngine sharedEngine] startPushStreaming:config];
    
  2. 等待单人直播推流结果。

    主播 A 通过 onNERtcEngineStartPushStreamingWithResult:channelId: 回调监听单人直播推流状态,如果推流成功,则执行下一步操作。

    Objective-C- (void)onNERtcEngineStartPushStreamingWithResult:(NERtcError)result channelId:(uint64_t)channelId {
        if (kNERtcNoError != result) {
            //开始 cdn 推流失败,此时仍然处于 PK 直播中,可以根据业务情况进行相关处理,如需要继续结束需要参照第一步,再次调用开启推流
            return;
        }
        //停止旁路推流
        [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        }];
    }
    
  3. 移除旁路推流任务。

    主播 A 和 主播 B 分别调用 removeLiveStreamTask 方法移除旁路推流任务。

    根据第 2 步, 在 onNERtcEngineStartPushStreamingWithResult 回调方法停止旁路推流

示例代码

Objective-C//开始接受连麦
- (int)enterSeat {
    //step1:添加旁路推流任务,旁路任务中的音频参数保持跟 cdn 下相同(采样率、声道数等等)
    [[NERtcEngine sharedEngine] addLiveStreamTask:task compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
    }];
    //step2 等待旁路推流结果(onNERTCEngineLiveStreamState:taskID:url:),如果 state 为 kNERtcLsStatePushing 表示添加旁路任务成功,则停止 cdn 推流(stopPushStreaming)
    [[NERtcEngine sharedEngine] stopPushStreaming];
}

//结束接受连麦
- (int)leaveSeat {
    //step1 开始 cdn 推流
    [[NERtcEngine sharedEngine] startPushStreaming:config];
    //step2 等待 cdn 推流结果回调(onNERtcEngineStartPushStreamingWithResult:channelId:),成功后移除旁路推流任务(removeLiveStreamTask:compeltion:)
    [[NERtcEngine sharedEngine] removeLiveStreamTask:task compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
    }];
}

注意事项

removeLiveStreamTask 的调用

  1. 当 PK 业务直接退出房间时候,需要调用 removeLiveStreamTask 停止旁路推流,防止流没有停止,观众还能拉到流,出现业务异常,可参考以下场景。

    Objective-C//主动 leaveChannel,退出房间 (不是切为单人直播)
    - (void)leavePK{
        [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        }];
        [[NERtcEngine sharedEngine] leavechannel];
    }
    
    //SDK disconnect 断开
    - (void)onNERtcEngineDidDisconnectWithReason:(NERtcError)reason{
        [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        }];
    }
    
  2. 推荐每次开始 PK addLiveStreamTask 前,可以先调用 removeLiveStreamTask 停止之前的旁路推流任务,防止一些异常情况,出现 addLiveStreamTask 失败,导致业务异常,流程见开始 PK 第 2 步.

    Objective-C    [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        }];
        [[NERtcEngine sharedEngine] addLiveStreamTask:[self mockLiveStreamInfoForPk] compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
            if (0 != errorCode) {
                //表示开启开启 PK 失败,此时仍然处于单人直播状态,此时业务可以根据自身定制。如果想要继续 PK 需要重新按照实现 PK 直播逻辑继续开始 PK。
            }
        }
    

异常情况的回调处理

旁路推流回调失败处理,回退至单推处理逻辑:

Objective-C//开启旁路推流失败回调
 [[NERtcEngine sharedEngine] addLiveStreamTask:info compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        if (0 != errorCode) {
             //停止 MediaRelay
            [[NERtcEngine sharedEngine] stopChannelMediaRelay];
            //停止旁路推流
            [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
            }];
        }
   }]

//旁路推流状态回调
- (void)onNERTCEngineLiveStreamState:(NERtcLiveStreamStateCode)state
                              taskID:(NSString *)taskID
                                 url:(NSString *)url {
    //表示 PK 成功
    if (state == kNERtcLsStatePushing) {
       //参考开始 PK 第 3 步停止单人直播
    } else if (state == kNERtcLsStatePushFail){
        //此时需要按照以下操作回退到单推
        [[NERtcEngine sharedEngine] stopChannelMediaRelay];
        [[NERtcEngine sharedEngine] removeLiveStreamTask:PKTaskId compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
        }];

        [[NERtcEngine sharedEngine] startPushStreaming:config];
    }
}

以上回退时业务需要告知连麦对方,主播已经回退,连麦结束。

主要回调

请在初始化时注册推流相关的回调,连麦场景需要关注的主要回调:

Objective-C//开始推流 startPushStreaming 结果回调
- (void)onNERtcEngineStartPushStreamingWithResult:(NERtcError)result channelId:(uint64_t)channelId{
if (kNERtcNoError != result) {
    //kNERtcErrInvalidState 不需要关注
    if(kNERtcErrInvalidState != result){
        return;
    }
        //推流失败,业务按需处理,参考结束连麦的第 2 步。
    }else{
        //推流成功,业务按需处理,参考结束连麦的第 2 步。
    }
}

//停止推流 stopPushStreaming 结果回调
-(void)onNERtcEngineStopPushStreaming:(NERtcError)result{
    //可以不关注
}

//SDK 客户端和服务器断开连接
- (void)onNERtcEngineReconnectingStart{
    //此时表示客户端 SDK 无法连上服务器,正在重连,业务按需处理,可以提示主播
}

//SDK 客户端重连状态结果回调
- (void)onNERtcEngineRejoinChannel:(NERtcError)result{
    //此时表示重连成功,SDK 连上了服务器
}

//推流过程重连失败,最终断开回调
- (void)onNERtcEngineDidDisconnectWithReason:(NERtcError)reason{
    //此时表示 SDK 重连失败了,业务按需处理,如果需要继续推流,需要再次调用 startPushStreaming 接口
}

//旁路直播状态回调
- (void)onNERTCEngineLiveStreamState:(NERtcLiveStreamStateCode)state
                            taskID:(NSString *)taskID
                                url:(NSString *)url {
    //表示 连麦 成功
    if (state == kNERtcLsStatePushing) {
    //参考开始连麦第 2 步停止单人直播
    }else{
    //参考开始连麦第 2 步操作回退到单推
    }
}
此文档是否对你有帮助?
有帮助
去反馈
  • 功能原理
  • 注意事项
  • 前提条件
  • 实现直播中连麦
  • API 时序图
  • 开始连麦
  • 结束连麦
  • 示例代码
  • 注意事项
  • 主要回调