使用 Python结合ffmpeg 实现单线程和多线程推流

news2025/1/7 19:35:28

一、引言

在本文中,我们将详细介绍如何使用 Python 进行视频的推流操作。我们将通过两个不同的实现方式,即单线程推流和多线程推流,来展示如何利用 cv2(OpenCV)和 subprocess 等库将视频帧推送到指定的 RTMP 地址。这两种方式都涉及到从摄像头读取视频帧,以及使用 ffmpeg 命令行工具将视频帧进行编码和推流的过程。

二、单线程推流

以下是单线程推流的代码:

import cv2 as cv
import subprocess as sp


def push_stream():
    # 视频读取对象
    cap = cv.VideoCapture(0) 
    fps = int(cap.get(cv.CAP_PROP_FPS))
    w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
    h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
    ret, frame = cap.read()
    # 推流地址
    rtmpUrl = "rtmp://192.168.3.33:1935/live/"
    # 推流参数
    command = ['ffmpeg',
              '-y',
              '-f', 'rawvideo',
              '-vcodec','rawvideo',
              '-pix_fmt', 'bgr24',
              '-s', "{}x{}".format(w, h),
              '-r', str(fps),
              '-i', '-',
              '-c:v', 'libx264',
              '-pix_fmt', 'yuv420p',
              '-preset', 'ultrafast',
              '-f', 'flv', 
              rtmpUrl]
    # 创建、管理子进程
    pipe = sp.Popen(command, stdin=sp.PIPE, bufsize=10 ** 8)
    # 循环读取
    while cap.isOpened():
        # 读取一帧
        ret, frame = cap.read()
        if frame is None:
            print('read frame err!')
            continue
        # 显示一帧
        cv.imshow("frame", frame)
        # 按键退出
        if cv.waitKey(1) & 0xFF == ord('q'):
            break
        # 读取尺寸、推流
        # img=cv.resize(frame,size)
        pipe.stdin.write(frame) 
    # 关闭窗口
    cv.destroyAllWindows()
    # 停止读取
    cap.release()

在这个单线程的实现中,我们执行以下步骤:

  1. 初始化视频读取对象
    • 使用 cv2.VideoCapture(0) 来打开默认的摄像头设备。
    • 获取摄像头的帧率 fps、宽度 w 和高度 h 等参数。
  2. 设置推流地址和参数
    • 定义 rtmpUrl 作为推流的目标地址。
    • 构造 ffmpeg 的命令列表 command,该列表包含了一系列的参数,如 -y 表示覆盖输出文件、-f rawvideo 表示输入格式为原始视频等。
    • 使用 sp.Popen 创建一个子进程,将 ffmpeg 命令作为子进程运行,并且将其输入管道 stdin 连接到我们的程序。
  3. 循环读取和推流
    • 在一个 while 循环中,不断读取摄像头的帧。
    • 若读取失败,打印错误信息并继续。
    • 使用 cv2.imshow 显示当前帧,同时监听 q 键,按下 q 键时退出程序。
    • 将读取到的帧通过管道发送给 ffmpeg 进行推流。

三、多线程推流

以下是多线程推流的代码:

import queue
import threading
import cv2 as cv
import subprocess as sp


class Live(object):
    def __init__(self):
        self.frame_queue = queue.Queue()
        self.command = ""
        # 自行设置
        self.rtmpUrl = ""
        self.camera_path = ""

    def read_frame(self):
        print("开启推流")
        cap = cv.VideoCapture(self.camera_path)

        # Get video information
        fps = int(cap.get(cv.CAP_PROP_FPS))
        width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))

        # ffmpeg command
        self.command = ['ffmpeg',
                       '-y',
                       '-f', 'rawvideo',
                       '-vcodec','rawvideo',
                       '-pix_fmt', 'bgr24',
                       '-s', "{}x{}".format(width, height),
                       '-r', str(fps),
                       '-i', '-',
                       '-c:v', 'libx264',
                       '-pix_fmt', 'yuv420p',
                       '-preset', 'ultrafast',
                       '-f', 'flv', 
                       self.rtmpUrl]

        # read webcamera
        while(cap.isOpened()):
            ret, frame = cap.read()
            if not ret:
                print("Opening camera is failed")
                break

            # put frame into queue
            self.frame_queue.put(frame)

    def push_frame(self):
        # 防止多线程时 command 未被设置
        while True:
            if len(self.command) > 0:
                # 管道配置
                p = sp.Popen(self.command, stdin=sp.PIPE)
                break

        while True:
            if self.frame_queue.empty()!= True:
                frame = self.frame_queue.get()
                # process frame
                # 你处理图片的代码
                # write to pipe
                p.stdin.write(frame.tostring())

    def run(self):
        threads = [
            threading.Thread(target=Live.read_frame, args=(self,)),
            threading.Thread(target=Live.push_frame, args=(self,))
        ]
        [thread.setDaemon(True) for thread in threads]
        [thread.start() for thread in threads]

