views:

496

answers:

2

I'm trying to use python + ffmpeg + oggenc to convert any audiofile to ogg. The program works, almost. But for big files (i think > ~6mb) the ffmpeg process starts to sleep at pipe_wait. I don't know which pipe it waits for.

If I kill the ffmpeg process, the oggenc process continues and I get a resulting ogg-file with about ~2:40 of all the sound.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
from sys import argv

ffmpeg = Popen([
    "ffmpeg",
    "-i", argv[1],
    "-vcodec", "null",
    "-acodec", "pcm_s16le",
    "-ac", "2",
    "-ab", "44100",
    "-f", "wav",
    "-"
],stdout = PIPE,stderr = PIPE)

oggenc = Popen([
    "oggenc",
    "-", "--raw",
    "-q", "4",
    "-o", argv[2]
],stdin = ffmpeg.stdout,stderr = PIPE)

oggenc.communicate()
ffmpeg.communicate()

EDIT:

Thought I might add that this works perfectly:

#!/bin/bash

ffmpeg -i "$1" -vcodec null -acodec pcm_s16le -ac 2 -ab 44100 -f wav - | oggenc - --raw -q 4 -o "$2"
A: 

Its tricky to see who needs to pass the pipe, eer I mean you should use a debugger like the one in NetBeans to help gather more clues. Pipe's may not be the best approach, perhaps using a temporary file would simplify things.

Rook
Well, temporary files was my first thought but then it will take almost twice the time, first ffmpeg converts the file to raw audio and then oggenc converts it to ogg/vorbis. With pipes they can do it at the same time, which saves time.
Linus Unnebäck
…and pipes will make better use of multiple cores (if available).
ΤΖΩΤΖΙΟΥ
+2  A: 

What exactly do you do with the stderr channels of the two pipes?

Encoders/decoders typically produce lots of stderr output, as status updates; this output is piped to your process, and buffers will become full. Perhaps you should add some dummy ffmpeg.stderr.read() call before the (useless, I think) .communicate calls, or even better, drop the stderr=PIPE arguments completely.

UPDATE

For the ">/dev/null" equivalent, do the following:

nulfp= open(os.devnull, "w")
…
…= subprocess.Popen(…, stderr=nulfp.fileno())

Obviously, you can re-use the same nulfp for all stderrs you want to ignore.

ΤΖΩΤΖΙΟΥ
The reason I had stderr piped was becuase I didn't wanted it to write anything to the terminal. I tried using "-loglevel quiet" on ffmpeg to see if it worked, but than the everything just quited directly. First I thought it was the .communicate that terminated the program but as it turns out: It was ffmpeg not liking "-loglevel quiet" so it terminated itself.Everything works when I drop the stderr=PIPE arguments, thanks for your help! Now I just need to figure out how to not show the sdterr data...
Linus Unnebäck
Just did the same thing but without .fileno(), any pros/cons using it?
Linus Unnebäck
It's the same, no pros/cons. The subprocess module will get the `.fileno()` internally when you give it a file object.
ΤΖΩΤΖΙΟΥ
os.devnull works cross platform
Corey Goldberg
@Corey Goldberg: thanks!
ΤΖΩΤΖΙΟΥ
Thanks for the tip! For future reference: one still needs to do `null = open(os.devnull,'w')`.
Linus Unnebäck