先上代码:

  1    @SneakyThrows  //合并操作,最终文件不包含结束标识,方便多次合并
2 private static void mergeM3U8File(String source, String target) {
3
4 //读取target
5 List<String> sl = new ArrayList<>();
6 try (BufferedReader reader = new BufferedReader(new FileReader(source))) {
7 String line;
8 while ((line = reader.readLine()) != null) {
9 sl.add(line);
10 }
11 }
12 //读取source
13 List<String> tl = new ArrayList<>();
14 try (BufferedReader reader = new BufferedReader(new FileReader(target))) {
15 String line;
16 while ((line = reader.readLine()) != null) {
17 tl.add(line);
18 }
19 }
20 //合并且统一ts文件名
21 String filename = target.replace(dir,"");
22 filename = filename.replace(".m3u8","");
23 Long order = 0l;
24 if (tl.size() <= 0) {
25 for (String s : sl) {
26 if(s.startsWith("#")) tl.add(s);
27 else {
28 tl.add(filename + order + ".ts");
29 log.info("转存文件 {}{} 存在状态 {}", dir, s, Files.exists(Paths.get(dir + s)));
30 Files.copy(Paths.get(dir + s), Paths.get(dir + filename + order + ".ts"));
31 Files.delete(Paths.get(dir + s));
32 order++;
33 }
34 }
35 }
36 else {
37 //删除结束段落
38 if(tl.get(tl.size()-1).startsWith("#EXT-X-ENDLIST")) {
39 tl.remove(tl.size() - 1);
40 }
41 //获取文件序号(dir+filename+order.ts)
42 String s = tl.get(tl.size() - 1);
43 s = s.replace(".ts","");
44 s = s.replace(filename,"");
45 order = Long.parseLong(s);
46 //sl处理 获取头部标识的下标并移除头部
47 int i = 0;
48 for (i = 0; i < sl.size(); i++) {
49 if (sl.get(i).startsWith("#EXTINF")) break;
50 }
51 for (int j = 0; j < i; j++) {
52 sl.remove(0);
53 }
54 sl.remove(sl.size() - 1);
55 tl.add("#EXT-X-DISCONTINUITY");
56 //sl文件内容写入tl
57 for (int j = 0; j < sl.size(); j++) {
58 String s1 = sl.get(j);
59 if(s1.startsWith("#EXTINF")) //#EXTINF
60 tl.add(s1);
61 else//按规则写入文件索引
62 {
63 order++;
64 tl.add(filename + order + ".ts");
65 log.info("转存文件 {}{} 存在状态 {}",dir,s1,Files.exists(Paths.get(dir + s1)));
66 Files.copy(Paths.get(dir + s1), Paths.get(dir + filename + order + ".ts"));
67 Files.delete(Paths.get(dir + s1));
68 }
69 }
70 }
71 //生成新的buffer
72 StringBuffer buffer = new StringBuffer();
73 for (String t : tl) {
74 buffer.append(t).append(System.lineSeparator());
75 }
76 //写入文件
77 try (BufferedWriter writer = new BufferedWriter(new FileWriter(target))) {
78 writer.write(buffer.toString());
79 }
80 //清除source
81 Files.delete(Paths.get(source));
82 }
83
84 @SneakyThrows //给m3u8文件添加结束标识
85 private static void endM3u8(String m3u8) {
86 List<String> sl = new ArrayList<>();
87 try (BufferedReader reader = new BufferedReader(new FileReader(m3u8))) {
88 String line;
89 while ((line = reader.readLine()) != null) {
90 sl.add(line);
91 }
92 }
93 sl.add("#EXT-X-ENDLIST");
94 //生成新的buffer
95 StringBuffer buffer = new StringBuffer();
96 for (String t : sl) {
97 buffer.append(t).append(System.lineSeparator());
98 }
99 //写入文件
100 try (BufferedWriter writer = new BufferedWriter(new FileWriter(m3u8))) {
101 writer.write(buffer.toString());
102 }
103
104 }

关于m3u8文件的说明:

- #EXTM3U:文件头,标识这是一个M3U8文件。
- #EXT-X-VERSION:表示M3U8的版本号。
- #EXT-X-TARGETDURATION:表示每个分段的最长时间(以秒为单位)。
- #EXT-X-MEDIA-SEQUENCE:表示播放列表中的第一个分段的编号。
- #EXTINF:表示当前分段的播放时间长度(以秒为单位)和URI。
- #EXT-X-ENDLIST:表示播放列表结束。

- #EXT-X-STREAM-INF:表示变换码率视频流中的音视频属性。
- #EXT-X-DISCONTINUITY:表示两个分段之间的不连续性。
- #EXT-X-PROGRAM-DATE-TIME:表示当前分段的播放时间点。
- #EXT-X-BYTERANGE:表示分段的字节范围。

合并代码中的m3u8文件的格式:

 * #EXTM3U
* #EXT-X-VERSION:3
* #EXT-X-TARGETDURATION:2
* #EXT-X-MEDIA-SEQUENCE:0
* #EXT-X-PLAYLIST-TYPE:EVENT
* #EXTINF:1.441000,
* 2052636967-12640237988249000.ts
* #EXTINF:1.848000,
* 2052636967-12640237988249001.ts
* #EXT-X-ENDLIST

关于代码中的一些说明:

1、由于m3u8文件中的ts文件索引规则一般是  m3u8文件名+索引号,因此在合并文件之前需要先统一索引文件名;

2、由于m3u8文件之间是相互独立的,所以要在写入外部m3u8索引文件之前加入 #EXT-X-DISCONTINUITY 标识,否则播放器会在播放完第一部分的m3u8文件之后停止播放;

