爬虫实战案例《抓取视频》之FFmpeg的使用(七)
Python合并ts文件至mp4格式及解密教程详解
-
m3u8是什么格式?m3u8是苹果公司推出的视频播放标准,是m3u的一种,只是编码格式采用的是UTF-8。
-
使用m3u8格式文件主要因为可以实现多码率视频的适配,视频网站可以根据用户的网络带宽情况,自动为客户端匹配一个合适的码率文件进行播放,从而保证视频的流畅度。
-
m3u8准确来说是一种索引文件,使用m3u8文件实际上是通过它来解析对应的放在服务器上的视频网络地址,从而实现在线播放。
-
它将视频切割成一小段一小段的ts格式的视频文件,然后存在服务器中(现在为了减少I/o访问次数,一般存在服务器的内存中),通过m3u8解析出来路径,然后去请求。
-
合并 ts 文件其实有很多种方法,有一些教程直接使用 cmd 的 copy 命令直接合并 ts 文件:
copy /b movie*.ts movie_new.ts
-
这个方法虽然可以合并,但是无法转化为 mp4 格式,而且也有可能出现视频缺损的情况。因此本文将讲解如何使用 ffmpeg 合并 ts 文件为mp4格式,使用 ffmpeg 也能有效防止视频出现缺损的问题。
1.准备
- 开始之前,你要确保Python和pip已经成功安装在电脑上
- 如果你用Python的目的是数据分析,可以直接安装Anaconda
安装ffmpeg
-
Mac (打开终端(Terminal), 用 homebrew 安装):
brew install ffmpeg --with-libvorbis --with-sdl2 --with-theora
-
Linux:
apt-get install ffmpeg libavcodec-extra
-
Windows:
- 进入 http://ffmpeg.org/download.html#build-windows,点击 windows 对应的图标,进入下载界面点击 download 下载按钮,
- 解压下载好的zip文件到指定目录
- 将解压后的文件目录中 bin 目录(包含 ffmpeg.exe )添加进 path 环境变量中
- DOS 命令行输入 ffmpeg -version, 出现以下界面说明安装完成:
2. 简单合并ts文件
-
使用 ffmpeg 合并一些 ts 文件非常简单,你只需要在终端输入一行命令即可:
ffmpeg -f concat -safe 0 -i file_list.txt -c copy movie.mp4
-
其中 file_list.txt 为如下格式文本文件:
file './路径/1.ts'
file './路径/2.ts'
file './路径/3.ts' -
我们可以用 Python 脚本生成这个 file_list.txt:
目录结构
project/
new_ts/ 存储ts文件的目录
1.ts
2.ts ...
file_list.txt
main.py
main.py
def merge(filePath, filename='output'): ''' 这种合并会有视频和音频对应不上的问题 进行ts文件合并 :param filePath: :return: ''' # 根据当前文件名称的数字进行排序 [1.ts, 2.ts...] file_list = sorted(os.listdir(filePath), key=lambda x: int(x.split('.')[0])) # 排序后写入到文件中 with open("./file_list.txt", "w") as f: for file in file_list: # 格式为 file ./new_ts/1.ts ... f.write("file '{}/{}'\n".format(filePath, file)) cmd = f'ffmpeg -f concat -safe 0 -i file_list.txt -c copy {filename}.mp4' os.system(cmd)
执行完就当前代码就执行完了合并
-
注意:以上代码合并后的视频可能会出现视频和音频对不上的问题 如果出现这样的问题 使用下面的方式进行处理
合并代码
def merge(filePath, filename='output'): ''' 进行ts文件合并 解决视频音频不同步的问题 建议使用这种 :param filePath: :return: ''' # 根据当前文件名称的数字进行排序 file_list = sorted(os.listdir(filePath), key=lambda x: int(x.split('.')[0])) # ./new_ts/1.ts|./new_ts/2.ts ... Str = '|'.join([f'{filePath}/{i}' for i in file_list]).strip('|') cmd = f'ffmpeg -i "concat:{Str}" -c copy -bsf:a aac_adtstoasc -movflags +faststart {filename}.mp4' os.system(cmd)
3. 解密处理
-
上面我们讲的是没有经过加密的 ts 文件,这些文件下载后直接可以播放,但经过AES-128加密后的文件下载后会无法播放,所以还需要进行解密。
-
如何判断是否需要加密?观察视频网站是否有m3u8的文件传输,下载下来并打开:
无需解密index.m3u8文件
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:4 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:3.086, https://hey05.cjkypo.com/20211215/FMbNtNzz/1100kb/hls/7qs6gJc0.ts #EXTINF:2.085, https://hey05.cjkypo.com/20211215/FMbNtNzz/1100kb/hls/rYpHhq0I.ts #EXTINF:2.085, https://hey05.cjkypo.com/20211215/FMbNtNzz/1100kb/hls/bfays5sw.ts
需要解密index.m3u8文件
#EXT-X-VERSION:3 #EXT-X-TARGETDURATION:1 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHOD=AES-128,URI="/20220418/671fJxOB/2000kb/hls/key.key" # 当前路径为解密秘钥的位置 需要使用代码拼凑成完整路径 进行请求 域名+/20220418/671fJxOB/2000kb/hls/key.key #EXTINF:1.235, /20220418/671fJxOB/2000kb/hls/kj6uqHoP.ts # 并且这里ts的url也要拼凑完整 #EXTINF:1.001, /20220418/671fJxOB/2000kb/hls/ZXX8LYPa.ts #EXTINF:1.001, /20220418/671fJxOB/2000kb/hls/sOezpD2H.ts #EXTINF:1.001, ...
-
如果你的文件是加密的,那么你还需要一个key文件,Key文件下载的方法和m3u8文件类似,如下所示 key.key 就是我们需要下载的 key 文件,并注意这里 m3u8 有2个,需要使用的是像上面一样存在 ts 文件超链接的 m3u8 文件:
-
下载所有 ts 文件,将下载好的所有的 ts 文件、m3u8、key.key 放到一个文件夹中,将 m3u8 文件改名为 index.m3u8,将 key.key 改名为 key.m3u8 。更改 index.m3u8 里的 URL,变为你本地路径的 key 文件,将所有 ts 也改为你本地的路径
文件路径
project/
ts/
0.ts
1.ts
...
index.m3u8
key.m3u8
修改后的index.m3u8内容如下所示:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:1 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHOD=AES-128,URI="/Users/xialigang/PycharmProjects/爬虫/抓取带秘钥的电影/ts/key.m3u8" #EXTINF:1.235, /Users/xialigang/PycharmProjects/爬虫/抓取带秘钥的电影/ts/0.ts #EXTINF:1.001, /Users/xialigang/PycharmProjects/爬虫/抓取带秘钥的电影/ts/1.ts #EXTINF:1.001, /Users/xialigang/PycharmProjects/爬虫/抓取带秘钥的电影/ts/2.ts
处理index.m3u8内容的代码如下所示
def do_m3u8_url(path): if not os.path.exists(path): os.mkdir(path) with open("index.m3u8", mode="r", encoding="utf-8") as f: data = f.readlines() # 修改后重新写入 fw = open('index.m3u8', 'w') i = 0 for line in data: # 如果不是url 则走下次循环 if line.startswith("#"): fw.write(line) else: fw.write(f'/Users/xialigang/PycharmProjects/爬虫/抓取带秘钥的电影/{path}/{i}.ts\n') print(line, i) i += 1
-
然后用ffmpeg进行合并:
ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy new.mp4
代码合并
os.chdir("./ts") os.system('ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy movie.mp4')
-
这样就大功告成了!我们成功解密并使用 ffmpeg 合并了这些 ts 视频片段,实际应用场景可能和这不一样,具体网站具体分析