通过阅读本文,您可以了解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; } }