使用golang来解密m3u8视频播放列表里面的ts文件
如果我有一个1.2G的mp4格式的电影,想要放在网站上进行播放,直接用video标签,src属性设置为视频的地址就可以了!
这种观看体验,究竟怎么样,可以自己去尝试下。。。
结果是令人崩溃的。。。加载巨慢,无法选择性观看自己想要看的部分,反正就是哪哪儿都不爽。
那么为了解决这个问题,现在有很多新的播放技术,其中一种就是m3u8。
1、m3u8其实不是一个真正的视频文件,而是一个视频播放列表(playlist)。
2、它是一种文本文件,里面记录了一系列的视频片段(segment)的网络地址。
3、这些视频片段通常是ts格式的,也就是传输流(transport stream)格式。
4、ts格式的视频片段可以很快地在网络上传输和播放,而不需要等待整个文件下载完毕。
5、这样就可以实现流媒体(streaming media)的效果,也就是边下边播。
看完上面5点,是不是瞬间感觉一切都好起来了。
有点扯远了,今天的主要目的是分享如何使用golang对加密的ts文件进行解密处理
先上一个m3u8的文件内容给大伙儿瞧一瞧,因为篇幅问题,我只贴了开始、中间、尾部的部分,但是足够说明问题了
1 #EXTM3U 2 #EXT-X-VERSION:3 3 #EXT-X-TARGETDURATION:8 4 #EXT-X-MEDIA-SEQUENCE:0 5 #EXT-X-KEY:METHOD=AES-128,URI="/ts.key",IV=0x00000000000000000000000000000000 6 #EXTINF:4.000000, 7 index0.ts 8 #EXTINF:4.000000, 9 index1.ts 10 #EXTINF:4.000000, 11 index2.ts 12 #EXTINF:4.000000, 13 index3.ts 14 #EXTINF:4.000000, 15 index4.ts 16 ...... 17 #EXTINF:7.360000, 18 index9.ts 19 #EXTINF:2.680000, 20 index10.ts 21 #EXTINF:4.000000, 22 ...... 23 index1691.ts 24 #EXTINF:4.000000, 25 index1692.ts 26 #EXTINF:4.000000, 27 index1693.ts 28 #EXTINF:4.000000, 29 index1694.ts 30 #EXTINF:3.760000, 31 index1695.ts 32 #EXT-X-ENDLIST
仔细看,我们发现这些个ts文件其实是经过加密的,不是明文的。
我刚开始没注意,拿到m3u8文件之后,直接一股脑把所有的ts文件全部下载下来,然后按顺序合并成一个完整的ts文件,却发现在任何播放器里面都无法播放,一下子就蒙圈了。
- 这里合并诸多ts文件为一个ts文件的方法有很多,我这里介绍2个方法,如果安装了FFmpeg命令行工具,用这个可以全部搞定,合并和转化格式。
- windows系统下,直接使用dos命令,编写一个bat文件执行即可,文件内容如下:
-
1 copy /b 0.ts+1.ts+2.ts+3.ts new.ts 2 //每个ts文件之间用+号连接 3 //news.ts为最终输出的文件 4 //如果ts文件是明文的,而且合并过程没有出现错误,那么最终生成的ts文件是可以在播放器里面正常播放的 5 //为了播放效果更好,而且可以多端兼容,我们最好是把ts文件转成mp4文件 6 //ts转mp4,可以使用第三方软件,例如:格式工厂,也可以使用FFmpeg命令行工具,在cmd终端输入: 7 ffmpeg -i input.ts -c:v libx264 -c:a aac -strict -2 output.mp4 8 //input.ts为需要转换的TS文件名 9 //-c:v libx264表示使用H.264编码器 10 //-c:a aac表示使用AAC编码器 11 //-strict -2表示开启兼容模式,保证兼容性 12 //output.mp4为输出文件名
- 使用ffmpeg工具也可以,这种方式可以直接生成mp4文件,要使用这个工具,需要先去ffmpeg官网下载对应的系统版本,然后进行安装才可以使用哦,然后在cmd终端输入:
-
1 ffmpeg -i "concat:input1.ts|input2.ts|input3.ts" -c copy output.mp4 2 //input1.ts、input2.ts、input3.ts等为需要合并的TS文件名,使用|分隔。 3 //-c copy表示直接复制视频和音频数据,不进行编码,以提高转换速度。 4 //output.mp4为输出文件名。
之前的ts文件我也是这样操作的呀,下载之后,直接合并,然后播放,整个流程都是丝滑,可是为什么这里的ts文件无法播放勒。。。
前面我提到过,这个m3u8里面的ts文件是加密过的,所以直接合并是无法播放的,从那里可以知道是加密过的呢
文件里面说明了,加密方法:AES-128,加密KEY存放的URL地址:ts.key,偏移量:IV的值
知道了这些,那么接下来就是要处理解密文件的事情了,解密了之后,再合并文件,应该就可以正常播放了。
下载ts.key文件,我们发现其实里面就是存放了一个秘钥,现在我使用go来写一个解密文件的小工具,贴一下我的代码,我测试过是可以正常跑的,有需要的小伙伴可以参考下:
1 package main 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "encoding/binary" 7 "fmt" 8 "io" 9 "os" 10 "strconv" 11 ) 12 //解密ts文件内容,并写入到输出文件 13 func decodeAES128CBC(key []byte, index int, inFile string, out io.Writer) error { 14 block, err := aes.NewCipher(key) 15 if err != nil { 16 return err 17 } 18 inBuf, err := os.ReadFile(inFile) 19 if err != nil || len(inBuf) == 0 { 20 return err 21 } 22 23 var iv [16]byte 24 binary.BigEndian.PutUint32(iv[12:], uint32(index)) 25 26 outBuf := make([]byte, len(inBuf)) 27 mode := cipher.NewCBCDecrypter(block, iv[:]) 28 mode.CryptBlocks(outBuf, inBuf) 29 30 pad := int(outBuf[len(outBuf)-1]) 31 _, err = out.Write(outBuf[:len(outBuf)-pad]) 32 return err 33 } 34 //创建输出文件,循环调用解密方法 35 func decodeAesM3u8(key, inPath, outFile string) (int, error) { 36 out, err := os.Create(outFile) 37 if err != nil { 38 return 0, err 39 } 40 defer out.Close() 41 42 ii := 0 43 for i := 1; i <= 1695; i++ { 44 fileName := fmt.Sprintf("index%d.ts", i) 45 err = decodeAES128CBC([]byte(key), i, inPath+fileName, out) 46 if err != nil { 47 return i, err 48 } 49 ii++ 50 } 51 return ii, nil 52 } 53 //合并多个ts文件为一个 54 func mergeM3u8(inPath, outFile string) (int, error) { 55 out, err := os.Create(outFile) 56 if err != nil { 57 return 0, err 58 } 59 defer out.Close() 60 61 for i := 0; ; i++ { 62 in, err := os.Open(inPath + strconv.Itoa(i)) 63 if err != nil { 64 if i == 0 { 65 return 0, err 66 } 67 return i, nil 68 } 69 70 _, err = io.Copy(out, in) 71 if err != nil { 72 _ = in.Close() 73 return i, err 74 } 75 76 err = in.Close() 77 if err != nil { 78 return i, err 79 } 80 } 81 } 82 83 func main() { 84 //存放ts文件的目录 85 inPath := "E:/go/src/video/" 86 //秘钥,就是ts.key里面的那个秘钥 87 key := "CbA619e8DBg3E7E6" 88 //合并之后的文件路径+文件名 89 outFile := "E:/go/src/1695.ts" 90 91 var n int 92 var err error 93 if len(key) == 16 { 94 n, err = decodeAesM3u8(key, inPath, outFile) 95 } else { 96 n, err = mergeM3u8(inPath, outFile) 97 } 98 if err != nil { 99 fmt.Println(err) 100 return 101 } 102 fmt.Println("成功转换", n, "个文件") 103 }
最终得到一个ts文件,是可以正常播放的,然后再使用FFmpeg转成mp4文件就可以了。