在这个多线程的实现中,我们使用了 threadingqueue 库:

  1. 类的初始化
    • 创建一个 Live 类,在 __init__ 方法中初始化帧队列 frame_queuecommandrtmpUrlcamera_path 等变量。
  2. 读取帧的线程方法
    • read_frame 方法中,使用 cv2.VideoCapture(self.camera_path) 打开摄像头。
    • 获取摄像头的参数,并构造 ffmpeg 命令。
    • 不断从摄像头读取帧,并将帧放入队列 frame_queue 中。
  3. 推流的线程方法
    • push_frame 方法中,等待 command 被设置,然后使用 sp.Popen 启动 ffmpeg 子进程。
    • 从帧队列中取出帧,并将其写入 ffmpeg 的输入管道进行推流。
  4. 启动线程
    • run 方法创建并启动两个线程,一个用于读取帧,一个用于推流,并且将它们设置为守护线程。

四、代码解释和注意事项

单线程推流

  • 这种方式相对简单,适合初学者理解。但由于是单线程操作,在处理复杂任务时可能会导致性能瓶颈,特别是在同时进行视频显示、读取和推流的情况下,可能会出现卡顿现象。

多线程推流

  • 利用多线程可以将不同的任务分配给不同的线程,提高性能。
  • frame_queue 是一个线程安全的队列,用于在两个线程之间传递帧数据,避免了数据竞争问题。
  • setDaemon(True) 使得线程在主线程结束时自动终止,防止程序无法正常退出。

五、总结

通过上述代码和解释,我们可以看到如何使用 Python 进行单线程和多线程的视频推流操作。单线程代码简单明了,但性能可能受限;多线程代码可以更好地处理高负载,但也需要注意线程安全和资源管理等问题。在实际应用中,我们可以根据具体的需求和硬件性能来选择合适的推流方式。同时,我们可以进一步优化代码,例如添加异常处理、优化帧处理逻辑等,以提高程序的稳定性和性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2272176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[SMARTFORMS] 输出文本变量绑定

在SMARTFORMS表单中的表格接口的"导入"页签处添加导入参数IV_EBELN 参数IV_EBELN为采购凭证编号,并为该参数添加默认值 在SMARTFORMS表单中的全局定义中的"全局数据"页签定义变量 结构ZSPO_HEADER_437、ZSPO_ITEM_437与表类型ZTPO_ITEM_437的定…

【DevOps】Jenkins部署

Jenkins部署 文章目录 Jenkins部署资源列表基础环境一、部署Gilab1.1、安装Gitlab1.2、修改配置文件1.3、加载配置文件1.4、访问Gitlab1.5、修改root登录密码1.6、创建demo测试项目1.7、上传代码1.8、验证上传的代码 二、部署Jenkins所需软件2.1、部署JDK2.2、部署Tomcat2.3、部…

Redis(二)value 的五种常见数据类型简述

