使用golang来解密m3u8视频播放列表里面的ts文件

发布时间 2023-11-17 17:44:20作者: 第一夫人

如果我有一个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文件就可以了。