我可以通过ffmpeg
将mp4转换为wav:
ffmpeg -vn test.wav -i test.mp4
我也可以使用subprocess
执行相同的操作,只要我的输入和输出是文件路径即可。
但是如果我想直接在字节上使用ffmpeg
或像io.BytesIO()
这样的“文件状”对象怎么办?
这里是尝试:
import subprocess
from io import BytesIO
b = BytesIO()
with open('test.mp4', 'rb') as stream:
command = ['ffmpeg', '-i']
proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=b)
proc.communicate(input=stream.read())
proc.wait()
proc.stdin.close()
proc.stdout.close()
给我:
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-84-0ddce839ebc9> in <module>
5 with open('test.mp4', 'rb') as stream:
6 command = ['ffmpeg', '-i']
----> 7 proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=b)
...
1486 # Assuming file-like object
-> 1487 c2pwrite = stdout.fileno()
1488
1489 if stderr is None:
UnsupportedOperation: fileno
当然,我可以使用临时文件来集中字节,但是我希望能够避免写入磁盘(因为此步骤只是转换管道中的一个链接)。
这里是部分答案:三个函数显示了如何从文件到文件(出于完整性),从字节到文件以及从文件到字节完成此操作。逐字节解决方案正在反击。
import shlex
import subprocess
def from_file_to_file(input_file: str, output_file: str, action="-f wav -acodec pcm_s16le -ac 1 -ar 44100"):
command = f"ffmpeg -i {input_file} {action} -vn {output_file}"
subprocess.call(shlex.split(command))
def from_file_to_bytes(input_file: str, action="-f wav -acodec pcm_s16le -ac 1 -ar 44100"):
command = f"ffmpeg -i {input_file} {action} -"
ffmpeg_cmd = subprocess.Popen(
shlex.split(command),
stdout=subprocess.PIPE,
shell=False
)
b = b''
while True:
output = ffmpeg_cmd.stdout.read()
if len(output) > 0:
b += output
else:
error_msg = ffmpeg_cmd.poll()
if error_msg is not None:
break
return b
def from_bytes_to_file(input_bytes, output_file, action="-f wav -acodec pcm_s16le -ac 1"):
command = f"ffmpeg -i /dev/stdin {action} -vn {output_file}"
ffmpeg_cmd = subprocess.Popen(
shlex.split(command),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
shell=False
)
ffmpeg_cmd.communicate(input_bytes)
基于@thorwhalen的答案,这是从字节到字节的工作方式。@thorwhalen可能缺少的是与流程交互时实际的管道到管道发送和获取数据的方式。发送字节时,应先关闭stdin,然后才能从中读取进程。
def from_bytes_to_bytes(
input_bytes: bytes,
action: str = "-f wav -acodec pcm_s16le -ac 1 -ar 44100")-> bytes or None:
command = f"ffmpeg -y -i /dev/stdin -f nut {action} -"
ffmpeg_cmd = subprocess.Popen(
shlex.split(command),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
shell=False
)
b = b''
# write bytes to processe's stdin and close the pipe to pass
# data to piped process
ffmpeg_cmd.stdin.write(input_bytes)
ffmpeg_cmd.stdin.close()
while True:
output = ffmpeg_cmd.stdout.read()
if len(output) > 0:
b += output
else:
error_msg = ffmpeg_cmd.poll()
if error_msg is not None:
break
return b