iOS解析M3U8文件及TS文件下载与合并

点击下载 Demo

一、 M3U8文件简介

M3U8文件是指UTF-8编码格式的M3U文件。M3U文件是记录了一个索引纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的网络地址进行在线播放。

M3U8还有一个同胞叫HLS。HLS(HTTP Live Streaming)是苹果公司针对iPhone、iPod、iTouch和iPad等移动设备而开发的基于HTTP协议的流媒体解决方案。在 HLS 技术中 Web 服务器向客户端提供接近实时的音视频流。但在使用的过程中是使用的标准的 HTTP 协议,所以这时,只要使用 HLS 的技术,就能在普通的 HTTP 的应用上直接提供点播和直播。在App Store中的视频相关的应用,基本都是应用的此种技术。该技术基本原理是将视频文件或视频流切分成小片(ts)并建立索引文件(m3u8)。支持的视频流编码为H.264,音频流编码为AAC。

将一个完整视频分成多个TS视频文件,用户下载m3u8文件,通过m3u8文件的索引地址播放具体的每个小段视频。

客户端拿到上面的二级M3U8文件后,会继续请求里面的文件,这时就可进行播放了。上面讲解的是点播的情况,直播的情况,M3U8文件里面会有属性告诉是直播,客户端会定时来请求新的M3U8文件。

20160330111641074107.png

二、M3U8文件解析

M3U8文件参考链接: https://dco4urblvsasc.cloudfront.net/811/81095_ywfZjAuP/game/1000kbps.m3u8 及内容:

#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXTINF:10,
1000kbps-00001.ts
#EXTINF:10,
1000kbps-00002.ts

...

#EXTINF:10,
1000kbps-00099.ts
#EXTINF:10,
1000kbps-00100.ts
#ZEN-TOTAL-DURATION:999.70000
#ZEN-AVERAGE-BANDWIDTH:1098134
#ZEN-MAXIMUM-BANDWIDTH:1700874
#EXT-X-ENDLIST

上面就是解析出来的M3U8索引数据,#EXTINF:10表示的是这段TS的时长是10秒,1000kbps-00001.ts这里表示的是每一个TS的文件名,有的M3U8这里直接是一个完成的http链接。我们要拼接处每一个TS文件的下载链接,TS文件一般都存储在.M3U8索引文件所在的路径,只需要将TS文件名替换到.M3U8索引即可。

根据M3U8地址下载文件,然后解析出M3U8索引的具体内容。

// 处理m3u8文件
- (void)dealPlayList {
    self.tipLab.text = @"处理m3u8文件";
    
    // 读取m3u8文件内容
    NSString *filePath = [self.documentPath stringByAppendingPathComponent:self.textView.text.lastPathComponent];
    NSString *content = [NSString stringWithContentsOfFile:filePath
                                                  encoding:NSUTF8StringEncoding
                                                     error:nil];
    NSArray *array = [content componentsSeparatedByString:@"\n"];
    
    // 筛选出 .ts 文件
    NSMutableArray *listArr = [NSMutableArray arrayWithCapacity:array.count];
    for (NSString *str in array) {
        if ([str containsString:@".ts"]) {
            [listArr addObject:str];
        }
    }
    
    NSString *firstStr = listArr.firstObject;
    NSString *videoName = [firstStr componentsSeparatedByString:@"."].firstObject;
    self.tipLab.text = [NSString stringWithFormat:@"共有 %ld 个视频", listArr.count];
    // 下载 ts 文件
    [self downloadVideoWithArr:listArr andIndex:0 videoName:videoName];
}

三、TS文件下载及合并

筛选出 TS 后,需要挨个下载:

// 循环下载 ts 文件
- (void)downloadVideoWithArr:(NSArray *)listArr andIndex:(NSInteger)index videoName:(NSString *)videoName {
    if (index >= listArr.count) {
        self.tipLab.text = @"视频下载完成";
        [self combVideos];
        return;
    }
    
    self.tipLab.text = [NSString stringWithFormat:@"共有 %ld 个ts文件, 下载中:%.2f%%", listArr.count, (float)index/listArr.count * 100];
    self.progressView.progress = (float)index/listArr.count;
    
    // 拼接ts全路径,有的文件直接包含,不需要拼接
    NSString *downloadURL = [self.textView.text stringByReplacingOccurrencesOfString:self.textView.text.lastPathComponent withString:listArr[index]];
    
    // 存储路径
    NSString *listName = listArr[index];
    NSString *fileName = [NSString stringWithFormat:@"video_%ld.%@",(long)index,listName.pathExtension];
    NSString *destinationPath = [self.videoPath stringByAppendingPathComponent:fileName];
    if ([[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
        [self downloadVideoWithArr:listArr andIndex:index+1 videoName:videoName];
        return;
    }
    
    __weak typeof(self)wkSelf = self;
    [self downloadURL:downloadURL
      destinationPath:destinationPath
             progress:nil
           completion:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
               if (!error) {
                   [wkSelf downloadVideoWithArr:listArr andIndex:index+1 videoName:videoName];
               }
           }];
}

TS文件是可以直接合并的视频文件,因此可以这样合并,

// 合成为一个ts文件
- (void)combVideos {
    NSString *fileName = @"合成原文件.ts";
    NSString *filePath = [[self documentPath] stringByAppendingPathComponent:fileName];
    NSFileManager *mgr = [NSFileManager defaultManager];
    if ([mgr fileExistsAtPath:filePath]) {
        self.tipLab.text = @"已合成视频";
        return;
    }

    NSArray *contentArr = [mgr contentsOfDirectoryAtPath:[self videoPath]
                                                   error:nil];
    NSMutableData *dataArr = [NSMutableData alloc];
    int videoCount = 0;
    for (NSString *str in contentArr) {
        // 按顺序拼接 TS 文件
        if ([str containsString:@"video_"]) {
            NSString *videoName = [NSString stringWithFormat:@"video_%d.%@",videoCount, str.pathExtension];
            NSString *videoPath = [[self videoPath] stringByAppendingPathComponent:videoName];
            // 读出数据
            NSData *data = [[NSData alloc] initWithContentsOfFile:videoPath];
            // 合并数据
            [dataArr appendData:data];
            videoCount++;
        }
    }

    [dataArr writeToFile:filePath atomically:YES];
    
    [self convert];
}

合成后的ts文件,可以直接播放,不过效果不是很好,所以可对其进行转码,转成MP4格式等。可参考 iOS集成FFmpeg及视频格式转码


参考链接
M3U8文件简介:http://blog.sina.com.cn/s/blog_6cf7acdf0102v0xv.html
M3U8 HLS协议文件简介:https://www.movcms.com/news.aspx?id=189
论如何下载一个在线的m3u8文件到本地成为一个mp4!:http://zhuanlan.51cto.com/art/201711/558658.htm
iOS流媒体开发之三:HLS直播(M3U8)回看和下载功能的实现:https://www.jianshu.com/p/b0db841ed6d3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容