首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PyAV:如何同时向屏幕显示多个视频流

PyAV:如何同时向屏幕显示多个视频流
EN

Stack Overflow用户
提问于 2020-11-25 20:15:06
回答 1查看 386关注 0票数 0

我刚刚开始学习视频帧和python语言的新手。我需要使用PyAV同时在屏幕上显示多个视频流。

下面的代码只适用于一个摄像头。请帮我在屏幕上显示多个摄像头。我应该在这段代码中添加或修复什么?

代码语言:javascript
复制
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()
EN

回答 1

Stack Overflow用户

发布于 2021-07-28 22:04:06

使用PyAV播放多个流是可能的,但不是微不足道的。主要的挑战是同时解码多个流,这在单线程程序中可能需要比视频的帧率要求更长的时间。不幸的是,线程在这里没有帮助(Python在任何给定时间只允许一个线程处于活动状态),因此解决方案是构建一个多进程架构。

我为一个side project创建了下面的代码,它使用PyAV和OpenCV实现了一个简单的多流视频播放器。它创建一个单独的后台进程来解码每个流,使用队列将帧发送到主进程。因为队列的大小有限,所以解码器没有超过主进程的风险-如果在下一个准备好的时候没有检索到帧,那么它的进程将阻塞,直到主进程赶上为止。

假设所有流都以相同的帧速率运行。

代码语言:javascript
复制
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()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65004533

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档