python - subprocess.Popen无法正确管道urllib3响应

时间:2018-02-11 00:43:41

标签: python ffmpeg pipe

我想通过网络打开文件,并使用subprocess.Popen将其直接传递给ffmpeg。目标是将音频文件数据直接流式传输到ffmpeg。这是代码:

# test.py
import subprocess, sys, urllib3, time

http = urllib3.PoolManager()
r = http.urlopen('GET', sys.argv[1], preload_content=False)

args = 'ffmpeg -i - -y audio.mp3'.split(' ')
subprocess.Popen(args, stdin=r)

r.close()

如果我运行本地HTTP服务器并为程序提供url,它会成功运行,并且ffmpeg会对其进行处理。

$ python3 test.py http://192.168.1.200/original.webm

但是,如果我尝试从远程服务器(例如下面的服务器)检索,则ffmpeg会失败。

$ python3 test.py https://cdn.discordapp.com/attachments/304959901376053248/412003156638040084/original.webm

使用以下输出

pipe:: Invalid data found when processing input

我希望这段代码能够产生与运行此终端命令相同的结果。此命令对discord cdn URL和本地HTTP服务器URL都成功。

$ curl [file url] | ffmpeg -i - -y audio.mp3

我在Linux上使用python 3.5,以及ffmpeg 3.4.1。

编辑1

我现在倾向于认为它不是错误的,更多的是关于Popen如何阅读/写入urllib对流程的反应'标准输入。通过运行本地netcat服务器并将输出发送到文件($ nc -l 1234 > nc_output.webm)并调整脚本,如下所示:

import subprocess, sys, urllib3, time

http = urllib3.PoolManager()
r = http.urlopen('GET', sys.argv[1], preload_content=False)

args = 'nc 192.168.1.200 1234'.split(' ')
subprocess.run(args, stdin=r)

r.close()

然后以$ python3 test.py https://cdn.discordapp.com/attachments/304959901376053248/412003156638040084/original.webm

运行

通过比较nc_output.webm和original.webm文件,我可以立即看到nc_output.webm略大(4017585字节,而不是4008589字节)。试图玩nc_output.webm(mpv,vlc,ffprobe)也失败了,这就解释了为什么ffmpeg在抱怨。无论Popen对流的字节做什么都足以使输出文件无用。

但是,如果URL指向本地HTTP服务器,例如从python -m SimpleHTTPServer运行的一个HTTP服务器,则问题仍然不再发生,这导致我认为这与从远程源读取相关的延迟有关

1 个答案:

答案 0 :(得分:1)

urllib3实现使用Python代码从打开的连接中获取内容。这非常适合使用Python客户端,但无法跨越子进程边界。

您正在使用subprocess.Popen(),这实际上可让您相对轻松地解决此问题。您正在子进程中启动ffmpeg,但随后控件返回到您的Python代码,而子进程仍在后台,等待您在标准输入上提供数据。

r = http.urlopen('GET', sys.argv[1], preload_content=False)

# Manually split the command, just for aesthetics
s = subprocess.Popen(['ffmpeg', '-i', '-', '-y', 'audio.mp3'], stdin=subprocess.PIPE)
while True:
    b = r.read(8192)
    if b == '':
        break
    s.communicate(b)
r.close()
s.close()