目录 一、string(字符串) 1、raw 2、int 3、embstr 二、hash(哈希表) 1、hashtable 2、ziplist 三、list(列表) ​编辑 1、linkedlist 2、ziplist 3、quicklist(redis 3.2后的列表内…

PyQt实战——将pcm文本数据转换成.pcm的二进制文件

系类往期文章: PyQt5实战——多脚本集合包,前言与环境配置(一) PyQt5实战——多脚本集合包,UI以及工程布局(二) PyQt5实战——多脚本集合包,程序入口QMainWindow(三&…

Word2Vec解读

Word2Vec: 一种词向量的训练方法 简单地讲,Word2Vec是建模了一个单词预测的任务,通过这个任务来学习词向量。假设有这样一句话Pineapples are spiked and yellow,现在假设spiked这个单词被删掉了,现在要预测这个位置原本的单词是…

STM32F1学习——编码器接口

一、编码器接口 编码器接口可以接收正交编码器的信号,根据编码器旋转产生的正交信号脉冲,通过硬件自动控制CNT值的自增或自减,从而指出编码器的位置、旋转方向和旋转速度。 每个高级定时器和通用定时器都有一个编码器接口,他们会占…

什么是网关路由

1.认识网关 网关(Gateway)和路由(Router)是两个相关但不同的概念。 一、网关(Gateway) 定义 网关是一个网络节点,它充当了不同网络之间的连接点。可以将其看作是一个网络的 “大门”&#xf…

S32K144 UDSdoCAN 升级刷写实现笔记

文章目录 1. 摘要2. 开发环境搭建2.1 开发板2.2 IDE 安装2.3 更新扩展包2.4 烧录仿真测试2.4.1 新建工程2.4.2 导入已有工程2.4.3 编译工程2.4.4 硬件连接2.4.5 Debug2.4.6 添加 .c .h 文件2.5 串口配置2.5.1 时钟2.5.2 GPIO2.5.3 定时器2.5.4 uart 工程2.5.5 烧录验证3. 升级原…

第十八周:Faster R-CNN论文阅读

Faster R-CNN论文阅读 摘要Abstract文章简介1. 引言2. Faster R-CNN 框架2.1 RPN2.2 损失函数2.3 RPN的训练细节 3. Faster R-CNN的训练4. 优缺点分析总结 摘要 本篇博客介绍了 Faster R-CNN,这是一种双阶段的目标检测网络,是对 Fast R-CNN 的改进。为了…

Day28下 - 大模型微调:酒店评论情感分析

一、前置准备 1. 下载 LLaMA Factory https://github.com/hiyouga/LLaMA-Factory.git 搭建过程详见:https://blog.csdn.net/CSBLOG/article/details/144584581 2. 选择 预训练模型 和 prompt指令模型 预训练阶段在实际工作中,一般是用不上的&#xff…

【网络安全 | 漏洞挖掘】JS Review + GraphQL滥用实现管理面板访问

未经许可,不得转载。 正文 在映射目标范围后,我发现了一个用于管理的控制台界面,但没有注册功能。 于是我开始尝试: 1、模糊测试注册端点 -> 失败 2、在请求中将登录替换为注册 -> 再次失败 尝试均未奏效后,我决定冷静下来,重新思考方法并利用技术手段。 我观察…

数据库管理-第278期 开年综艺,第七届中国PG数据库生态大会有感(20250105)

数据库管理278期 20245-01-05 数据库管理-第278期 开年综艺,第七届中国PG数据库生态大会有感(20250105)1 走后门的可观测性2 社区VS商业3 从O来到PG去4 现场集锦5 IF CLUB社区总结 数据库管理-第278期 开年综艺,第七届中国PG数据库…

Docker 安装Elasticsearch搜索引擎 搜索优化 词库挂载 拼音分词 插件安装

介绍 允许用户快速索引和搜索大量的文本数据。通过使用倒排索引,它能够在海量数据中高效检索相关信息。提供灵活的查询语言,可以做全文搜索、模糊搜索、数据统计等,用来代替MYSQL的模糊搜索,MYSQL的模糊搜索不支持使用索引从而导…

NVR小程序接入平台EasyNVR使用FFmpeg取流时提示错误是什么原因呢?

在视频监控系统中,FFmpeg常用于从各种源(如摄像头、文件、网络流等)获取流媒体数据,这个过程通常称为“取流”。 在EasyNVR平台中,使用FFmpeg取流是一种常见的操作。FFmpeg作为一款强大的开源多媒体处理工具&#xff…

【电源专题】为什么测试电源的SW波形上冲振荡之前的0V电位要先来个小的下降

在同步电源的开关节点SW波形测试中,你可能会发现周期性的SW波形在上升前的一小段时间时间内会有一个小小的下跌,这个下跌会低于0V。那么这个下跌是怎么来的呢? 如下所示为某降压转换器的SW开关节点波形: 其展开后可以看到在上升之前有20ns左右的时间,SW电压是下跌…

基于EB和S32DS3.5建立基础工程

本文参考: https://blog.csdn.net/weixin_41660366/article/details/141949690 https://blog.csdn.net/zhoujingCSDN/article/details/142284796 0、简介 本文基于S32K312 介绍新建工程并引入EB,环境如下: MCU:NXP S32k312 RT…

如何申请LabVIEW软件著作权?

申请 软件著作权 时,若你的单位开发的应用是基于 LabVIEW 的图形化编程语言,你需要将 LabVIEW 程序中的图形化设计转换为源代码形式,以符合软件著作权申请的要求。由于LabVIEW本身是图形化编程语言,而不是传统的文本编程语言&…

C/C++中new/delete与malloc/free的区别及对象管理

C/C++中new/delete与malloc/free的区别及对象管理 在C/C++编程中,动态内存管理是一个核心且复杂的话题,其中new、delete、malloc和free是四个经常用于此目的的工具。尽管它们都涉及到内存的分配和释放,但它们在处理对象时的方式和效果却大相径庭。本文将通过示例来说明这些工…

GitHub 图像修复开源项目推荐【持续更新】

GFPGAN 介绍:GFPGAN(Generative Facial Prior-GAN)是由腾讯ARC(Applied Research Center)开发的一种实用的真实世界人脸修复算法。它专门设计用于人脸图像的生成和优化,尤其在低质量人脸图像的超分辨率恢复…

JWT认证实战

JWT(JSON Web Token)是一种轻量级的、基于 JSON 的开放标准(RFC 7519),用于在各方之间安全地传递信息。JWT 的特点是结构简单、轻量化和跨平台支持,适用于用户身份验证、信息加密以及无状态的 API 访问控制…