快速入门

本文为您介绍如何使用 NERTC SDK 快速实现互动直播功能。

前提条件

在开始运行工程之前,需要准备好如下材料:

集成SDK

CocoaPods集成

集成前,请先前往SDK下载页面查看当前最新版本,并查询本地仓库中对应的版本是否为最新版本。若不是最新版本,建议先更新本地仓库,以确保可以集成最新的SDK版本。

    pod search NERtcSDK      //本地仓库中查询 NERtcSDK 信息
    pod repo update          //更新本地仓库
    pod install              //执行安装

至此, NERTC SDK 已经导入完成。

手动导入SDK集成

至此, NERtcSDK 已经导入完成。

实现互动直播

初始化

#import <NERtcSDK/NERtcSDK.h>
@interface Myapp ()<NERtcEngineDelegateEx>
...
NERtcEngine *coreEngine = [NERtcEngine sharedEngine];
NERtcEngineContext *context = [[NERtcEngineContext alloc] init];
context.engineDelegate = self;
context.appKey = AppKey;
[coreEngine setupEngineWithContext:context];
...

初始化的参数类 NERtcEngineContext 属性说明:

@interface NERtcEngineContext : NSObject

/**
 已开通音视频功能的云信应用的AppKey。
 */
@property (nonatomic, copy) NSString *appKey;

/**
 log的相关设置,由开发者提供。可选参数。
 */
@property (nonatomic, strong) NERtcLogSetting *logSetting;

/**
 通话相关信息的回调接口,由开发者提供。
 */
@property (nonatomic, weak) id<NERtcEngineDelegateEx> engineDelegate;

@end

设置本地视图

启动视频流

@protocol INERtcEngine <NSObject>
/**
 开启自己的视频。可以在加入频道前和频道后调用。

 @param enabled 是否开启
 @return 操作返回值,成功则返回 0
*/
- (int)enableLocalVideo:(BOOL)enabled;
@end
[NERtcEngine.sharedEngine enableLocalVideo:YES];

设置本地视频画布

启动视频流后,可以设置本地视频画布,用来显示本地采集的视频画面。

@protocol INERtcEngine <NSObject>
/**
 设置本地视频画布

 @param canvas 视频窗口,如果需要删除则传 nil
 @return 操作返回值,成功则返回 0
 */
- (int)setupLocalVideoCanvas:(NERtcVideoCanvas *)canvas;
@end
NERtcVideoCanvas *canvas = [weakSelf setupLocalCanvas];
[NERtcEngine.sharedEngine setupLocalVideoCanvas:canvas];

设置成功后,即可显示本地视频画面。

设置直播模式

在互动直播的场景中,我们建议在加入频道前,设置频道模式为直播模式。当前默认为通信模式。

@protocol INERtcEngine <NSObject>
/**
 设置频道场景 只能在加入频道之前调用。 NERtcEngine 会根据不同的使用场景采用不同的优化策略,通信场景偏好流畅,直播场景偏好画质。

 @param channelProfile 频道场景
 @return 操作返回值,被执行了则返回 0
 */
- (int)setChannelProfile:(NERtcChannelProfileType)channelProfile;
@end

NERtcChannelProfileType 枚举:

枚举值 说明
kNERtcChannelProfileCommunication 通信模式
kNERtcChannelProfileLiveBroadcasting 直播模式

设置推流开关

在加入频道前,还需要打开推流开关,这样拉流的观众可以拉取到对应的音视频流。

@protocol INERtcEngine <NSObject>
/**
 复杂参数设置接口 参数 key,请参阅 NERtcEngineBase.h 中的定义。

 @return 操作返回值,被执行了则返回 0
 */
- (int)setParameters:(NSDictionary *)parameters;
@end

NERtcEngineBase.h中,将下面的 key 设置为 @(1)

// BOOL -- 是否允许在房间推流时推送自身的音视频流,默认值 NO 
extern NSString * const kNERtcKeyPublishSelfStreamEnabled;

