[英]PyAV: how to display multiple video streams to the screen at the same time
我刚刚学习使用视频帧和 python 语言的新手。 我需要使用 PyAV 同时在屏幕上显示多个视频流。
下面的代码适用于一台相机。 请帮我在屏幕上显示多个摄像头。 我应该在这段代码中添加或修复什么?
dicOption={'buffer_size':'1024000','rtsp_transport':'tcp','stimeout':'20000000','max_delay':'200000'}
video = av.open("rtsp://viewer:vieweradmin@192.16.5.69:80/1", 'r',format=None,options=dicOption, metadata_errors='nostrict')
try:
for packet in video.demux():
for frame in packet.decode():
if packet.stream.type == 'video':
print(packet)
print(frame)
img = frame.to_ndarray(format='bgr24')
#time.sleep(1)
cv2.imshow("Video", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except KeyboardInterrupt:
pass
cv2.destroyAllWindows()
使用 PyAV 播放多个流是可能的,但并非易事。 主要挑战是同时解码多个流,这在单线程程序中可能需要比视频帧速率所需的更长的时间。 不幸的是线程在这里没有帮助(Python 允许在任何给定时间只有一个线程处于活动状态),因此解决方案是构建一个多进程架构。
我为一个辅助项目创建了下面的代码,它使用 PyAV 和 OpenCV 实现了一个简单的多流视频播放器。 它创建一个单独的后台进程来解码每个 stream,使用队列将帧发送到主进程。 因为队列的大小有限,所以不存在解码器超过主进程的风险——如果在下一个帧准备好时没有检索到帧,它的进程将阻塞,直到主进程赶上。
假定所有流都以相同的帧速率运行。
import av
import cv2
import numpy as np
import logging
from argparse import ArgumentParser
from math import ceil
from multiprocessing import Process, Queue
from time import time
def parseArguments():
r'''Parse command-line arguments.
'''
parser = ArgumentParser(description='Video player that can reproduce multiple files simultaneoulsy')
parser.add_argument('paths', nargs='+', help='Paths to the video files to be played')
parser.add_argument('--resolution', type=int, nargs=2, default=[1920, 1080], help='Resolution of the combined video')
parser.add_argument('--fps', type=int, default=15, help='Frame rate used when playing video contents')
return parser.parse_args()
def decode(path, width, height, queue):
r'''Decode a video and return its frames through a process queue.
Frames are resized to `(width, height)` before returning.
'''
container = av.open(path)
for frame in container.decode(video=0):
# TODO: Keep image ratio when resizing.
image = frame.to_rgb(width=width, height=height).to_ndarray()
queue.put(image)
queue.put(None)
class GridViewer(object):
r'''Interface for displaung video frames in a grid pattern.
'''
def __init__(self, args):
r'''Create a new grid viewer.
'''
size = float(len(args.paths))
self.cols = ceil(size ** 0.5)
self.rows = ceil(size / self.cols)
(width, height) = args.resolution
self.shape = (height, width, 3)
self.cell_width = width // self.cols
self.cell_height = height // self.rows
cv2.namedWindow('Video', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO | cv2.WINDOW_GUI_EXPANDED)
cv2.resizeWindow('Video', width, height)
def update(self, queues):
r'''Query the frame queues and update the viewer.
Return whether all decoders are still active.
'''
grid = np.zeros(self.shape, dtype=np.uint8)
for (k, queue) in enumerate(queues):
image = queue.get()
if image is None:
return False
(i, j) = (k // self.cols, k % self.cols)
(m, n) = image.shape[:2]
a = i * self.cell_height
b = a + m
c = j * self.cell_width
d = c + n
grid[a:b, c:d] = image
grid = cv2.cvtColor(grid, cv2.COLOR_RGB2BGR)
cv2.imshow('Video', grid)
cv2.waitKey(1)
return True
def play(args):
r'''Play multiple video files in a grid interface.
'''
grid = GridViewer(args)
queues = []
processes = []
for path in args.paths:
queues.append(Queue(1))
processes.append(Process(target=decode, args=(path, grid.cell_width, grid.cell_height, queues[-1]), daemon=True))
processes[-1].start()
period = 1.0 / args.fps
t_start = time()
t_frame = 0
while grid.update(queues):
# Spin-lock the thread as necessary to maintain the frame rate.
while t_frame > time() - t_start:
pass
t_frame += period
# Terminate any lingering processes, just in case.
for process in processes:
process.terminate()
def main():
logging.disable(logging.WARNING)
play(parseArguments())
if __name__ == '__main__':
main()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.