簡體   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