繁体   English   中英

PyAV:如何同时在屏幕上显示多个视频流

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM