本文是对我原创工具m3u8视频下载合并器关键代码解析及软件实现的思路的讲解,想要工具的请跳转链接

1.思路说明

思路挺简单,具体步骤如下:

  1. 下载m3u8文件
  2. 解析m3u8文件获得ts文件列表
  3. 根据文件列表批量下载ts文件
  4. 进行ts的解密操作(如果没有加密则跳过此步骤)
  5. 将解密后的文件或未加密的ts文件按照m3u8中的列表顺序进行合并,得到mp4文件

可以把Kotlin看作为Java语言的增强版,Java中的知识Kotlin也是通用的

本文涉及到知识如下

  • String字符串的处理
  • IO流,读文件进行读写
  • 网络编程
  • AES解密(其实我也不是很懂)

2.m3u8格式介绍

可能这个格式大家不是很了解,其实现在大家看的大多数在线视频,都是使用了m3u8文件来实现在线视频播放的。

M3U8 是 Unicode 版本的 M3U,用 UTF-8 编码。"M3U" 和 "M3U8" 文件都是苹果公司使用的 HTTP Live Streaming(HLS) 协议格式的基础,这种协议格式可以在 iPhone 和 Macbook 等设备播放。

简单地来说,m3u8就是一个播放列表,里面保存这多个短视频的地址,之后服务器从此文件中按照顺序依次下载ts文件并进行播放。

ts文件也可以看做为mp4文件,可以直接拿QQ影音等软件打开,但这只限于未加密的ts文件

可能有些小伙伴会发现, 有些ts文件直接打开软件会提示不支持解析此文件,这其实就是因为ts文件已经被加密了。

我们可以以文本的方式打开m3u8的文件,内容如下:

#EXTM3U
#EXT-X-TARGETDURATION:10 #EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
...

上面的是未加密的m3u8文件内容,我们来看看加密的m3u8文件:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHOD=AES-128,URI="key.key"
#EXTINF:10.000000,
00000.ts
#EXTINF:10.000000,
00001.ts
#EXTINF:10.000000,
00002.ts
#EXTINF:10.000000
...

PS:想要了解m3u8格式更多的资料,请查看我底下的参考链接

这里提一下获取m3u8文件的方式,可以通过浏览器F12进入调试模式,之后找到m3u8的网址资源,或者是通过猫抓(Chrome插件)获取链接,猫抓插件安装请自行百度

3.解析m3u8文件获取信息

由上面我们大概了解到了m3u8文件里面的内容,我们将m3u8文件下载到本地之后,可以得到两个信息,key文件地址(如果采用了加密的话)和全部的ts文件地址

#EXTM3U
#EXT-X-TARGETDURATION:10 #EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
...

上面的这个是没有采用加密的,而且,ts文件都是给出了具体的网址,这是极为理想的情况,但是市面上大部分不会采用这样的,一般都是像下面的这种格式:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x12345(可能有)
#EXTINF:10.000000,
00000.ts
#EXTINF:10.000000,
00001.ts
#EXTINF:10.000000,
00002.ts
#EXTINF:10.000000
...

上面的m3u8文件采用了加密,而且ts文件都是只有编号,没有网址,而且key文件也是非常的简短,根本就不是一个地址,这种情况我们就得进行字符串的拼接处理。

一般的网站,会将m3u8文件、key文件(有加密的话)、ts文件都是放在同一路径

比如说现在有个m3u8的地址为www.xxx.com/2020/1/14/m3u8.m3u8,使用了加密,所以它的key文件为www.xxx.com/2020/1/14/key.key,ts文件为www.xxx.com/2020/1/14/0000.ts

上面只是个简单的例子,具体的网站还得具体分析,可以使用抓包进行分析。

现在来对上面的m3u8文件进行简单地分析吧:

采用了AES-128进行了加密,key的地址为key.key,偏移量IV为12345,有些是没有使用偏移量,则可以使用0来代替

我们通过解析m3u8文件,首先是获得key文件和所有ts文件的地址,然后进行下载即可

通用的下载代码(下载m3u8文件、key文件、ts文件):

/**
* 下载文件到本地
* @param url 网址
* @param file 文件
*/
private fun downloadFile(url: String, file: File) {
val conn = URL(url).openConnection()
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)")
val bytes = conn.getInputStream().readBytes()
file.writeBytes(bytes)
}

4.ts文件下载优化

ts文件过多,如果只开启一个单线程进行下载,下载太慢了,所以,可以采用多线程进行下载

这里的话,由于之前解析可以获得一个ts文件地址的列表,把此列表分为几份列表,每份列表开启一个子线程来进行下载,这样便可以保证任务的并发性,提高了下载速度。

这里,稍微有点复杂,因为要把列表划分成几份列表,我大概是这样分的:

首先,计算出列表可以平均分为几份,每份列表的数目,之后再将剩下的列表分为一份,但是,使用循环的话不是很好写,所以就先把第一个列表和最后一个列表分好,之后来个循环,将中间的平分完。

