[英]How to use subprocess.Popen() instead of os.popen() in Python?
[英]How to use python subprocess with bytes instead of files
我可以使用ffmpeg
將 mp4 轉換為 wav,方法是:
ffmpeg -vn test.wav -i test.mp4
我也可以使用subprocess
來做同樣的事情,只要我的輸入和 output 是文件路徑。
但是,如果我想直接在字節上使用ffmpeg
或像io.BytesIO()
這樣的“類文件” object 怎么辦?
這是一個嘗試:
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
當然,我可以使用臨時文件匯集我的字節,但我希望能夠避免寫入磁盤(因為這一步只是轉換管道中的一個環節)。
根據@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
這是部分答案:三個函數顯示了如何從文件到文件(為了完整性)、從字節到文件以及從文件到字節。 字節到字節的解決方案正在反擊。
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)
這是我最近提出的解決方案,盡管我使用 AWS 和 GCP 存儲桶對象作為輸入和 output。我無論如何都不是 python 的專家,但這讓我得到了我想要的結果。
您需要在本地機器上安裝ffmpeg,並將其添加到環境變量中才能訪問ffmpeg。
如果您使用的是雲,則 ffmpeg 預裝在谷歌雲功能上,您可以利用 AWS 的存儲庫庫中的 Lambda 層。
希望有人能利用這一點。 :)
import subprocess
# tested against 'wav', 'mp3', 'flac', 'mp4'
desired_output = 'mp3'
track_input = 'C:\\Users\\.....\\track.wav'
track_output = f'C:\\Users\\......\\output_track.{desired_output}'
encoded_type = ''
format_for_conversion = desired_output
if desired_output =='m4a':
encoded_type= '-c:a aac'
format_for_conversion = 'adts'
with open(track_input, "rb") as in_track_file:
data = in_track_file.read()
input_track_data= bytearray(data)
# using pipe:0 refers to the stdin, pipe:1 refers to stdout
ffmpeg_command = f'ffmpeg -i pipe:0 {encoded_type} -f {format_for_conversion} pipe:1 '
ffmpeg_process = subprocess.Popen(ffmpeg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output_stream = ffmpeg_process.communicate(input_track_data)
# comes back as a tuple
output_bytes = output_stream[0]
with open(track_output, 'ab') as f:
delete_content(f)
f.write(output_bytes)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.