先上代码:

  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. NDVI最大值合成

    这里有NDVI250m分辨率的数据,目标:合成年最大值数据 [Spatial Analyst工具]|[局部分析]|[像元统计数据]

  2. 【ASP.NET Core】修改Blazor.Server的Hub地址后引发的问题

    Blazor Server,即运行在服务器上的 Blazor 应用程序,它的优点是应用程序在首次运行时,客户端不需要下载运行时.但它的代码是在服务器上执行的,然后通过 SignalR 通信来更新客户端 ...

  3. java的数据和表达式

    一.基本语法元素 1.空白和注释及语句 (1)空白: 换行符.回车符.空格键.水平定位键(Tab) 编译器会忽略掉多余的空白 作用:增加程序的易读性 (2)注释:主要作用是将代码解释其功能和作用,在编 ...

  4. Redis6.0.9集群搭建

    前提条件: Redis版本:6.0.9(因为5.0之前创建用的是redis-trib,还需要ruby,ruby-gem) 安装环境: Centos7 1. 准备配置文件 一个是通用文件:redis-c ...

  5. ASP.NET Core Web API Swagger 按标签Tags分组排序显示

    需求 swagger页面按标签Tags分组显示. 没有打标签Tags的接口,默认归到"未分组". 分组内按接口路径排序 说明 为什么没有使用GroupName对接口进行分组? 暂时 ...

  6. 声网王浩宇:RTE 场景下的 Serverless 架构挑战【RTE 2022】

    前言 在「RTE2022 实时互联网大会」中,声网云原生边缘计算团队的负责人 @王浩宇 Dylan 以<RTE 场景下的 Serverless 架构挑战 -- 声网如何兼顾后端服务的可靠.高效和 ...

  7. GUI编程 --2

    GUI编程 --2 2.4 事件监听 按钮的使用. package com.ssl.lesson02; import java.awt.*; import java.awt.event.ActionE ...

  8. Salesforce LWC学习(十二) Dependence Picklist实现

    本篇可参看: Salesforce LWC学习(六) @salesforce & lightning/ui*Api Reference salesforce零基础学习(八十七)Apex 中Pi ...

  9. 在react中使用wangEditorV5

    wangEditor是基于JavaScript和css的一款web富文本编辑器,是国内比较好用的一款轻量级富文本编辑器,上手简单,易用且开源免费. 官方文档:http://www.wangeditor ...

  10. GaussDB(DWS)网络调度与隔离管控能力

    摘要:调度算法是调度器的核心,设计调度算法要充分考虑业务场景和用户需求,没有万能的调度算法,只有合适的调度算法. 本文分享自华为云社区<GaussDB(DWS)网络调度与隔离管控能力>,作 ...