【问题标题】:Python gets stuck at pipe.stdin.write(image.tostring())Python 卡在 pipe.stdin.write(image.tostring())
【发布时间】:2020-06-21 16:39:13
【问题描述】:

我正在阅读每一帧视频并为其添加时间戳,如下所示。

command = ['ffmpeg',
            '-y', # (optional) overwrite output file if it exists
            '-f', 'rawvideo', #Input is raw video
            '-pix_fmt', 'bgr24', #Raw video format
            '-s', str(int(width)) + 'x' + str(int(height)), # size of one frame
            '-i', '-', # The input comes from a pipe
            '-an', # Tells FFMPEG not to expect any audio
            '-vcodec', 'mpeg4',
            '-b:v', '10M', #Sets a maximum bit rate
            Output_name]
    #Open the pipe
    pipe = sp.Popen(command, stdin=sp.PIPE, stderr=sp.PIPE)

    print('Processing....')
    print(' ')
    #Reads through each frame, calculates the timestamp, places it on the frame and exports the frame to the output video.
    #import pdb
    #pdb.set_trace()
    while current_frame < total_frames:
        success, image = video.read()
        if success:
            elapsed_time = video.get(cv2.CAP_PROP_POS_MSEC)
            current_frame = video.get(cv2.CAP_PROP_POS_FRAMES)
            timestamp = initial + dt.timedelta(microseconds = elapsed_time*1000)
            cv2.putText(image, 'Date: ' + str(timestamp)[0:10], (50,int(height-150)), cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (255, 255, 255), 3)
            cv2.putText(image, 'Time: ' + str(timestamp)[11:-4], (50,int(height-100)), cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (255, 255, 255), 3)
            pipe.stdin.write(image.tostring())
            print('frame number',current_frame)
        else:
            print('video reader fail')    
    video.release()
    pipe.stdin.close()
    pipe.stderr.close()

但是,在大约 18k 帧之后,Python 卡在了“pipe.stdin.write(image.tostring())”。它不会产生任何错误,只是挂起。如何解决这个问题?

提前致谢。

【问题讨论】:

  • 你在监控内存使用吗?
  • 没有。在运行该过程时如何做到这一点?
  • 哪个操作系统?
  • 操作系统:Ubuntu 18.04。
  • 听起来标准输入缓冲区正在填充,因为 ffmpeg 没有使用它。

标签: python opencv ffmpeg subprocess


【解决方案1】:

我想我解决了这个难题:

stderr 缓冲区已满,进程卡住。
我设法在 Windows 10 下重现了这个问题。

  • FFmpeg 不时将状态写入stderr
  • 您正在使用stderr=sp.PIPE,但未从stderr 读取。
  • 编码许多帧后,stderr 缓冲区已满,进程卡住。

您可以删除stderr=sp.PIPE,或者确保从stderr 读取数据。

stderr 读取数据可以使用线程执行:

# Read from pipe.stdrr for "draining the pipe"
def drain_stderr():
    while True:
        try:
            stderr_output = pipe.stderr.readline()
        except:
            pass

我创建了一个“自包含”代码示例,用于生成合成视频文件,并使用合成视频作为输入来执行代码。

这里是测试代码示例:

import numpy as np
import cv2
import subprocess as sp
import threading
import datetime as dt

# Generate synthetic video file - resolution 640x480, 30000 frames, 1 fps
# H.264 encoded video (for testing):
#########################################################################
input_name = 'test.mp4'
width, height = 640, 480
total_frames = 30000
sp.run('ffmpeg -y -f lavfi -i testsrc=size={}x{}:rate=1 -vcodec libx264 -crf 23 -t {} {}'.format(width, height, total_frames, input_name))
#########################################################################


# Read from pipe.stdrr for "draining the pipe"
def drain_stderr():
    while keep_drain_stderr:
        try:
            stderr_output = pipe.stderr.readline()
        except:
            pass


Output_name = 'out.mp4'

command = ['ffmpeg',
            '-y', # (optional) overwrite output file if it exists
            '-f', 'rawvideo', #Input is raw video
            '-pix_fmt', 'bgr24', #Raw video format
            '-s', str(int(width)) + 'x' + str(int(height)), # size of one frame
            '-i', '-', # The input comes from a pipe
            '-an', # Tells FFMPEG not to expect any audio
            '-vcodec', 'mpeg4',
            '-b:v', '10M', #Sets a maximum bit rate
            Output_name]

# Open the pipe
pipe = sp.Popen(command, stdin=sp.PIPE, stderr=sp.PIPE)

keep_drain_stderr = True
thread = threading.Thread(target=drain_stderr)
thread.start()


# Open video file for reading
video = cv2.VideoCapture(input_name)

print('Processing....')
print(' ')

#Reads through each frame, calculates the timestamp, places it on the frame and exports the frame to the output video.
#import pdb
#pdb.set_trace()
initial = dt.timedelta(microseconds=0*1000)
current_frame = 0
while current_frame < total_frames:
    success, image = video.read()
    if success:
        elapsed_time = video.get(cv2.CAP_PROP_POS_MSEC)
        current_frame = video.get(cv2.CAP_PROP_POS_FRAMES)
        timestamp = initial + dt.timedelta(microseconds=elapsed_time*1000)
        cv2.putText(image, 'Date: ' + str(timestamp)[0:10], (50,int(height-150)), cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (255, 255, 255), 3)
        cv2.putText(image, 'Time: ' + str(timestamp)[11:-4], (50,int(height-100)), cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (255, 255, 255), 3)
        pipe.stdin.write(image.tostring())

        print('frame number', current_frame)
    else:
        print('video reader fail')

keep_drain_stderr = False
video.release()
pipe.stdin.close()
pipe.stderr.close()

#Wait 3 seconds before killing FFmpeg
try:
    pipe.wait(3)
except (sp.TimeoutExpired):
    pipe.kill()

thread.join()

【讨论】:

  • 是的。那是对的。我只是从 sp.Popen 中删除了 stderr 并且它起作用了。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2012-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多