全部產品
Search
文件中心

ApsaraVideo Live:iOS端

更新時間:Jun 30, 2024

通過閱讀本文,您可以瞭解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;
        }
    }