Python ffmpeg 子进程从不在 Linux 上退出,在 Windows 上工作
Python ffmpeg subprocess never exits on Linux, works on Windows
不知是否有人可以帮忙解释一下这是怎么回事?
I 运行 2 个子进程,1 个用于 ffprobe,1 个用于 ffmpeg。
popen = subprocess.Popen(ffprobecmd, stderr=subprocess.PIPE, shell=True)
和
popen = subprocess.Popen(ffmpegcmd, shell=True, stdout=subprocess.PIPE)
在 Windows 和 Linux 上,ffprobe 命令都会触发、完成并从 taskmanager/htop 中删除。但只有在 Windows 上,ffmpeg 才会发生同样的情况。在 Linux 上,命令保留在 htop...
任何人都可以解释发生了什么,如果它很重要,我该如何阻止它发生?
编辑:这是命令...
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 + '"'
和
ffmpegcmd = 'ffmpeg' + \
' -re' + \
' -user_agent "' + r.headers['User-Agent'] + '"' + \
' -headers "Referer: ' + r.headers['Referer'] + '"' + \
' -timeout "10"' + \
' -i "' + r.url + '"' + \
' -c copy' + \
' -f mpegts' + \
' pipe:'
编辑:这是一个行为与描述相同的示例...
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)
ffmpegcmd
变量是什么类型?是字符串还是 list/sequence?
请注意,Windows 和 Linux/POSIX 在启用或禁用 [=11=] 参数时表现不同。 ffmpegcmd
是字符串还是列表很重要。
直接摘自the documentation:
On POSIX with shell=True, the shell defaults to /bin/sh. If args is a
string, the string specifies the command to execute through the shell.
This means that the string must be formatted exactly as it would be
when typed at the shell prompt. This includes, for example, quoting or
backslash escaping filenames with spaces in them. 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. That is to say, Popen does the equivalent of:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
On Windows with shell=True, the COMSPEC environment variable specifies
the default 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 (e.g. dir or copy). You do not need shell=True to run a batch
file or console-based executable.
由于 shell=True
,您有额外的 sh
进程,并且允许您的 ffmpeg 副本尝试附加到原始终端的标准输入,因为您没有覆盖该文件句柄。要解决这两个问题以及一些安全漏洞,请切换到 shell=False
,设置 stdin=subprocess.DEVNULL
,并且(要阻止僵尸可能被遗忘,请注意下面的 finally:
块调用 popen.poll()
查看 child 是否退出,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)
请注意,将二进制流解析为一系列行根本不合适。使用块(并更改您的响应 headers 以便浏览器知道将内容解析为视频)会更合适。
不知是否有人可以帮忙解释一下这是怎么回事?
I 运行 2 个子进程,1 个用于 ffprobe,1 个用于 ffmpeg。
popen = subprocess.Popen(ffprobecmd, stderr=subprocess.PIPE, shell=True)
和
popen = subprocess.Popen(ffmpegcmd, shell=True, stdout=subprocess.PIPE)
在 Windows 和 Linux 上,ffprobe 命令都会触发、完成并从 taskmanager/htop 中删除。但只有在 Windows 上,ffmpeg 才会发生同样的情况。在 Linux 上,命令保留在 htop...
任何人都可以解释发生了什么,如果它很重要,我该如何阻止它发生?
编辑:这是命令...
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 + '"'
和
ffmpegcmd = 'ffmpeg' + \
' -re' + \
' -user_agent "' + r.headers['User-Agent'] + '"' + \
' -headers "Referer: ' + r.headers['Referer'] + '"' + \
' -timeout "10"' + \
' -i "' + r.url + '"' + \
' -c copy' + \
' -f mpegts' + \
' pipe:'
编辑:这是一个行为与描述相同的示例...
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)
ffmpegcmd
变量是什么类型?是字符串还是 list/sequence?
请注意,Windows 和 Linux/POSIX 在启用或禁用 [=11=] 参数时表现不同。 ffmpegcmd
是字符串还是列表很重要。
直接摘自the documentation:
On POSIX with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. 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. That is to say, Popen does the equivalent of:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
On Windows with shell=True, the COMSPEC environment variable specifies the default 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 (e.g. dir or copy). You do not need shell=True to run a batch file or console-based executable.
由于 shell=True
,您有额外的 sh
进程,并且允许您的 ffmpeg 副本尝试附加到原始终端的标准输入,因为您没有覆盖该文件句柄。要解决这两个问题以及一些安全漏洞,请切换到 shell=False
,设置 stdin=subprocess.DEVNULL
,并且(要阻止僵尸可能被遗忘,请注意下面的 finally:
块调用 popen.poll()
查看 child 是否退出,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)
请注意,将二进制流解析为一系列行根本不合适。使用块(并更改您的响应 headers 以便浏览器知道将内容解析为视频)会更合适。