11

I'm trying to use ffmpeg with Python's subprocess module to convert some audio files. I grab the audio files from a URL and would like to just be able to pass the Python File Objects to ffmpeg, instead of first saving them to disk. It would also be very nice if I could just get back a file stream instead of having ffmpeg save the output to a file.

For reference, this is what I'm doing now:

tmp = "/dev/shm"
audio_wav_file = requests.get(audio_url)
##              ##                         ##
## This is what I don't want to have to do ##
wavfile = open(tmp+filename, 'wrb')   
wavfile.write(audio_wav_file.content)
wavfile.close()
##              ##                         ##
conversion = subprocess.Popen('ffmpeg -i "'+tmp+filename+'" -y "'+tmp+filename_noext+'.flac" 2>&1', shell = True, stdout = subprocess.PIPE).stdout.read()

Does anyone know how to do this?

Thanks!

Lyle Pratt
  • 5,636
  • 4
  • 27
  • 28

4 Answers4

7

with ffmpeg you can use - as input/output file name to indicate that it should read the data from stdin / write to stdout.

Then you can use the stdin/stdout arguments of Popen to read/write your data.

an example:

from subprocess import Popen, PIPE

with open("test.avi", "rb") as infile:
    p=Popen(["ffmpeg", "-i", "-", "-f", "matroska", "-vcodec", "mpeg4",
        "-acodec", "aac", "-strict", "experimental", "-"],
           stdin=infile, stdout=PIPE)
    while True:
        data = p.stdout.read(1024)
        if len(data) == 0:
            break
        # do something with data...
        print(data)
    print p.wait() # should have finisted anyway

instead you supplying a file for stdin you could also use a PIPE and write directly to the processes input stream (p.stdin). or in your case you would simply use wavfile...

note that you have to specify the output format and codecs explicitly, as ffmpeg can't guess them from the file extension as it usually does.
and it will only work for muxers that don't need seekable output streams, but flac should work...

mata
  • 67,110
  • 10
  • 163
  • 162
2

For anyone still reading this: This can be done without subprocesses by using FFMPEG's pipe protocol instead. If FFMPEG is called using the package ffmpeg-python, the stdout, stderr output of the FFMPEG command can be fed into Python variables as seen here:

out, err = inpstream.output('pipe:', ... ).run(capture_stdout=True)
0

Since it looks like you're on Unix (no .exe on the end of 'ffmpeg'), I would recommend using a named pipe, a.k.a. fifo:

mkfifo multimedia-pipe

Have the Python script write the audio data into 'multimedia-file' and ask FFmpeg to read from the same file. I have used this pattern to decode multimedia files into their huge, raw representations for validation without having to occupy disk space.

Alternatively, try passing the 'http://...' URL directly to FFmpeg's input option.

Multimedia Mike
  • 12,660
  • 5
  • 46
  • 62
0

PyAV can be used with paths or file like objects, from the docs:

file (str) – The file to open, which can be either a string or a file-like object.

(If you have a byte array, you can wrap it with io.BytesIO before passing it to av.open)

jns
  • 1,250
  • 1
  • 13
  • 29