python实现m3u8链接文件播放
m3u8文件格式简单说明
维基百科定义:M3U8 是 Unicode 版本的 M3U,用 UTF-8 编码。"M3U" 和 "M3U8" 文件都是苹果公司使用的 HTTP Live Streaming(HLS) 协议格式的基础,这种协议格式可以在 iPhone 和 Macbook 等设备播放。
m3u8 文件实质是一个播放列表(playlist),其可能是一个媒体播放列表(Media Playlist),或者是一个主列表(Master Playlist)。但无论是哪种播放列表,其内部文字使用的都是 utf-8 编码。
当 m3u8 文件作为媒体播放列表(Meida Playlist)时,其内部信息记录的是一系列媒体片段资源,顺序播放该片段资源,即可完整展示多媒体资源。CCTV6直播其格式如下所示:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:35232
#EXT-X-TARGETDURATION:10
#EXTINF:10.000,
cctv6hd-1549272376000.ts
#EXTINF:10.000,
cctv6hd-1549272386000.ts
#EXTINF:10.000,
cctv6hd-1549272396000.ts
#EXTINF:10.000,
cctv6hd-1549272406000.ts
#EXTINF:10.000,
cctv6hd-1549272416000.ts
#EXTINF:10.000,
cctv6hd-1549272426000.ts
对于点播来说,客户端只需按顺序下载上述片段资源,依次进行播放即可。而对于直播来说,客户端需要定时重新请求该 m3u8 文件,看下是否有新的片段数据需要进行下载并播放。
下载ts文件并播放
opencv可以直接解析ts文件,所以基本思想就是不断下载新的ts片段文件,并保存。然后用opencv解析读取视频。
'''
@Author: Angus Cai
@Date: 2019-05-21 20:35:01
@LastEditors: Angus Cai
@LastEditTime: 2019-05-21 21:56:07
@Description: 不断请求下载M3U8文件里的所有片段并播放
'''
# -*- coding: utf-8 -*-
# Created on 2018/3/22
import requests
import os,re
import time
import urllib.request
import cv2
header = {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);'
}
play_list = [] #记录播放列表
def play(url):
download_path = os.getcwd() + "\download"
if not os.path.exists(download_path):
os.mkdir(download_path)
while True:
all_content = requests.get(url).text
# print(all_content)
file_line = all_content.splitlines()
# print(file_line)
if file_line[0] != "#EXTM3U":
raise BaseException(u"非M3U8的链接")
else:
unknow = True
for index, line in enumerate(file_line):
if "EXTINF" in line:
unknow = False
print(file_line[index + 1])
request = urllib.request.Request(file_line[index + 1],headers=header)
response = urllib.request.urlopen(request)
print(response)
html = response.read()
file_name = file_line[index+1].split('/')[-1]
file_name = file_name.split('-')[-1]
print(file_name)
if file_name=="404_0.ts":
print("设备离线!!!!")
continue
with open(download_path + "\\" + file_name, 'wb') as f:
f.write(html)
print(len(html))
# f.flush()
if file_name not in play_list:
play_list.append(file_name)
cap = cv2.VideoCapture(download_path + "\\" + file_name)
while True:
ret, frame = cap.read()
if not ret:
break
cv2.imshow("Test", frame)
time.sleep(1/30)
k = cv2.waitKey(1) & 0xff
if k == 27 : break
cap.release()
# os.remove(download_path + "\\" + file_name)
else:
# os.remove(download_path + "\\" + file_name)
continue
# cap.release
cv2.destroyAllWindows()
if __name__ == '__main__':
play("http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8")
多线程操作实现流畅播放
通过多进程与队列,一个进程下载ts文件并存在本地,同时将文件名放入队列;另一个进程从队列中取出文件名并进行读取。
'''
@Author: Angus Cai
@Date: 2019-05-22 08:23:34
@LastEditors: Angus Cai
@LastEditTime: 2019-05-22 09:49:29
@Description:
'''
import multiprocessing as mp
import cv2
import time
import subprocess as sp
import numpy
import requests
import os,re
import time
import urllib.request
def queue_img_put(q, HD_VIDEO_URL, header, play_list):
download_path = os.getcwd() + "\download"
if not os.path.exists(download_path):
os.mkdir(download_path)
while True:
if q.qsize() <= 100:
all_content = requests.get(HD_VIDEO_URL).text
# print(all_content)
file_line = all_content.splitlines()
# print(file_line)
if file_line[0] != "#EXTM3U":
raise BaseException(u"非M3U8的链接")
else:
unknow = True
for index, line in enumerate(file_line):
if "EXTINF" in line:
unknow = False
# pd_url = url.rsplit("/", 1)[0] + "/" + file_line[index + 1]
# print(file_line[index + 1])
print("http://ivi.bupt.edu.cn/hls/"+file_line[index + 1])
request = urllib.request.Request("http://ivi.bupt.edu.cn/hls/"+file_line[index + 1],headers=header)
# request = urllib.request.Request(file_line[index + 1],headers=header)
response = urllib.request.urlopen(request)
# print(response)
html = response.read()
file_name = file_line[index+1].split('/')[-1]
file_name = file_name.split('-')[-1]
print(file_name)
if file_name=="404_0.ts":
print("设备离线!!!!")
continue
# result_file_name= url_list[i][length:][-10:-3]
# result_file_name= patt.findall(file_name)[0]
# print("正在处理%s"%result_file_name+".ts","共%s/%s项"%(i+1,url_length))
with open(download_path + "\\" + file_name, 'wb') as f:
f.write(html)
print(len(html))
f.flush()
if file_name not in play_list:
play_list.append(file_name)
q.put(download_path + "\\" + file_name)
else:
# os.remove(download_path + "\\" + file_name)
continue
else:
play_list = []
continue
def queue_img_get(q, window_name):
cv2.namedWindow(window_name, flags=cv2.WINDOW_FREERATIO)
# image = cv2.imread("./404.PNG")
while True:
# print("播放。。")
if q.qsize() > 0:
file_name = q.get()
cap = cv2.VideoCapture(file_name)
while True:
ret,frame = cap.read()
if not ret:
break
# detected_image_array, detections = Detector(frame)
cv2.imshow(window_name, frame)
time.sleep(1/30)
k = cv2.waitKey(1) & 0xff
if k == 27 : break
cap.release()
os.remove(file_name)
else:
# print("等待中。。。")
# cv2.imshow(window_name, image)
# k = cv2.waitKey(1) & 0xff
# if k == 27 : break
continue
cv2.destroyAllWindows()
def run(): # single camera
HD_VIDEO_URL = "http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8"
window_name = "CCTV6"
header = {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);'
}
play_list = []
mp.set_start_method(method='spawn') # init
queue = mp.Queue(maxsize=100)
processes = [mp.Process(target=queue_img_put, args=(queue, HD_VIDEO_URL, header, play_list)),
mp.Process(target=queue_img_get, args=(queue, window_name))]
[setattr(process, "daemon", True) for process in processes] # process.daemon = True
[process.start() for process in processes]
[process.join() for process in processes]
if __name__ == '__main__':
run()