3、关于m3u8文件的结束行 #EXT-X-ENDLIST 问题,大部分播放器在未识别到结束标识且已经读取到文件最后一行索引时,会不断的请求m3u8文件,获取新资源,可以通过这个特新来实现直播效果;

												

利用简单的IO操作实现M3U8文件之间的合并的更多相关文章

  1. uniapp中拿到base64转blob对象,或base64转bytes字节数组,io操作写入字节流文件bytes

    1. uniAPP中拿到附件的base64如何操作,如word文件 /*** 实现思路:* 通过native.js的io操作创建文件,拿到平台绝对路径* 再通过原生类进行base64解码,拿到字节流b ...

  2. Python中的文件IO操作(读写文件、追加文件)

    Python中文件的读写包含三个步骤:打开文件,读/写文件,关闭文件. 文件打开之后必须关闭,因为在磁盘上读写文件的功能是由操作系统提供的,文件作为对象,被打开后会占用操作系统的资源,而操作系统在同一 ...

  3. [python 学习] IO操作之读写文件

    一.读取全部文件: # -*- coding: utf-8 -*- f = open('qq_url.txt','r'); print f.read(); f.close(); 二.读取规定长度文件 ...

  4. python 利用三方的xlrd模块读取excel文件,处理合并单元格

      目的: python能使用xlrd模块实现对Excel数据的读取,且按照想要的输出形式.  总体思路: (1)要想实现对Excel数据的读取,需要用到第三方应用,直接应用. (2)实际操作时候和我 ...

  5. 【UNIX环境高级编程】文件 IO 操作 一 ( open | close | creat | lseek | write | read )

    博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271 一. 文件打开关闭操作相关函数介绍 1. open 函数 (1) op ...

  6. C# IO操作(二)File类和Directory类的常用方法

    本篇主要介绍一些常用的IO操作,对文件和目录的操作:留给自己复习之用. 1.创建文件 string sPath1=Path.GetDirectoryName(Assembly.GetExecuting ...

  7. Linux系统编程:简单文件IO操作

    使用Linux的文件API,经常看见一个东西,叫做文件描述符. 什么是文件描述符? (1)文件描述符其实实质是一个数字,这个数字在一个进程中表示一个特定的含义,当我们open打开一个文件时,操作系统在 ...

  8. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  9. 文件IO操作

    前言 本文介绍使用java进行简单的文件IO操作. 操作步骤 - 读文件 1. 定义一个Scanner对象 2. 调用该对象的input函数族进行文件读取 (参见下面代码) 3. 关闭输入流 说明:其 ...

  10. 树莓派学习笔记——使用文件IO操作GPIO SysFs方式

    0 前言     本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...

随机推荐

  1. AD域安全攻防实践(附攻防矩阵图)

    以域控为基础架构,通过域控实现对用户和计算机资源的统一管理,带来便利的同时也成为了最受攻击者重点攻击的集权系统. 01.攻击篇 针对域控的攻击技术,在Windows通用攻击技术的基础上自成一套技术体系 ...

  2. 刘勇智:一码通缺陷分析与架构设计方案丨声网开发者创业讲堂 Vol.02

    本文内容源自「声网开发者创业讲堂 Vol.02」的演讲分享,分享讲师为 Thoughtworks 专家级咨询师刘勇智.大家可以点击此链接,观看视频回放以及下载讲师 PPT. 从去年年底到现在,随着疫情 ...

  3. 同步协程的必备工具: WaitGroup

    1. 简介 本文将介绍 Go 语言中的 WaitGroup 并发原语,包括 WaitGroup 的基本使用方法.实现原理.使用注意事项以及常见的使用方式.能够更好地理解和应用 WaitGroup 来协 ...

  4. Promise的使用及原理

    此文章主要讲解核心思想和基本用法,想要了解更多细节全面的使用方式,请阅读官方API 这篇文章假定你具备最基本的异步编程知识,例如知道什么是回调,知道什么是链式调用,同时具备最基本的单词量,例如page ...

  5. SpringBoot——国际化

    更多内容,前往IT-BLOG 一.Spring 编写国际化时的步骤 [1]编写国际化配置文件:[2]使用 ResourceBundleMessageSource 管理国际化资源文件:[3]在页面使用 ...

  6. 安装Windows10后电脑整体速度变慢

    是不是觉得从旧版本Windows系统比如(Windows 7)升级到Windows10以后,感觉什么操作都变慢了.譬如打开文件夹,游戏加载速度变缓慢.尤其是腾讯WeGame软件进入游戏前的检测速度明显 ...

  7. 五月二十七日jdbc,算法以及数据库

    1.ResultSetMetaData接口主要获得结果集.例如:结果集字段数量和名字通过ResultSet的getMetaData()方法获得对应对象 public class app17_20 { ...

  8. Vue3中无法为el-tree-select设置反选问题分析

    好久没有写博客了,刚好上周遇到一个难缠问题,这里记录一下. 环境:Vue3.2.Element Plus 问题:子组件 setting.vue => 弹窗组件 Dialog => 树选择组 ...

  9. python 类中的属性排序

    可以使用Python中的类(class)来定义一个包含姓名和年龄的类.以下是一个示例代码: class Person: def __init__(self, name, age): self.name ...

  10. GitLab 配置优化

    代码库最近换了,使用了 GitLab.这两天观察了下服务器的使用情况,发现 GitLab 很吃内存.直接占用了 30 多个G的内存. 一.发现问题 使用 top 命令查看内存时,发现前几十个都是同一个 ...