/**
* 下载ts文件
* @param threadCount 线程数(默认开启5个线程下载,速度较快,100M宽带测试速度有17M/s)
*/
fun downloadTsFile(threadCount: Int = 5) {
val countDownLatch = CountDownLatch(threadCount)
//每份列表的数目
val step = tsUrls.size / threadCount
//最后列表的数目(剩下的)
val yu = tsUrls.size % threadCount
//第一份列表
thread {
val firstList = tsUrls.take(step)
downloadTsList(firstList)
countDownLatch.countDown()
}
//最后一份列表
thread {
val lastList = tsUrls.takeLast(step + yu)
downloadTsList(lastList)
countDownLatch.countDown()
}
//中间的平分
for (i in 1..threadCount - 2) {
val list = tsUrls.subList(i * step, (i + 1) * step + 1)
thread {
downloadTsList(list)
countDownLatch.countDown()
}
}
countDownLatch.await()
println("所有ts文件下载完毕")
}

上面的使用了CountDownLatch类的对象进行线程的控制,只有当所有线程完成之后,此方法才算结束

5.ts文件解密

先上代码,之后再细讲:

//1.获得key和iv的字符串
val keyString = "2e9515db8fe8358bc8fcf6ae601a00be"
val ivString = "d0817f83115d911241fe8ba17673f120" //2.获得key和iv的bytes数组
val keyBytes = decodeHex(keyString)
val ivBytes = decodeHex(ivString) //3.key数组转为SecretKeySpec对象,iv数组转为IvParameterSpec
val algorithm = "AES"
val skey = SecretKeySpec(keyBytes, algorithm)
val iv = IvParameterSpec(ivBytes) //4. 初始化cipher
val transformation = "AES/CBC/PKCS5Padding"
val cipher = Cipher.getInstance(transformation)
cipher.init(Cipher.DECRYPT_MODE,skey,iv) //5. 解密,
val tsFile = File("Q:\\m3u8破解\\2273\\440.ts")
val result = cipher.doFinal(tsFile.readBytes())
val newFile = File("Q:\\m3u8破解\\2273\\440_s.ts") //6.写入文件
BufferedOutputStream(FileOutputStream(newFile)).write(result)

key文件本质是一个16字节文件,我们可以通过winhex等软件查看里面的内容

不过,查看出来之后的内容,我们还得进行转换,因为是字符串,所以得调用decodeHex方法,将字符串转为bytes数组

所以,直接使用代码查看更为方便,Kotlin中可以直接读取bytes(如果使用Java的话,推荐使用common-io的第三方jar包),如:

val keyFile = File("Q:\\test\key.key")
//获得bytes数组
val bytes = keyFile.readBytes()

PS:对了,如果m3u8文件中没有使用到IV偏移量,直接使用0即可(要保证bytes数组的长度为16),如果使用了IV的话,要使用decodeHex方法转为bytes数组

val ivBytes = if (ivString.isBlank()) byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) else decodeHex(ivString)
 /**
* 将字符串转为16进制并返回字节数组
*/
private fun decodeHex(input: String): ByteArray {
val data = input.toCharArray()
val len = data.size
if (len and 0x01 != 0) {
try {
throw Exception("Odd number of characters.")
} catch (e: Exception) {
e.printStackTrace()
} }
val out = ByteArray(len shr 1) try {
var i = 0
var j = 0
while (j < len) {
var f = toDigit(data[j], j) shl 4
j++
f = f or toDigit(data[j], j)
j++
out[i] = (f and 0xFF).toByte()
i++
}
} catch (e: Exception) {
e.printStackTrace()
} return out
} @Throws(Exception::class)
private fun toDigit(ch: Char, index: Int): Int {
val digit = Character.digit(ch, 16)
if (digit == -1) {
throw Exception("Illegal hexadecimal character $ch at index $index")
}
return digit
}

有了key文件和IV偏移量的bytes,我们就可以往下走了,下面的代码其实都没有什么好说明的,明眼人估计一看就懂了,这里就不多说了

需要注意的是,因为解密之后,我们还需要把所有已经解密好的ts文件按照顺序合并成一个mp4文件,所以,注意解密后数据的名字。

建议在保存原来编号的基础上,加上写简短的字母,之后,就可以通过contains方法进行判断是否文件名是否符合条件

6.ts文件合并

合并的话,使用IO流,按照顺序依次把流追加到末尾即可

参考

m3u8 文件格式详解

关于m3u8格式的视频文件ts转mp4下载和key加密问题

aes 256 32位key和32位iv

加密ts解密

