视频流解码方法:
opencv解码、ffmpeg解码、DeFFcode解码
使用前:需要安装opencv-python、ffmpeg-python、DeFFcode等库
1、opencv解码视频流
#导入opencv库
import cv2 as cv
video_path = "video.mp4" # 读取视频路径
#解码前,使用cv.VideoCapture打开视频文件,并创建一个视频对象
capture = cv.VideoCapture(video_path)
#查看视频文件的宽、高、帧数率以及总帧数
width = int(capture.get(cv.CAP_PROP_FRAME_WIDTH)) # 360
height = int(capture.get(cv.CAP_PROP_FRAME_HEIGHT)) # 480
fps = round(capture.get(cv.CAP_PROP_FPS)) # 30
frameCount = int(capture.get(cv.CAP_PROP_FRAME_COUNT)) # 2313
print(height, width, fps, frameCount)
# 创建写入的视频对象(读取视频,并保存成新的视频)
# fourcc = cv.VideoWriter_fourcc('X', 'V', 'I', 'D') # 编码器设置 XVID
fourcc = cv.VideoWriter_fourcc(*'XVID') # 'X','V','I','D' 简写为 *'XVID'
vedioWrite = "video_1.avi" # 写入视频文件的路径
capWrite = cv.VideoWriter(vedioWrite, fourcc, fps, (width, height), True)
frame_count = 0 #视频帧数初值
timed = 231 #抽帧间隔
timed_1=12 #抽帧间隔
#检查读取是否成功
while capture.isOpened():
#使用read方法逐帧解码,每次返回两个值:读取是否成功的bool值,一个表示当前帧数据的numpy数组
ret, frame = capture.read() # 读取一帧
if not ret:
break
if frame:
frame_count += 1
#解码后的帧数据,是以numpy数组的形式呈现的
# 设置保存路径和文件名(可以根据需要进行更改)
# 为你的图片取名格式化
save_path = "save/{}.jpg".format(frame_count)
#抽帧条件,并保存一帧为一张图片
if(frame_count%timed==0):
cv.imwrite(save_path, frame)
#抽帧条件,并把当前帧写入到新的视频文件对象
if (frame_count%timed_1==0):
capWrite.write(frame)
if cv.waitKey(10) & 0xFF == ord('q'): # 按 'q' 退出
break
else:
print("Can't receive frame at frameNum {}".format(frame_count))
break
capture.release() # 关闭读取视频文件
capWrite.release() # 关闭视频写入对象
cv.destroyAllWindows() # 关闭显示窗口
2、ffmpeg解码视频流
import ffmpeg
video_path = 'video.mp4'
# 获取视频的详细信息,编码格式,名字,尺寸...
probe = ffmpeg.probe(video_path)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
# 获取视频持续时间
duration = float(video_stream['duration'])
# 视频解码
out, _ = (
ffmpeg
.input(video_path)
.output('pipe:', format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, video_stream['height'], video_stream['width'], 3])
)
针对ffmpeg解码视频流,可以通过ffmpeg.probe得到视频的信息。通过以下代码得到视频每个字段的信息。
info = ffmpeg.probe(str(file))
vs = next(c for c in info['streams'] if c['codec_type'] == 'video')
duration_secs = float(vs['duration'])
format = info['format']['format_name']
codec_name = vs['codec_name']
width = vs['width']
height = vs['height']
num_frames = vs['nb_frames']
以下ffmpeg部分参考:https://cloud.tencent.com/developer/article/1568673
ffmpeg对视频流不同处理:
import ffmpeg
# 全抽帧
def get_frames():
input_file = '/Users/didi/Desktop/ffmpeg/test.mp4'
output_file = '/Users/didi/Desktop/ffmpeg/image/image-%5d.jpg'
out, err = (
ffmpeg
.input(input_file)
.output(output_file)
.run(quiet=False, overwrite_output=True)
)
if out == b'':
print('do nothing')
# 按一定的频率抽帧
def get_frames_by_rate():
input_file = '/Users/didi/Desktop/ffmpeg/test.mp4'
output_file = '/Users/didi/Desktop/ffmpeg/image/image-%5d.jpg'
out, err = (
ffmpeg
.input(input_file, ss=0)
# .output(output_file, r='1', f='image2')
.output(output_file, vf='fps=fps=1', f='image2')
.run(quiet=False, overwrite_output=True)
)
if out == b'':
print('do nothing')
# 按指定时间片段抽帧
def get_frames_by_times():
times = [1, 5, 8, 10]
for time in times:
input_file = '/Users/didi/Desktop/ffmpeg/test.mp4'
output_file = '/Users/didi/Desktop/ffmpeg/image/image-' + str(time) + '.jpg'
out, err = (
ffmpeg
.input(input_file, ss=time)
.output(output_file, vframes='1', f='image2')
.run(quiet=False, overwrite_output=True)
)
if out == b'':
print('do nothing')
if __name__ == '__main__':
get_frames_by_rate()
import ffmpeg
# 视频剪切
def cut_videos():
input_file = '/Users/didi/Desktop/ffmpeg/test.mp4'
output_file = '/Users/didi/Desktop/ffmpeg/video/video1.mp4'
out, err = (
ffmpeg
# 注意ss,t的单位都是秒
.input(input_file, ss=220, t=20)
.output(output_file, codec="copy")
.run(quiet=False, overwrite_output=True)
)
if out == b'':
print('do nothing')
# 视频合并
def concat_video():
# 注意:要合并的视频文件和txt文件需要放在同一个目录下
input_file = '/Users/didi/Desktop/ffmpeg/video/aa.txt'
output_file = '/Users/didi/Desktop/ffmpeg/video/Cam.mp4'
try:
out, err = (
ffmpeg
.input(input_file, f='concat')
.output(output_file, c='copy')
.run(quiet=False, overwrite_output=True)
)
except Exception as e:
print(e)
if __name__ == '__main__':
concat_video()
#cut_videos()
3、DeFFcode解码
DeFFcode核心功能就是利用ffmpeg进行视频解码。(功能还待完善)
# FFedecoder创建视频源和视频解码规则,formulate在ffmpeg中执行语句
# 本地视频
# decoder = FFdecoder("test.mp4").formulate()
# rtsp流
decoder = FFdecoder("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4").formulate()
# 从decoder中抓取RGB图像
for frame in decoder.generateFrame():
print(frame.shape)
# 将rgb图像转换为bgr图像,送给opencv展示
frame_bgr = frame[:, :, ::-1]
cv2.imshow("Output Frame", frame_bgr)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# 安全关闭解码进程
decoder.terminate()