我想通过网络打开文件,并使用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服务器,则问题仍然不再发生,这导致我认为这与从远程源读取相关的延迟有关
答案 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()