打造m3u8视频(流视频)下载解密合并器(kotlin)的更多相关文章

  1. stars-one原创工具——m3u8视频下载合并器(kotlin)

    一款可以下载m3u8.解密ts文件及合并ts文件的视频下载工具 蓝奏云下载地址 github地址 软件对你有帮助的话,不妨赞赏一波!感谢! 程序说明 采用多线程下载,可有效的提高下载速度 内置解密程序 ...

  2. Python3 根据m3u8下载视频,批量下载ts文件并且合并

    Python3 根据m3u8下载视频,批量下载ts文件并且合并 m3u8是苹果公司推出一种视频播放标准,是一种文件检索格式,将视频切割成一小段一小段的ts格式的视频文件,然后存在服务器中(现在为了减少 ...

  3. 关于m3u8格式的视频文件ts转mp4下载和key加密问题

    一,利用网站浏览器F12键,利用谷歌浏览器插件找到视频的.m3u8文件,并打开. 二,打开m3u8文件后,里面有很多.ts的链接,和key的链接. 三,保存为html文件,下载ts文件,代码如下:可加 ...

  4. python+fiddler下载vip视频 && ts视频可合并

    如果你只想在线看视频可以去看这篇博客:python实现通过指定浏览器免费观看vip视频  先看一下我们程序运行的结果 我们要解析的接口就是(就是这个"接口+视频地址"可以解析出vi ...

  5. 怎么下载腾讯课堂M3U8格式的视频

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 前言 用过腾讯课堂的小伙伴们可能 ...

  6. Python爬虫入门教程 51-100 Python3爬虫通过m3u8文件下载ts视频-Python爬虫6操作

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

  7. m3u8解析、转码、下载、合并

    m3u8解析.转码.下载.合并 现在网也上大多数视频需要下载都很麻烦,极少数是MP4,大多都是m3u8, 先说视频下载, pc端: 打开网页,点击视频播放,打开开发者工具,找到网络那一栏, 等整个网页 ...

  8. 使用vlc播放器播放rtsp流视频

    可参考: 使用vlc播放器做rtsp服务器 web网页中使用vlc插件播放相机rtsp流视频 使用vlc进行二次开发做自己的播放器 首先需要安装vlc播放器,下载及安装步骤略 使用vlc播放器播放rt ...

  9. web网页中使用vlc插件播放相机rtsp流视频

    可参考: 使用vlc播放器做rtsp服务器 使用vlc播放器播放rtsp视频 使用vlc进行二次开发做自己的播放器 vlc功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...

随机推荐

  1. dotnet core 添加 SublimeText 编译插件

    因为 SublimeText 有很多插件都是使用 Py 写的,而我想使用 dotnet core 给 SublimeText 写一个编译插件,也就是在我使用 Markdown 的时候可以点击编译,将 ...

  2. linux加载和卸载模块

    模块建立之后, 下一步是加载到内核. 如我们已指出的, insmod 为你完成这个工作. 这个 程序加载模块的代码段和数据段到内核, 接着, 执行一个类似 ld 的函数, 它连接模块中 任何未解决的符 ...

  3. Callable Objects

    We learned in 7.11 that there are "array-like" objects that are not true arrays but can be ...

  4. C# 大端小端转换

    关于大端和小端,是一个有趣的问题.本文告诉大家如何在C#转换大端和小端. 这里有一个有趣的故事,请看详解大端模式和小端模式 - CSDN博客 默认的 C# 使用的是小端,如果收到的消息是大端,那么就会 ...

  5. dotnet core 发布只带必要的依赖文件

    在使用 dotnet core 发布独立项目的时候,会带上大量依赖的库,但是通过微软提供的工具可以去掉一些在代码没有用到的库. 本文介绍的工具是 Microsoft.Packaging.Tools.T ...

  6. 牛客小白月赛15A 斑羚飞渡

    链接:https://ac.nowcoder.com/acm/contest/917/A 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语言262144K 64b ...

  7. LuoguP3521 [POI2011]ROT-Tree Rotations

    P3521 [POI2011]ROT-Tree Rotations 题目大意: 给一棵\((1≤n≤200000)\)个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少. 我们发现 ...

  8. C# struct和class

    文字说明 值类型,存储于堆栈,存在于计算机内存逻辑区域中 引用类型,存储在堆中,位于计算机的不同逻辑位置 struct是值类型 推荐较小的数据类型使用,因为结构的大小会影响应用程序的性能 修改实际上是 ...

  9. javascript 闭包的理解(一)

    过很多谈如何理解闭包的方法,但大多数文章,都是照抄或者解释<Javascript高级程序设计(第三版)>对于闭包的讲解,甚至例程都不约而同的引用高程三181页‘闭包与变量’一节的那个“返回 ...

  10. 第二阶段:2.商业需求文档MRD:5.MRD-Roadmap及规划

    产品路线图可以用泳道图来实现.将之前做过的泳道图的角色换为阶段即可. 可以以月为单位.左边就是一些产品的功能. 基础功能,有的功能会跨月甚至夸功能模块.比如图中的会员等级. 通过线段来联系各个功能与先 ...