加入频道

加入频道前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,可以使用信令

@protocol INERtcEngine <NSObject>
/**
 加入频道

 @param token 频道token
 @param channelName 频道名
 @param uId 当前用户的Id
 @param completion 操作完成的 block 回调
 @return 操作返回值,被执行了则返回 0
 */
- (int)joinChannelWithToken:(NSString *)token
                channelName:(nullable NSString *)channelName
                      myUid:(uint64_t)uId
                 completion:(NERtcJoinChannelCompletion)completion;
@end
//加入房间
- (void)joinChannelWithRoomId:(NSString *)roomId
                       userId:(uint64_t)userId {
    __weak typeof(self) weakSelf = self;
    [NERtcEngine.sharedEngine joinChannelWithToken:@""
                                       channelName:roomId
                                             myUid:userId
                                        completion:^(NSError * _Nullable error, uint64_t channelId, uint64_t elapesd) {
        if (error) {
            //加入失败了,弹框之后退出当前页面
        } else {
            //加入成功
        }
    }];
}

推流任务

在成功加入房间后,需要设置推流任务。典型的业务场景里是由主播进行设置。注意:此处的主播业务层面的角色,NERTC SDK体系内不区分主播与连麦者。

推流任务也可通过 服务端API 进行管理,请根据您的业务需求选择对应的方式。

NERTC中推流任务相关的类如下:

描述
NERtcLiveStreamTaskInfo 推流任务
NERtcLiveStreamLayout 视频布局
NERtcLiveStreamUserTranscoding 直播成员布局
NERtcLiveStreamImageInfo 背景图信息

增加推流任务

@protocol INERtcEngineEx <INERtcEngine>
/**
 添加房间推流任务,成功添加后当前用户可以收到该直播流的状态通知。通话中有效。

 @param taskInfo 直播任务信息
 @param completion 任务结果回调
 @return 操作返回值,成功则返回 0
*/
- (int)addLiveStreamTask:(NERtcLiveStreamTaskInfo *)taskInfo 
              compeltion:(NERtcLiveStreamCompletion)completion;
