iOS端

通过阅读本文,您可以了解iOS端阿里云播放器SDK集成Native RTS SDK实现超低延时直播的方法。

前提条件

环境中已安装CocoaPods。

SDK集成

使用pod方式添加阿里云播放器SDK和Native RTS SDK的依赖文件。

  1. 打开终端窗口。

  2. 进入项目所在路径,创建Podfile文件。

    pod init

  3. 编辑Podfile文件,添加最新版本的依赖。

     pod 'RtsSDK'
     pod 'AliPlayerSDK_iOS'
     pod 'AliPlayerSDK_iOS_ARTC'
    说明
    • AliPlayerSDK_iOS_ARTCRtsSDK对应的依赖文件添加后,阿里云播放器SDK会自动将RTS SDK加载为插件。

    • 如果您需要添加指定版本的依赖,请自行补充版本号(例如:pod 'RtsSDK','2.1.0')。获取最新的版本,阿里云播放器SDK请参见iOS播放器SDK超低延时直播SDK请参见SDK下载

    • 集成阿里云播放器SDK和Native RTS SDK时需要满足对应的配套关系,详情请参见发布日志

  4. 安装SDK。执行之后,会生成*.xcworkspace文件,表示SDK集成完毕。

    pod install

阿里云播放器SDK接口使用

调用阿里云播放器SDK接口实现超低延时直播功能。更多阿里云播放器SDK功能,请参见进阶功能API说明

