博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ios - m3u8 解析组装
阅读量:6640 次
发布时间:2019-06-25

本文共 8432 字,大约阅读时间需要 28 分钟。

前言: 之前有项目要实现m3u8本地播放,后面自己写了一个多线程下载的demo,里面坑很多。因为时间问题,也没时间去填坑,后面还是采用单线程下载。但想了下m3u8本地缓存播放这个需求,每个公司的需求可能有所差异。但m3u8是协议好的东西,解析跟组装的部分应该都差不多。所以下面就记录下这部分的内容。

  1. 定义模型存储信息

    @class ZBLM3u8TsInfo;@interface ZBLM3u8Info : NSObject@property (nonatomic, copy) NSString *targetduration;@property (nonatomic, copy) NSString *version;@property (nonatomic, copy) NSString *mediaSequence;///key信息@property (nonatomic, copy) NSString *keyMethod;@property (nonatomic, copy) NSString *keyUri;@property (nonatomic, copy) NSString *keyLocalUri;@property (nonatomic, copy) NSString *keyIv;@property (nonatomic, strong) NSMutableArray 
    *tsInfos;@end@interface ZBLM3u8TsInfo : NSObject@property (nonatomic, copy) NSString *duration;@property (nonatomic, copy) NSString *oriUrlString;@property (nonatomic, copy) NSString *localUrlString;@property (nonatomic, assign) NSInteger index;@property (nonatomic, assign,getter = isHasDiscontiunity) BOOL hasDiscontiunity;@end复制代码
  2. 定义处理类,根据m3u8文本信息把信息保存到模型中并做相关的容错处理

    .h定义

    @class ZBLM3u8Info;typedef void (^ZBLM3u8AnalysiseCompletaionHandler)(ZBLM3u8Info *m3u8Info,NSError *error);@interface ZBLM3u8Analysiser : NSObject/** m3u8解析 @param urlStr m3u8网络url @param completaionHandler 回调 */+ (void)analysisWithUrlString:(NSString*)urlStr completaionHandler:(ZBLM3u8AnalysiseCompletaionHandler)completaionHandler;/** m3u8文本组装 @param m3u8Info m3u8信息模型 @return m3u8文本 */+ (NSString*)synthesisLocalM3u8Withm3u8Info:(ZBLM3u8Info *)m3u8Info;FOUNDATION_EXPORT NSString * const ZBLM3u8AnalysiserResponeErrorDomain;FOUNDATION_EXPORT NSString * const ZBLM3u8AnalysiserAnalysisErrorDomain;@end复制代码

    .m实现

    /*解析m3u8 和组装m3u8*/NSString * const ZBLM3u8AnalysiserResponeErrorDomain = @"error.m3u8.analysiser.respone";NSString * const ZBLM3u8AnalysiserAnalysisErrorDomain = @"error.m3u8.analysiser.analysis";@implementation ZBLM3u8Analysiser+ (void)analysisWithUrlString:(NSString*)urlStr completaionHandler:(ZBLM3u8AnalysiseCompletaionHandler)completaionHandler{    NSLog(@"analysis start");    //判断m3u8文本是否已经保存到本地,因为下载m3u8文本是耗时操作    NSString *oriM3u8String = [NSString stringWithContentsOfFile:[[ZBLM3u8Setting fullCommonDirPrefixWithUrl:urlStr] stringByAppendingPathComponent:[ZBLM3u8Setting oriM3u8InfoFileName]] encoding:0 error:nil];        __block BOOL happenException = NO;    if (oriM3u8String.length) {        NSLog(@"use local oriM3u8Info");        //处理有可能失败,所有需要加try-catch-finally,保护程序        @try {            [self analysisWithM3u8String:oriM3u8String completaionHandler:completaionHandler];        } @catch (NSException *exception) {            happenException = YES;            [[ZBLM3u8FileManager shareInstance]removeFileWithPath:[[ZBLM3u8Setting fullCommonDirPrefixWithUrl:urlStr] stringByAppendingPathComponent:[ZBLM3u8Setting oriM3u8InfoFileName]]];            completaionHandler(nil,[[NSError alloc]initWithDomain:ZBLM3u8AnalysiserAnalysisErrorDomain code:NSURLErrorUnknown userInfo:@{@"info":exception.reason}]);        } @finally {                    }        if (!happenException) {            return;        }    }        happenException = NO;    //异步获取    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        NSString *m3u8Str = nil;        //处理有可能失败,所有需要加try-catch-finally,保护程序        @try {            NSError *error = nil;            //根据url请求获取m3u8文本,耗时操作            m3u8Str = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:urlStr] usedEncoding:0 error:&error];                        if (error)            {                completaionHandler(nil,error);                return ;            }                                    if (m3u8Str.length == 0)            {                completaionHandler(nil,[[NSError alloc]initWithDomain:ZBLM3u8AnalysiserResponeErrorDomain code:NSURLErrorBadServerResponse userInfo:nil]);                return;            }                        //解析            [self analysisWithM3u8String:m3u8Str completaionHandler:completaionHandler];                    } @catch (NSException *exception) {            happenException = YES;            completaionHandler(nil,[[NSError alloc]initWithDomain:ZBLM3u8AnalysiserAnalysisErrorDomain code:NSURLErrorUnknown userInfo:@{@"info":exception.reason}]);        } @finally {                    }                if (!happenException) {            //保存原m3u8文本,方便二次利用            [[ZBLM3u8FileManager shareInstance]saveDate:[m3u8Str dataUsingEncoding:NSUTF8StringEncoding] ToFile:[[ZBLM3u8Setting fullCommonDirPrefixWithUrl:urlStr] stringByAppendingPathComponent:[ZBLM3u8Setting oriM3u8InfoFileName]] completaionHandler:nil];        }    });}/** private method m3u8文本解析 @param m3u8String m3u8文本 @param completaionHandler 回调 */+ (void)analysisWithM3u8String:(NSString*)m3u8String completaionHandler:(ZBLM3u8AnalysiseCompletaionHandler)completaionHandler{        ZBLM3u8Info *info = [ZBLM3u8Info new];    info.version = [[m3u8String subStringFrom:@"#EXT-X-VERSION:" to:@"#"] removeSpaceAndNewline];    info.targetduration = [[m3u8String subStringFrom:@"#EXT-X-TARGETDURATION:" to:@"#"] removeSpaceAndNewline];    info.mediaSequence = [[m3u8String subStringFrom:@"#EXT-X-MEDIA-SEQUENCE:" to:@"#"] removeSpaceAndNewline];        info.keyMethod = [m3u8String subStringFrom:@"#EXT-X-KEY:METHOD=" to:@","];    info.keyUri = [m3u8String subStringFrom:@"URI=\"" to:@"\""];    info.keyIv = [[m3u8String subStringFrom:@"IV=" to:@"#"] removeSpaceAndNewline];        NSRange tsRange = [m3u8String rangeOfString:@"#EXTINF:"];    if (tsRange.location == NSNotFound) {        completaionHandler(nil,[[NSError alloc]initWithDomain:ZBLM3u8AnalysiserAnalysisErrorDomain code:NSURLErrorUnknown userInfo:@{@"info":@"none downloadUrl for .ts file"}]);        return;    }    NSInteger index = 0;    NSMutableArray *tsInfos = @[].mutableCopy;    m3u8String = [m3u8String substringFromIndex:tsRange.location];    while (tsRange.location != NSNotFound) {        ZBLM3u8TsInfo *tsInfo = [ZBLM3u8TsInfo new];        tsInfo.duration = [m3u8String subStringFrom:@"#EXTINF:" to:@","];        m3u8String = [m3u8String subStringForm:@"," offset:1];        tsInfo.oriUrlString = [[m3u8String subStringTo:@"#"] removeSpaceAndNewline];                NSRange exRange = [m3u8String rangeOfString:@"#EX"];        NSRange discontinuityRange = [m3u8String rangeOfString:@"#EXT-X-DISCONTINUITY"];        if (exRange.location == discontinuityRange.location) {            tsInfo.hasDiscontiunity = YES;        }        tsInfo.index = index ++;        [tsInfos addObject:tsInfo];        tsRange = [m3u8String rangeOfString:@"#EXTINF:"];        if (tsRange.location != NSNotFound) {            m3u8String = [m3u8String subStringForm:@"#EXTINF:" offset:0];        }    }    NSLog(@"analysis compelte");    info.tsInfos = tsInfos;    NSParameterAssert(completaionHandler);    completaionHandler(info,nil);}复制代码
  3. 根据本地保存的ts信息更新模型,并重新组装m3u8文本信息

    + (NSString*)synthesisLocalM3u8Withm3u8Info:(ZBLM3u8Info *)m3u8Info{    NSString *newM3u8String = @"";        NSString *header = @"#EXTM3U\n";    if (m3u8Info.version.length) {        header = [header stringByAppendingString:[NSString stringWithFormat:@"#EXT-X-VERSION:%ld\n",(long)m3u8Info.version.integerValue]];    }    if (m3u8Info.targetduration.length) {        header = [header stringByAppendingString:[NSString stringWithFormat:@"#EXT-X-TARGETDURATION:%ld\n",(long)m3u8Info.targetduration.integerValue]];    }    if (m3u8Info.mediaSequence.length) {        header = [header stringByAppendingString:[NSString stringWithFormat:@"#EXT-X-MEDIA-SEQUENCE:%ld\n",(long)m3u8Info.mediaSequence.integerValue]];    }        NSString *keyStr = @"";    if(m3u8Info.keyUri.length)    {        keyStr = [NSString stringWithFormat:@"#EXT-X-KEY:METHOD=%@,URI=\"%@\",IV=%@\n",m3u8Info.keyMethod,m3u8Info.keyLocalUri,m3u8Info.keyIv];    }        __block NSString *body = @"";    [m3u8Info.tsInfos enumerateObjectsUsingBlock:^(ZBLM3u8TsInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        NSString *tsInfo = [NSString stringWithFormat:@"#EXTINF:%.6lf,\n%@\n",obj.duration.floatValue,obj.localUrlString];        body =  [body stringByAppendingString:tsInfo];        if (obj.isHasDiscontiunity) body = [body stringByAppendingString:@"#EXT-X-DISCONTINUITY\n"];    }];        newM3u8String = [[[[newM3u8String stringByAppendingString:header] stringByAppendingString:keyStr] stringByAppendingString:body] stringByAppendingString:@"#EXT-X-ENDLIST"];        return newM3u8String;    }复制代码

demo:

转载地址:http://xvovo.baihongyu.com/

你可能感兴趣的文章
Hadoop基础-Protocol Buffers串行化与反串行化
查看>>
Hadoop生态圈-phoenix(HBase)的索引配置
查看>>
LitJson使用方法
查看>>
Windows 7上安装配置TensorFlow-GPU运算环境
查看>>
【VS2013编译DirectX Tutorials时遇到的错误】FXC : error X3501: 'main': entrypoint not found
查看>>
npm上传vue组件
查看>>
在网页内预览pdf
查看>>
加载MySQL、Oracle、SQL Server 2000、SQL Server 2005及以上版本 的加载数据库驱动程序...
查看>>
WebGL学习笔记四点一
查看>>
java-switch语句
查看>>
[Android Security] APK自我保护 - DEX/APK校验
查看>>
[web前端] yarn和npm命令使用
查看>>
Minimum Expression of String 字符串最小表示
查看>>
國王遊戲(2012年NOIP全国联赛提高组)
查看>>
谈谈 ES6 的 Promise 对象
查看>>
PHP $_SERVER['PHP_SELF']、$_SERVER['SCRIPT_NAME'] 与 $_SERVER['REQUEST_URI'] 之间的区别
查看>>
创业者要有杀手气质和传教士能力
查看>>
Eclipse报错Resource '/.org.eclipse.jdt.core.external.folders/.link5' already exists.
查看>>
jmeter实例,如果有说明错误,请各位大神批评
查看>>
模拟时间--延时处理
查看>>