m3u8/ts格式的视频文件处理

本文基于python2.7以及适合有一定编程基础的朋友,如果有其他需求请联系博主
首先,假设你已经了解了m3u8类型文件的一些基础知识,如果还没还请自行百度
本文按具体情况分为三个部分:

1 只有m3u8文件,需要下载ts文件
2 有ts文件,但因为被加密无法播放,需要解码
3 ts文件能正常播放,但太多而小,需要合并

1. 下载ts文件

首先,从m3u8文件中获取ts文件的下载地址
如图为一个m3u8文件
m3u8文件处理
观察文件构成可以发现,视频被分割为一个个视频片段并在m3u8文件中存储为相对url的形式,所以我们需要提取出相对url再拼接根url才能进行下载

def get_ts_url(m3u8_path, root_url):
    urls = []
    with open(m3u8_path, 'r') as fhand:
        lines = fhand.readlines()
        for line in lines:
            if line.endswith('.ts\n'):
                urls.append(root_url + line.strip('\n'))
    return urls
  • 函数的参数为m3u8文件的路径和根网址

然后,根据拿到的各个ts文件地址开始进行下载

def download_ts(ts_urls, download_path):
    for i in range(len(ts_urls)):
        ts_url = ts_urls[i]
        file_name = ts_url.split('/')[-1]
        print('start download %s' % file_name)
        start = datetime.datetime.now().replace(microsecond=0)        
        try:
            response = requests.get(ts_url, stream=True)
        except Exception as e:
            print('Error prosessed: %s' % e )
            return 
        ts_path = download_path + '/{0}.ts'.format(i)
        with open(ts_path, 'wb+') as fhand:
            for chunk in response.iter_content(chunk_size=512):
                if chunk:
                    fhand.write(chunk)
        end = datetime.datetime.now().replace(microsecond=0)
        print('Time cost: %s' % (end - start))
  • 直接调用requests库的api进行下载,此外get方法使用了stream模式,即一边写入一边下载,能尽可能避免内存过多的占用
  • 此外添加了计时功能,能知道每个ts文件的下载时间

2. 解码ts文件

考虑对单个ts文件进行解码,先观察m3u8文件结构,是否存在EXT-X-KEY项,如上图有KEY项说明需要进行解码,且说明了加密算法为AES-128,URI项则是密文key的相对网址

首先,下载密文文件key

def download_key(m3u8_path, root_url):
    url = ''
    with open(m3u8_path, 'r') as fhand:
        lines = fhand.readlines()
        for line in lines:
            if line.startswith('#EXT-X-KEY'):#line.endswith('key.key\"/n'):
                print(line)
                url = line.split(',')[-1].split('\"')[-2] # .split('"')[-1]
                break
    key_url = root_url + url   
    file_name = key_url.split('/')[-1]
    res = requests.get(key_url)
    file_path = './'+file_name
    with open(file_path, 'wb') as fhand:
         fhand.write(res.content)
    return file_path    

然后,根据下载好的key文件进行解码

def decode_ts(file_path, f_id):
    raw = file(file_path, 'rb').read()
    iv = raw[0:16]
    data = raw[16:]
    key = file('./key.key', 'rb').read()
    plain_data = AES.new(key, AES.MODE_CBC, iv).decrypt(data)
    path = file_path.split('/')[-2]
    file('./decode_ts/{0}/{1}.ts'.format(path, f_id), 'wb').write(plain_data)
    print('file:' + str(f_id) + '\tsucceed!')
    # print(key)
  • 需要先安装第三方库Crypto才能调用它的api且目前最高只支持python2.7的版本
  • 本例使用CBC模式能够成功解码,如若不行可尝试GCM或其他
  • 若m3u8文件中有IV项需要进行提取,若没有可尝试每个ts文件的前16位(如本例中),再不行则是单独存放在客户端中,则无法上述方法解码

获取ts文件的绝对路径

def file_walker(path):
    file_list = []
    for root, dirs, files in os.walk(path): # a generator
        for fn in files:
            p = str(root+'/'+fn)
            file_list.append(p)
    return file_list
  • 函数参数为ts所在文件夹路径
  • 因为同目录中ts文件有很多个,所以需要先遍历文件夹拿到各个ts文件的地址
  • 可以调用os库的api完成功能,os.walk()是一个生成器,每次调用都会返回三个参数:当前目录、当前目录下的文件夹和当前目录下的文件,若当前目录下除了文件还有其他使用next()函数还能迭代访问
  • 不过在本例中,文件夹中只有ts类型文件,直接拼接根目录和文件名就能得到绝对路径了

最后,封装一下上述两个函数,使用for循环就能全部解码

3. 合并ts文件

def combine(ts_path, combine_path, file_name):
    file_list = file_walker(path)
    file_path = combine_path + path.split('/')[-1] + file_name + '.ts'
    with open(file_path, 'wb+') as fhand:
        for i in range(len(file_list)):
            fhand.write(file(file_list[i], 'rb').read())               
  • 由于ts文件在分割时比较简单粗暴,所以合并时直接按名称顺序读取为二进制文件追加写入到一个文件上即可
  • 最后得到一个完整且能正常播放的ts文件了,如果需要转换为其他格式可以使用一般的转格式软件就行(比如格式工厂)

本文到此结束,如果有什么问题欢迎及时交流。
参考博客:
V2ex破解 AES-128 加密的 m3u8+ts+key 视频

猜你喜欢

转载自blog.csdn.net/Shuang_Mo/article/details/81867261