@end
    //先初始化推流任务
    NERtcLiveStreamTaskInfo *info = [[NERtcLiveStreamTaskInfo alloc] init];
    info.taskID = taskID;    //taskID 可选字母、数字,下划线,不超过64位
    info.streamURL = pushUrl;   // 一个推流地址对应一个推流任务
    info.serverRecordEnabled = NO;  // 不进行直播录制,请注意与音视频服务端录制区分。
    // 设置推音视频流还是纯音频流
    info.lsMode = enableVideo ? kNERtcLsModeVideo : kNERtcLsModeAudio;

    //设置整体布局
    NERtcLiveStreamLayout *layout = [[NERtcLiveStreamLayout alloc] init];
    layout.width = 720;    //整体布局宽度
    layout.height = 1280;  //整体布局高度
    layout.backgroundColor = (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
    info.layout = layout;

    // 设置直播成员布局
    NERtcLiveStreamUserTranscoding *user1 = [[NERtcLiveStreamUserTranscoding alloc] init];
    user1.uid = uid1;
    user1.audioPush = true;        // 推流是否发布user1的音频
    user1.videoPush = enableVideo; // 推流是否发布user1的视频
    if (user1.videoPush) {
        // 如果发布视频,需要设置一下视频布局参数
        user1.x = 10; // user1 的视频布局x偏移,相对整体布局的左上角
        user1.y = 10; // user1 的视频布局y偏移,相对整体布局的左上角
        user1.width = 180; // user1 的视频布局宽度
        user1.height = 320; //user1 的视频布局高度
        user1.adaption = kNERtcLsModeVideoScaleCropFill;
        }

    // 设置第n位直播成员布局
    NERtcLiveStreamUserTranscoding *usern = [[NERtcLiveStreamUserTranscoding alloc]init];
    usern.uid = uidn;
    usern.audioPush = true;
    usern.videoPush = enableVideo;
    if (usern.videoPush) {
       usern.adaption = kNERtcLsModeVideoScaleCropFill;
       usern.x = user1.x + user1.width + 10;
       usern.y = user1.y + user1.height + 10;
       usern.width = 320;
       usern.height = 640;
    }

    layout.users = @[user1,...,usern];


    //配置背景占位图片,可以不配置。
    NERtcLiveStreamImageInfo *imageInfo = [[NERtcLiveStreamImageInfo alloc] init];
    imageInfo.url = url;      
    imageInfo.x = 0;
    imageInfo.y = 0;
    imageInfo.width = 720;
    imageInfo.height = 1280;
    layout.bgImage = imageInfo;

     //调用 addLiveStreamTask 接口添加推流任务
    int ret = [[NERtcEngine sharedEngine] addLiveStreamTask:info
                                                 compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {

    NSString *toast = !errorCode ? @"添加成功" : [NSString stringWithFormat:@"添加失败 errorcode = %d",errorCode];
    MakeToast(toast);

    }];

    if (ret != 0) {
        MakeToast(@"调用添加推流任务失败");
    }

更新推流任务

@protocol INERtcEngineEx <INERtcEngine>
/**
 更新修改房间推流任务。通话中有效。

 @param taskInfo 直播任务信息
 @param completion 任务结果回调
 @return 操作返回值,成功则返回 0
*/
- (int)updateLiveStreamTask:(NERtcLiveStreamTaskInfo *)taskInfo 
                 compeltion:(NERtcLiveStreamCompletion)completion;
@end
// 初始化新推流任务。注意:其中的taskid保持不变。
NERtcLiveStreamTaskInfo *updateLiveTask1 = preLiveTask;
...

// 更新推流任务
int ret = [[NERtcEngine sharedEngine] updateLiveStreamTask:updateLiveTask1
                                                compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {

    NSString *toast = !errorCode ? @"更新成功" : [NSString stringWithFormat:@"errorcode = %d",errorCode];
    MakeToast(toast);
}];

if (ret != 0) {
    MakeToast(@"更新推流任务失败");
}

删除推流任务

当不再需要推流时,可以选择删掉之前配置的推流任务。

注意:推流任务不会随着成员的离开而自动结束,成员离开只是结束了自己的推流,只有在手动删除或者频道所有成员都离开时才会结束。

@protocol INERtcEngineEx <INERtcEngine>
/**
 删除房间推流任务。通话中有效。

 @param taskId 直播任务Id  
 @param completion 任务结果回调
 @return 操作返回值,成功则返回 0
*/
- (int)removeLiveStreamTask:(NSString *)taskId 
                 compeltion:(NERtcLiveStreamCompletion)completion;
@end
NSString *taskID = preLiveTask.taskID;

int ret = [[NERtcEngine sharedEngine] removeLiveStreamTask:taskID
                                                compeltion:^(NSString * _Nonnull taskId, kNERtcLiveStreamError errorCode) {
    NSString *toast = !errorCode ? @"删除成功" : [NSString stringWithFormat:@"errorcode = %d",errorCode];
    MakeToast(toast);
}];

if (ret != 0) {
    MakeToast(@"删除推流任务失败");
}

kNERtcLiveStreamError枚举

在增加、更新与删除推流任务时当发生错误时,会回调出 kNERtcLiveStreamError,其枚举如下:

错误码 含义
kNERtcLiveStreamErrorRequestIsInvaild = 1301 task请求无效,被后续操作覆盖
kNERtcLiveStreamErrorIsInvaild = 1400 task参数格式错误
kNERtcLiveStreamErrorRoomExited = 1401 已经退出频道
kNERtcLiveStreamErrorNumLimit = 1402 推流任务超出上限
kNERtcLiveStreamErrorDuplicateId = 1403 推流ID重复
kNERtcLiveStreamErrorNotFound = 1404 taskId任务不存在,或频道不存在
kNERtcLiveStreamErrorRequestErr = 1417 请求失败
kNERtcLiveStreamErrorInternalServerErr = 1500 服务器内部错误
kNERtcErrLsTaskInvalidLayout = 1501 布局参数错误
kNERtcErrLsTaskUserPicErr = 1502 用户图片错误

推流状态回调

主播/连麦者参与互动直播的过程中,可以通过监听onNERTCEngineLiveStreamState来接收与推流状态相关的信息:

@protocol NERtcEngineDelegate <NSObject>
/**
 直播推流状态回调

 @param state 直播推流状态
 @param taskID taskID
 @param url 直播推流url
*/
- (void)onNERTCEngineLiveStreamState:(NERtcLiveStreamStateCode)state 
                              taskID:(NSString *)taskID 
                                 url:(NSString *)url;

其中,关于NERtcLiveStreamStateCode

状态码 含义
kNERtcLsStatePushing = 505 推流中
kNERtcLsStatePushFail = 506 互动直播推流失败
kNERtcLsStatePushStopped = 511 推流结束

API参考

方法 功能描述
setParameters 复杂参数设置接口
addLiveStreamTask 添加房间推流任务
updateLiveStreamTask 更新修改房间推流任务
removeLiveStreamTask 删除房间推流任务
事件 事件描述
onNERTCEngineLiveStreamState 直播推流状态回调

设置远端视图

视频通话过程中,除了要显示本地的视频画面,通常也要显示参与通话的其他用户的远端视频画面。

监听远端用户进出频道

@protocol NERtcEngineDelegate <NSObject>
/**
 其他用户加入频道的回调

 @param userID 用户的id
 @param userName 用户名称
 */
- (void)onNERtcEngineUserDidJoinWithUserID:(uint64_t)userID 
                                  userName:(NSString *)userName;

/**
 其他用户离开频道的回调

 @param userID 用户的id
 @param reason 离开的原因
 */
- (void)onNERtcEngineUserDidLeaveWithUserID:(uint64_t)userID 
                                        reason:(NERtcSessionLeaveReason)reason;
@end

设置远端视频画布

监听到远端用户加入频道后,可以设置远端视频画布,用来显示远端用户的视频画面。

@protocol INERtcEngine <NSObject>

/**
 设置远端的视频播放窗口
 只能在加入频道后调用

 @param userID 用户的id
 @param canvas 视频窗口,如果需要删除则传nil
 @return 操作返回值,成功则返回 0
 */
- (int)setupRemoteVideoCanvas:(NERtcVideoCanvas *)canvas forUserID:(uint64_t)userID;
@end
- (void)onNERtcEngineUserDidJoinWithUserID:(uint64_t)userID
                                  userName:(NSString *)userName {

    //如果已经setup了一个远端的canvas,则不需要再建立了
    ...
    if (_remoteCanvas != nil) {
        return;
    }

    //建立远端canvas,用来渲染远端画面
    NERtcVideoCanvas *canvas = [self setupRemoteCanvasWithUid:userID];
    [NERtcEngine.sharedEngine setupRemoteVideoCanvas:canvas
                                           forUserID:userID];
    ...
}

···

- (void)onNERtcEngineUserDidLeaveWithUserID:(uint64_t)userID
                                     reason:(NERtcSessionLeaveReason)reason {

    //如果远端的人离开了,重置远端模型和UI
    ...
    [NERtcEngine.sharedEngine setupRemoteVideoCanvas:nil
                                           forUserID:userID];
    ...
}

监听远端视频流发布

当频道中的其他用户有视频流发出/关闭时,分别会走入以下回调:

@protocol NERtcEngineDelegate <NSObject>

/**
 其他用户打开视频的回调

 @param userID 用户id
 @param profile 用户发送视频的最大分辨率类型
 */
- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID videoProfile:(NERtcVideoProfileType)profile;

/**
 其他用户关闭视频的回调

 @param userID 用户id
 */
- (void)onNERtcEngineUserVideoDidStop:(uint64_t)userID;
@end

订阅远端视频流

在设置完远端视频画布后,且监听到远端用户有视频发布时,可以订阅远端用户的视频流。

@protocol INERtcEngineEx <INERtcEngine>
/**
 订阅或取消订阅别人的视频,订阅了才会接收别人的视频数据。

 @param subscribe 是否订阅
 @param userID 用户的id
 @param streamType 订阅的远端视频流类型
 @return 操作返回值,成功则返回 0
 */
- (int)subscribeRemoteVideo:(BOOL)subscribe 
                  forUserID:(uint64_t)userID 
                 streamType:(NERtcRemoteVideoStreamType)streamType;
@end
// 监听到远端用户有视频流发布
- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID
                                    videoProfile:(NERtcVideoProfileType)profile {
    //如果已经订阅过远端视频流,则不需要再订阅了
    ...
    if (_remoteCanvas.subscribedVideo) {
        return;
    }

    //订阅远端视频流。
    _remoteCanvas.subscribedVideo = YES;
    [NERtcEngine.sharedEngine subscribeRemoteVideo:YES
                                 forUserID:userID
                                streamType:kNERtcRemoteVideoStreamTypeHigh];
    ...
}

// 监听到远端用户停止视频流发布
- (void)onNERtcEngineUserVideoDidStop:(uint64_t)userID {
    if (userID == _remoteCanvas.uid) {
        // 收到此回调后,SDK 内部会取消对应的视频流订阅,无需开发者主动取消订阅。
        ...
        _remoteStatLab.hidden = YES;
        ...
    }
}

订阅成功后,即可显示远端的视频画面。

音频流

在 NERtcSDK 中,本地音频的采集发布和远端音频订阅播放是默认启动的,正常情况下无需开发者主动干预。

离开频道

当通话结束,需要离开频道,可以调用以下接口:

@protocol INERtcEngine <NSObject>

/**
 离开频道

 @return 操作返回值,成功则返回 0
 */
- (int)leaveChannel;
@end
//UI 挂断按钮事件
- (IBAction)onHungupAction:(UIButton *)sender {
    [NERtcEngine.sharedEngine leaveChannel];
    [self dismiss];
}

执行完 leaveChannel 方法后,SDK 会触发 离开频道回调通知开发者:

@protocol NERtcEngineDelegate <NSObject>
/**
 离开频道回调

 @param result 离开的结果
 */
- (void)onNERtcEngineDidLeaveChannelWithResult:(NERtcError)result;
@end
- (void)onNERtcEngineDidLeaveChannelWithResult:(NERtcError)result{
    // 进行业务数据清理
}

销毁音视频实例

释放当前的 NERtcEngine 实例,建议在 App 确定不再需要使用 NERtcEngine 实例时,通过该接口释放 NERtcEngine 实例的对象资源。

@interface NERtcEngine : NSObject <INERtcEngineEx>

/**
 释放当前的 NERtcEngine 实例
 建议在 App 确定不再需要使用 NERtcEngine 实例时,通过该接口释放 NERtcEngine 实例的对象资源;

 1. 该接口的工作方式为同步调用方式,必须在子线程中才能调用,否则会调用失败;如:
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     [NERtcEngine destroyEngine];
 });
 2. 该接口不得在 SDK 的回调中调用,在接口返回前也不允许调用 SDK 的其他任何接口;
 3. 接口调用返回之后,如果需要再次使用 SDK,可以重新调用 sharedEngine 来获取一个新的 NERtcEngine 实例。

 */

+ (int)destroyEngine;
@end
- (void)dealloc {
    [NERtcEngine destroyEngine]; //销毁实例
}