简体   繁体   English

Python ffmpeg 子进程永远不会在 Linux 上退出,适用于 ZAEA23489CE3AAAA943406EBB28E0CD

[英]Python ffmpeg subprocess never exits on Linux, works on Windows

I wonder if someone can help explain what is happening?我想知道是否有人可以帮助解释发生了什么?

I run 2 subprocesses, 1 for ffprobe and 1 for ffmpeg.我运行 2 个子进程,1 个用于 ffprobe,1 个用于 ffmpeg。

popen = subprocess.Popen(ffprobecmd, stderr=subprocess.PIPE, shell=True)

And

popen = subprocess.Popen(ffmpegcmd, shell=True, stdout=subprocess.PIPE)

On both Windows and Linux the ffprobe command fires, finishes and gets removed from taskmanager/htop.在 Windows 和 Linux 上,ffprobe 命令触发、完成并从 taskmanager/htop 中删除。 But only on Windows does the same happen to ffmpeg.但只有在 Windows 上,ffmpeg 才会发生同样的情况。 On Linux the command remains in htop...在 Linux 上,命令仍保留在 htop...

在此处输入图像描述

Can anyone explain what is going on, if it matters and how I can stop it from happening please?任何人都可以解释发生了什么,如果它很重要,我该如何阻止它发生?

EDIT: Here are the commands...编辑:这里是命令...

ffprobecmd = 'ffprobe' + \
' -user_agent "' + request.headers['User-Agent'] + '"' + \
' -headers "Referer: ' + request.headers['Referer'] + '"' + \
' -timeout "5000000"' + \
' -v error -select_streams v -show_entries stream=height -of default=nw=1:nk=1' + \
' -i "' + request.url + '"'

and

ffmpegcmd = 'ffmpeg' + \
' -re' + \
' -user_agent "' + r.headers['User-Agent'] + '"' + \
' -headers "Referer: ' + r.headers['Referer'] + '"' + \
' -timeout "10"' + \
' -i "' + r.url + '"' + \
' -c copy' + \
' -f mpegts' + \
' pipe:'

EDIT: Here is a example that behaves as described...编辑:这是一个行为如所描述的示例......

import flask
from flask import Response
import subprocess

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def go():
    def stream(ffmpegcmd):
        popen = subprocess.Popen(ffmpegcmd, stdout=subprocess.PIPE, shell=True)
        try:
            for stdout_line in iter(popen.stdout.readline, ""):
                yield stdout_line
        except GeneratorExit:
            raise

    url = "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8"

    ffmpegcmd = 'ffmpeg' + \
                ' -re' + \
                ' -timeout "10"' + \
                ' -i "' + url + '"' + \
                ' -c copy' + \
                ' -f mpegts' + \
                ' pipe:'
    return Response(stream(ffmpegcmd))

if __name__ == '__main__':
    app.run(host= '0.0.0.0', port=5000)

You have the extra sh process due to shell=True , and your copies of ffmpeg are allowed to try to attach to the original terminal's stdin because you aren't overriding that file handle.由于shell=True ,您有额外的sh进程,并且您的 ffmpeg 副本被允许尝试附加到原始终端的标准输入,因为您没有覆盖该文件句柄。 To fix both those issues, and also some security bugs, switch to shell=False , set stdin=subprocess.DEVNULL , and (to stop zombies from potentially being left behind, note the finally: block below that calls popen.poll() to see if the child exited, and popen.terminate() to tell it to exit if it hasn't):要解决这两个问题以及一些安全错误,请切换到shell=False ,设置stdin=subprocess.DEVNULL ,并且(为了阻止僵尸可能被抛在后面,请注意下面调用popen.poll()finally:块看看孩子是否退出,如果没有退出, popen.terminate()告诉它退出):

#!/usr/bin/env python

import flask
from flask import Response
import subprocess

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def go():
    def stream(ffmpegcmd):
        popen = subprocess.Popen(ffmpegcmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE)
        try:
            # NOTE: consider reading fixed-sized blocks (4kb at least) at a time
            # instead of parsing binary streams into "lines".
            for stdout_line in iter(popen.stdout.readline, ""):
                yield stdout_line
        finally:
            if popen.poll() == None:
                popen.terminate()
                popen.wait() # yes, this can cause things to actually block

    url = "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8"

    ffmpegcmd = [
        'ffmpeg',
        '-re',
        '-timeout', '10',
        '-i', url,
        '-c', 'copy',
        '-f', 'mpegts',
        'pipe:'
    ]
    return Response(stream(ffmpegcmd))

if __name__ == '__main__':
    app.run(host= '127.0.0.1', port=5000)

Mind, it's not appropriate to be parsing a binary stream as a series of lines at all.请注意,将二进制 stream 解析为一系列行是不合适的。 It would be much more appropriate to use blocks (and to change your response headers so the browser knows to parse the content as a video).使用块(并更改响应标头以便浏览器知道将内容解析为视频)会更合适。

What type is the ffmpegcmd variable? ffmpegcmd变量是什么类型的? Is it a string or a list/sequence?它是字符串还是列表/序列?

Note that Windows and Linux/POSIX behave differently with the shell=True parameter enabled or disabled.请注意,Windows 和 Linux/POSIX 在启用或禁用shell=True参数时表现不同。 It matters whether ffmpegcmd is a string or a list. ffmpegcmd是字符串还是列表很重要。

Direct excerpt from the documentation :直接摘自文档

On POSIX with shell=True, the shell defaults to /bin/sh.在 shell=True 的 POSIX 上,shell 默认为 /bin/sh。 If args is a string, the string specifies the command to execute through the shell.如果 args 是字符串,则该字符串指定要通过 shell 执行的命令。 This means that the string must be formatted exactly as it would be when typed at the shell prompt.这意味着字符串的格式必须与在 shell 提示符下键入时完全相同。 This includes, for example, quoting or backslash escaping filenames with spaces in them.例如,这包括引用或反斜杠 escaping 文件名,其中包含空格。 If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself.如果 args 是一个序列,则第一项指定命令字符串,任何附加项将被视为对 shell 本身的附加 arguments。 That is to say, Popen does the equivalent of:也就是说,Popen 相当于:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

On Windows with shell=True, the COMSPEC environment variable specifies the default shell.在 shell=True 的 Windows 上,COMSPEC 环境变量指定默认 shell。 The only time you need to specify shell=True on Windows is when the command you wish to execute is built into the shell (eg dir or copy).您需要在 Windows 上指定 shell=True 的唯一时间是您希望执行的命令内置于 shell 中(例如 dir 或 copy)。 You do not need shell=True to run a batch file or console-based executable.您不需要 shell=True 来运行批处理文件或基于控制台的可执行文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM