通過閱讀本文,您可以瞭解iOS端阿里雲播放器SDK整合Native RTS SDK實現超低延時直播的方法。
前提條件
環境中已安裝CocoaPods。
SDK整合
使用pod方式添加阿里雲播放器SDK和Native RTS SDK的依賴檔案。
開啟終端視窗。
進入專案所在路徑,建立Podfile檔案。
pod init
編輯Podfile檔案,添加最新版本的依賴。
安裝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; } }