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