首页 > 编程问答 >ffmpeg python 导致死锁

ffmpeg python 导致死锁

时间:2024-08-01 07:47:25浏览次数:6  
标签:python opencv ffmpeg subprocess ffmpeg-python

我在使用 ffmpeg python 处理相机帧时遇到问题。我使用 process.communicate() 的第一种方法效果很好,但存在延迟问题。

process = (
            ffmpeg
            .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height))
            .filter(<filter_params>)
            .output('pipe:', format='rawvideo', pix_fmt='rgb24')
            .run_async(pipe_stdin=True, pipe_stdout=True, pipe_stderr=True)
        )
out, err = process.communicate(input=img.tobytes())
output_image = np.frombuffer(out, np.uint8).reshape((height, width, channels))

为了减少延迟,我尝试保持 ffmpeg 进程打开并输入相机帧进行处理。这可以正常运行几分钟,延迟值可以接受,但最终会陷入死锁。解决这个问题的最佳方法是什么?

import cv2
import numpy as np
import math

import ffmpeg 

def start_ffmpeg_process_async(width, height):
    return (
        ffmpeg
            .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height))
            .filter('<filter variables>')
            .output('pipe:', format='rawvideo', pix_fmt='rgb24')
            .run_async(pipe_stdin=True, pipe_stdout=True, pipe_stderr=True)
    )