说明
  • 以下为示例代码,详情代码请参见开源项目MONE_demo_opensource_iOS工程中的AUILiveRtsPlay组件下的AUILiveRtsPlayPullViewController类。

  • 基于阿里云播放器实现RTS拉流时,不能调用pause暂停直播流。您可以先调用stop停止播放,再调用prapare重新播放。

  • 不支持seek(拖动)。

  • 初始化Aliplayer

    - (AliPlayer *)aliPlayer{
        if (!_aliPlayer) {
            _aliPlayer = [[AliPlayer alloc] init];
            _aliPlayer.scalingMode =  AVP_SCALINGMODE_SCALEASPECTFIT;
            _aliPlayer.rate = 1;
        //如需实现AVPDelegate代理,添加此行
            _aliPlayer.delegate = self;
        //设置播放的视图,将您的播放视图赋值给aliplayer
            _aliPlayer.playerView = self.basePlayerView.playerView;
            _aliPlayer.autoPlay = YES;
        }
        return _aliPlayer;
    }
  • 设置播放URL

    AVPUrlSource *source = [[AVPUrlSource alloc] urlWithString:_url];
    [self.aliPlayer setUrlSource:source];
  • 播放参数配置

    配置播放参数,提升超低延时直播效果。播放器参数配置需要在调用prepare之前才能生效。

    AVPConfig *config = self.aliPlayer.getConfig;
    //直播最大延时
    [config setMaxDelayTime:1000];
    //卡顿恢复时长
    [config setHighBufferDuration:10];
    //起播最大延时
    [config setStartBufferDuration:10];
    [_aliPlayer setConfig:config];
    //默认为硬解,如播放器在准备过程中发现硬解失败,会自动切换到软解
     _aliPlayer.enableHardwareDecoder = YES;
  • 日志开关

    // 开启日志
    [AliPlayer setEnableLog:YES];
    [AliPlayer setLogCallbackInfo:LOG_LEVEL_TRACE callbackBlock:nil];
    
    // 关闭日志
    [AliPlayer setEnableLog:NO];
    [AliPlayer setLogCallbackInfo:LOG_LEVEL_NONE callbackBlock:nil];
    说明

    若URL降级RTMP协议,真机调试时,不建议打开日志。

  • 播放控制

    [self.aliPlayer prepare];
    [self.aliPlayer stop];
    [self.aliPlayer destroy];
    [self.aliPlayer reload];
  • 起播前注册回调

    -(void)createAliPlayer {
        // 1。创建播放器实例
        AliPlayer *aliPlayer = [[AliPlayer alloc] init];
        AVPConfig *config = [aliPlayer getConfig];
        config.maxDelayTime = 1000;
        config.highBufferDuration = 10;
        config.startBufferDuration = 10;
        // 2.注册delegate
        aliPlayer.delegate = self;
        [aliPlayer setConfig:config];
        aliPlayer.autoPlay = YES;
        NSString *url = "artc://xxxx";
        AVPUrlSource *source = [[AVPUrlSource alloc] urlWithString:self.url];
        [aliPlayer setUrlSource:source];
        [aliPlayer prepare];
    }
    
    // 3.监听播放器回调事件
     -(void)onPlayerEvent:(AliPlayer*)player eventWithString:(AVPEventWithString )eventWithString description:(NSString *)description {
    
     }
                        
  • 直播降级

    直播降级是将前缀为artc://的播放器URL源字符串直接修改为http-flv形式,然后更新播放器UrlSource,并开始播放的策略。

    self.playUrl = @"artc://xxxx";
    
    // 停止播放
    [self.aliplayer stop];
    
    // 获取当前的播放url,截取url的前缀
    NSArray *urlSeparated = [self.playUrl componentsSeparatedByString:@"://"];
    NSString *urlPrefix = urlSeparated.firstObject;
    // 判断url前缀是否是artc,如果是的话就降级为http-flv形式的传统直播
    if ([urlPrefix isEqualToString:@"artc"]) {
        self.playUrl = @"http://xxxx.flv"; // http-flv具体url格式参照直播地址生成时的http-flv样式
    
        // 重新设置播放源,进行准备播放
        AVPConfig *config = [self.player getConfig];
        config.maxDelayTime = 10000;
        config.highBufferDuration = 100;
        config.startBufferDuration = 100;
        [self.aliplayer setConfig:config];
        AVPUrlSource *source = [[AVPUrlSource alloc] urlWithString:self.playUrl];
        [self.aliplayer setUrlSource:source];
    
        // 开始播放
        [self.aliplayer prepare];
    }
                        

    启播或者直播过程中,播放器事件回调收到播放组件中透传输出的消息时,解析播放器事件说明JSON字符串,得到的code是RtsSDK中的message。

    收到E_DNS_FAIL、E_AUTH_FAIL、E_CONN_TIMEOUT、E_SUB_TIMEOUT、E_SUB_NO_STREAM时,需要直播降级;

    收到E_STREAM_BROKEN时,需要先重新播放一次,然后如果之后再次收到时就直播降级;

    收到E_RECV_STOP_SIGNAL时,直接停止播放,不需要直播降级。

    需要提前导入RtsSDK的API:

    #import <RtsSDK/rts_messages.h>

    然后处理播放器事件回调:

    self.retryStartPlay = YES;
    ......
    
    /**
     @brief 播放器事件回调
     @param player 播放器player指针
     @param eventWithString 播放器事件类型
     @param description 播放器事件说明
     @see AVPEventType
     */
    - (void)onPlayerEvent:(AliPlayer*)player eventWithString:(AVPEventWithString)eventWithString description:(NSString *)description {
        switch (eventWithString) {
            case EVENT_PLAYER_DIRECT_COMPONENT_MSG:
                {
                    NSDictionary *descriptionDic = [[description rts_toDictionary] copy];
                    NSString *contentStr = [descriptionDic objectForKey:@"content"];
                    NSDictionary *kv = [contentStr rts_paramsToDictionaryWithSeparator:@"="];
                    NSNumber *type = [kv objectForKey:@"code"];
                    switch (type.intValue) {
                    case E_DNS_FAIL: // DNS解析失败
                    case E_AUTH_FAIL: // 鉴权失败
                    case E_CONN_TIMEOUT: // 建联信令超时
                    case E_SUB_TIMEOUT:  // 订阅信令返回错误,或者超时
                    case E_SUB_NO_STREAM: // 订阅流不存在
                        {
                            // 直播降级处理逻辑
                        }
                            break;
                    case E_STREAM_BROKEN: // 媒体超时,没有收到音频包和视频包
                        {
                            // 第一次收到RTS媒体超时先重试播放一次,然后如果再次收到就直接降级播放
                            if (self.retryStartPlay) {
                                [self onStartPlay];
                                self.retryStartPlay = NO;
                            } else {
                                // 直播降级处理逻辑
                            }
                        }
                            break;
                    case E_RECV_STOP_SIGNAL:
                        {
                            // 停止播放
                           [self.aliplayer stop];
                        }
                            break;
                    default:
                        break;
                    }
                }
                    break;
                default:
                    break;
        }
    }
    ......
    
    #pragma mark -- 对NSString进行category(分类),抽出公共方法,解析json字符串
    - (NSDictionary *)rts_toDictionary {
        if (self == nil) {
            return nil;
        }
    
        NSData *jsonData = [self dataUsingEncoding:NSUTF8StringEncoding];
        NSError *err;
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
                                                            options:NSJSONReadingMutableContainers
                                                              error:&err];
        if(err) {
            NSLog(@"json解析失败:%@",err);
            return nil;
        }
        return dic;
    }
    
    - (NSDictionary *)rts_paramsToDictionaryWithSeparator:(NSString*)split {
        if (self == nil) {
            return nil;
        }
        NSMutableDictionary *multiDic = @{}.mutableCopy;
        NSString *content = [self stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        NSArray *arr = [content componentsSeparatedByString:@","];
        if (arr.count>0) {
            for (NSString *str in arr) {
                NSArray *kvArr = [str componentsSeparatedByString:split];
                if (kvArr.count==2) {
                    [multiDic setValue:kvArr[1] forKey:kvArr[0]];
                }
    
            }
        }
        return multiDic;
    }
  • 获取TraceId

    播放器事件回调收到播放组件中透传输出的消息时,解析播放器事件说明JSON字符串,得到的code是RtsSDK中的message,收到E_HELP_SUPPORT_ID_SUBSCRIBE时,解析字符串中"-sub-"后的字符串得到TraceId。

    - (void)onPlayerEvent:(AliPlayer*)player eventWithString:(AVPEventWithString)eventWithString description:(NSString *)description {
        switch (eventWithString) {
            case EVENT_PLAYER_DIRECT_COMPONENT_MSG:
                {
                    NSDictionary *descriptionDic = [[description rts_toDictionary] copy];
                    NSString *contentStr = [descriptionDic objectForKey:@"content"];
                    NSDictionary *kv = [contentStr rts_paramsToDictionaryWithSeparator:@"="];
                    NSNumber *type = [kv objectForKey:@"code"];
                    switch (type.intValue) {
                    case E_HELP_SUPPORT_ID_SUBSCRIBE: // 获取traceId
                        {
                            NSString *desc = [kv objectForKey:@"desc"];
                            if ([desc containsString:@"-sub-"]) {
                                NSString *traceId = [desc componentsSeparatedByString:@"-sub-"].lastObject;
                                self.traceId = traceId;
                            }
                        }
                        break;
                    default:
                        break;
                    }
                }
                    break;
                default:
                    break;
        }
    }