通过python消费者和生产者队列,实现视频流逐帧播放

通过python消费者和生产者队列,实现视频流逐帧播放

参考

https://blog.csdn.net/Int93/article/details/78804918

原理

通过opencv读入RTSP或RTMP流,采用消费者-生产者模型,通过生产者线程,每次读入一个视频帧(生产)存入queue队列,再通过消费者线程对视频帧进行展示(消费)。

代码(即时停止)

import copy
from queue import Queue, Empty
import threading
import cv2

# 定义一个共享的退出标志
exit_flag = False


class FrameProducer(threading.Thread):
    def __init__(self, frame_queue, url):
        super().__init__()
        self.frame_queue = frame_queue
        self.url = url
        self._cap = None
        self.daemon = True

    def run(self):
        global exit_flag
        log('in producer')
        self._cap = cv2.VideoCapture(self.url)
        while not exit_flag and self._cap.isOpened():
            ret, image = self._cap.read()
            log(f'get frame = {ret}')
            if ret:
                self.frame_queue.put(image)
            else:
                self._cap.release()
                self._cap = cv2.VideoCapture(self.url)


class FrameConsumer(threading.Thread):
    def __init__(self, frame_queue):
        super().__init__()
        self.frame_queue = frame_queue
        self.daemon = True

    def run(self):
        global exit_flag
        log('in consumer')
        while not exit_flag:
            try:
                log(f'frame_queue size= {self.frame_queue.qsize()}')
                frame = self.frame_queue.get(timeout=1)  # 非阻塞地尝试从队列中获取帧
                if frame is None:  # 如果队列为空,则退出循环
                    break
                cv2.imshow('cap video', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):  # 检查用户是否在opencv窗口中最近的 1 毫秒内按下了某个键,按下 'q' 键退出
                    self.frame_queue.put(frame)  # 将帧放回队列
                    self.frame_queue.task_done()
                    exit_flag = True
            except Empty:
                pass  # 队列为空,不做任何操作


def log(message):
    """线程安全的日志输出函数"""
    with logging_lock:
        print(message)


if __name__ == '__main__':
    logging_lock = threading.Lock()
    frame_queue = Queue(maxsize=60)
    url = 'rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid'
    producer = FrameProducer(frame_queue, url)
    producer.start()

    consumer = FrameConsumer(frame_queue)
    consumer.start()

    producer.join()
    consumer.join()
    log("程序已结束")

代码(确保队列中所有帧播放完再停止)

from queue import Queue, Empty
import threading
import cv2

# 定义一个共享的退出标志
exit_flag = False
logging_lock = threading.Lock()


class FrameProducer(threading.Thread):
    def __init__(self, frame_queue, url):
        super().__init__()
        self.frame_queue = frame_queue
        self.url = url
        self.video = None
        self.daemon = True

    def run(self):
        global exit_flag
        log('in producer')
        self.video = cv2.VideoCapture(self.url)
        while not exit_flag and self.video.isOpened():
            ret, image = self.video.read()
            log(f'get frame = {ret}')
            if ret:
                self.frame_queue.put(image)
        self.video.release()


class FrameConsumer(threading.Thread):
    def __init__(self, frame_queue):
        super().__init__()
        self.frame_queue = frame_queue
        self.daemon = True

    def run(self):
        global exit_flag
        log('in consumer')
        while not exit_flag or not self.frame_queue.empty():
            try:
                log(f'frame_queue size= {self.frame_queue.qsize()}')
                frame = self.frame_queue.get(timeout=1)  # 非阻塞地尝试从队列中获取帧
                if frame is None:  # 如果队列为空,则退出循环
                    break
                cv2.imshow('cap video', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):  # 检查用户是否在opencv窗口中最近的 1 毫秒内按下了某个键,按下 'q' 键退出
                    exit_flag = True
            except Empty:
                pass  # 队列为空,不做任何操作


def log(message):
    """线程安全的日志输出函数"""
    with logging_lock:
        print(message)


def main(url: str = 'rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid'):
    frame_queue = Queue(maxsize=60)
    producer = FrameProducer(frame_queue, url)
    producer.start()

    consumer = FrameConsumer(frame_queue)
    consumer.start()

    producer.join()
    consumer.join()
    log(f'frame_queue size= {frame_queue.qsize()}')
    frame_queue.task_done()
    log("程序已结束")

if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深|码|洞|悉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值