def main():

    cap = cv2.VideoCapture(0)

    if not cap.isOpened():
        print("Camera can't open \nExiting...")
        return -1

    ffmpeg_process_async = start_ffmpeg_process_async(cap.get(cv2.CAP_PROP_FRAME_WIDTH), 
        cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
 
    try:
        while True:

            success, img = cap.read() 
            if success:
                height, width, channels = img.shape
                ffmpeg_process_async.stdin.write(img.tobytes())
                raw_output = ffmpeg_process_async.stdout.read(width * height * channels)
                output_image = np.frombuffer(raw_output, np.uint8).reshape((height, width, channels))
                cv2.imshow('Webcam', output_image)
            else:
                print ("Camera read error")
            if cv2.waitKey(1) == ord('q'):
                print ("Exiting . . .")
                break
    finally:
        print ("Finalizing . . .")
        ffmpeg_process_async.stdin.close()
        ffmpeg_process_async.wait()
        cap.release()
        cv2.destroyAllWindows()


if __name__ == "__main__":
    main()

代码中出现的死锁问题是由于 ffmpeg_process_async.stdout.read(width * height * channels) 引起的。这个函数会尝试从 ffmpeg 进程的标准输出中读取指定字节数的数据,如果读取不到指定字节数,它会一直阻塞,直到读取到为止或者进程结束。

问题分析:

  • FFmpeg 输出不固定: FFmpeg 滤镜处理后的视频帧大小可能不是始终等于 width * height * channels 。这会导致 read() 函数一直等待接收足够的数据,从而造成阻塞。
  • 循环读取: 代码在循环中不断读取 FFmpeg 的输出,而没有机制保证 FFmpeg 已经输出了足够的数据。

解决方案:

为了解决这个问题,我们需要确保读取 FFmpeg 输出的操作不会阻塞主线程。以下是一些可行的方案:

1. 使用非阻塞读取:

可以将 ffmpeg_process_async.stdout 设置为非阻塞模式,这样 read() 函数就不会阻塞。

```python import fcntl import os

# ...

flags = fcntl.fcntl(ffmpeg_process_async.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(ffmpeg_process_async.stdout.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)

# ...

raw_output = ffmpeg_process_async.stdout.read(width * height * channels) if raw_output: # 处理输出图像 ```

需要注意的是,非阻塞读取可能会返回空数据,需要添加判断逻辑来处理这种情况。

2. 使用队列:

可以使用队列来异步处理 FFmpeg 的输出。创建一个单独的线程用于读取 FFmpeg 的输出并将其放入队列中,主线程则从队列中获取数据。

```python import queue

# ...

output_queue = queue.Queue()

def read_output(process, queue): while True: data = process.stdout.read(1024) if not data: break queue.put(data)

# 创建读取线程 read_thread = threading.Thread(target=read_output, args=(ffmpeg_process_async, output_queue)) read_thread.start()

# ...

try: while True: # ... 处理图像 ...

       try:
           raw_output = output_queue.get_nowait()  # 从队列中获取数据
           # 处理输出图像
       except queue.Empty:
           pass  # 处理队列为空的情况

# ... ```

3. 使用 subprocess.PIPE poll() :

可以使用 `subprocess.PIPE` 创建管道,并使用 `poll()` 方法检查是否有数据可读,避免阻塞。

```python
import subprocess
import time

# ...

ffmpeg_process_async = subprocess.Popen(
    ffmpeg_command,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

# ...

while True:
    # ... 处理图像 ...

    if ffmpeg_process_async.poll() is not None:
        print("FFmpeg process exited unexpectedly")
        break

    if ffmpeg_process_async.stdout.readable():
        raw_output = ffmpeg_process_async.stdout.read(width * height * channels)
        # 处理输出图像

    time.sleep(0.01)  # 避免 CPU 占用过高

# ...
```

选择哪种方案取决于具体的需求和代码结构。建议先尝试使用非阻塞读取或队列,如果效果不理想,再考虑使用 subprocess.PIPE poll()

标签:python,opencv,ffmpeg,subprocess,ffmpeg-python
From: 78817335

相关文章

  • 将 HTTP 分块编码数据流代码片段从 Node.js 转换为 Python
    我有一个Node.js客户端代码,它将请求发送到HTTP服务器,然后连续接收分块编码数据。这是带有一些流量数据输出的Node.js代码。consthttp=require('http');constoptions={hostname:'...',path:'...',port:...,...};constreq=http.request(......
  • GitHub Actions 工作流程中的 moviepy 安装错误:subprocess-exited-with-error
    我尝试在GitHubActions工作流程中安装moviepy时遇到错误。在我的本地机器上安装工作正常,但在CI环境中有时会失败。该错误消息表明获取构建轮子的要求未成功运行,退出代码为1。它还提到该错误源自子进程,并且可能不是pip的问题。Downloadingmoviepy-1.0.3.tar.gz(388......
  • vsc python 调试器和 pylance 无法识别已安装的包
    我最近使用snowflake-connector-python在我的虚拟环境中安装了pipinstallsnowflake-connector-python[pandas]==2.7.6,当我在激活虚拟环境的情况下从命令行运行我的脚本时,它工作正常。我设置了与VSC解释器相同的虚拟环境,但尝试运行python调试器会引发异常......
  • 如何从python读取matlab持续时间对象
    我创建一个matlab持续时间对象并将其保存到.mat文件:timeend=seconds(123);save('time.mat',timeend,'-v7.3');然后我从python读取它:withh5py.File('time.mat','r')asf:var=f['timeend'][:]print(list(var))......
  • 通过 python 连接到 Snowflake 时出错“UnpicklingError: invalid load key, '\x00'
    我在使用snowflake.connector.connect通过python连接到snowflake时遇到以下错误importsnowflake.connector#pipinstallsnowflake-connector-python#iamgettingtheenvfrom.envfileistoredlocallycnx=snowflake.connector.connect(user=os.getenv('USER'),pass......
  • Python Selenium 单击 webdriverwait 与 find_element
    我无法理解这两个代码块之间的区别。发送点击在webdriverwait和find_elements中都有效。代码1fromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.suppo......
  • Python 问题 如何创建在 PDF 中注册为剪切线的专色?
    我正在开发一个项目,需要我在图像周围创建一条剪切线,但在任何RIP程序(例如Versaworks或Flexi)上将其注册为实际剪切线时遇到困难。我尝试了很多不同的方法python库可以帮助解决这个问题,但我无法让它工作。我希望它像我们在Illustrator中所做的那样,创建一条名为CutConto......
  • 使用Python时如何避免`setattr`(和`getattr`)?以及是否有必要避免
    如果我想向协议缓冲区中的字段添加一个在编译时未知的值,我目前正在做setattr我通常不喜欢使用setattr,因为它看起来不太安全。但是当我知道该对象是protobuf时,我认为这很好,因为我设置它的值必须是protobuf允许的类型。所以也许它并不是真的不安全?让我举......
  • Java sshtools 生成的 EDDSA 签名与 Python 的 pycryptome 生成的签名不匹配
    我有一个python库,它使用pycryptodomelibrary使用openssh格式的ED25519私钥使用Ed25519算法对数据进行签名。然后需要使用sshtools库和相应的公钥在Java应用程序中验证签名。但是签名验证失败。约束:从文件中读取私钥/公钥很重要。我无法......
  • Elastic python请求超时错误:池达到最大大小,不允许更多连接
    我正在使用Elasticsearchpython模块。我正在尝试像这样建立到服务器的连接es=Elasticsearch([config.endpoint],api_key=config.key,request_timeout=config.request_timeout)服务器连接,然后我尝试执行丰富策略。es.enr......