I can convert a mp4 to wav, using ffmpeg
, by doing this:
ffmpeg -vn test.wav -i test.mp4
I can also use subprocess
to do the same, as long as my input and output are filepaths.
But what if I wanted to use ffmpeg
directly on bytes or a "file-like" object like io.BytesIO()
?
Here's an attempt at it:
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()
Gives me:
---------------------------------------------------------------------------
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
Of course, I could use temp files to funnel my bytes, but I'd like to be able to avoid writing to the disk (because this step is just one link in a pipeline of transformations).
Base on @thorwhalen's answer, here's how it would work from bytes to bytes. What you were probably missing @thorwhalen, is the actual pipe-to-pipe way to send and get data when interacting with a process. When sending bytes, the stdin should be closed before the process can read from it.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With