http://www.perlmonks.org?node_id=1066939

filmotheklown has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to wrap a system call to ffmpeg in such a way that it will kill ffmpeg if it takes too long to run.

# here's a command that takes a big MOV file and rips the audio out to + WAV $my_cmd = '/usr/bin/ffmpeg -y -i long_input.mov long_output.wav';

my understanding is that I should be able to do something like:

run \@cmd, timeout(10);

which would then attempt to convert the file to WAV, but if it takes longer than 10 seconds, kill the ffmpeg process. I'm not interested in solutions that simply allow the perl script to time out, it must kill the ffmpeg process too.

I've tried a lot of variations of creating @cmd, but I invariably get some error or another.

@cmd = ('/usr/bin/ffmpeg', '-y', '-i', 'long_input.mov','long_output.w +av'); @cmd = ('/usr/bin/ffmpeg -y -i long_input.mov long_output.wav'); @cmd = various other incarnations.

Either I get "file not found: /usr/bin/ffmpeg -y -i" or it fails to find the 'input' file that follows the '-i' command for ffmpeg. "i /home/product/ftp-temp/long.mov: No such file or directory" for example.

I've checked the path of my files and the raw command works form the CLI successfully, opening long_input.mov and ripping the audio to long_output.wav.

Clearly there's something I'm not understanding about IPC:run and how it operates.

I'm trying to accomplish the equivalent of "timeout --signal=SIGKILL 10s ffmpeg -y -i long_input.mov long_output.wav" which works in the CLI of centos, but within the perl script

Thank you in advance for your time. Any advice is appreciated.

Replies are listed 'Best First'.
Re: Using IPC::Run to kill stuck process
by runrig (Abbot) on Dec 13, 2013 at 01:27 UTC
    This one should work:
    my @cmd = ('/usr/bin/ffmpeg', '-y', '-i', 'long_input.mov','long_outpu +t.wav');
    Are you sure it doesn't work? And that it matches the command line that does work?

    Update: And taking a step back, does system(@cmd) work?

    Update2: Does long_output.wav exist at the time of execution? Is it possibly created within the script and you didn't close a filehandle yet?

      You're right, I must have gotten mixed up in the process. The command works as you indicated. Thank you everyone for your help.

      Here's my final working code for completeness

      #! /usr/bin/perl use warnings; use strict; use IPC::Run qw(run timeout); my $from_file = '/home/product/ftp-temp/long.mov'; my $tmp_to = '/home/product/ftp-temp/out.wav'; my $in; my $out; my $err; my @cmd = ('/usr/bin/ffmpeg','-y','-i', $from_file, $tmp_to); eval { #16 seconds is long enough to convert about 288 minutes of audio #Current server converts about 18 minutes per second of runtime. #production version will use custom timeout based on expected #lengths of material. run \@cmd, \$in,\$out,\$err, timeout(16, exception=>'timeout'); }; if ($@ =~ /timeout/) { print "Timed out...\n\n"; } else { print "\n\nCompleted Task\n\n"; }
Re: Using IPC::Run to kill stuck process
by toolic (Bishop) on Dec 13, 2013 at 01:43 UTC
    I agree with runrig that your 1st @cmd should work. Take a few steps back; maybe in all your hacking, things have gotten confused. Just try this simple code and show your exact error output:
    use warnings; use strict; use IPC::Run qw(run timeout); my @cmd = ('/usr/bin/ffmpeg', '-y', '-i', 'long_input.mov', 'long_outp +ut.wav'); run \@cmd, timeout(3);

    If that's still broken try something even simpler:

    use warnings; use strict; use IPC::Run qw(run timeout); my @cmd = qw(ls /tmp); run \@cmd, timeout(3);

    Are you using a recent version of IPC::Run?

Re: Using IPC::Run to kill stuck process
by taint (Chaplain) on Dec 13, 2013 at 02:11 UTC

    While I agree with both runrig, and toolic. My only thoughts are that it's a clean shutdown of the application/process. eg; no leftover open filehandles. I'm also wondering if the reason you're not seeing the file, is because there's a temp filehandle/buffer operation, in progress, and that's why your not seeing it in your expected final location. Maybe your "temp" variable -- /tmp, or something. That's all I can think of.

    --Chris

    Yes. What say about me, is true.
    
Re: Using IPC::Run to kill stuck process
by zentara (Archbishop) on Dec 13, 2013 at 16:15 UTC
    How about something like this piece of standard timeout code:
    #!/usr/bin/perl use warnings; use strict; my $script = 'ps auxww'; eval { # from http://www.rocketaware.com/perl/perlfunc/alarm.htm local $SIG{ALRM} = sub { die "alarm\n" }; # NB \n required alarm(5); # set time limit to 5 sec system($script); alarm(0); # if $script finishes in <5sec reset alarm }; die if $@ && $@ ne "alarm\n"; # propagate errors if ($@) { print "Timeout! [$@]\n"; # do stuff if eval fails (eg timeout) }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      I don't think the timeout is the problem (the problem seems to be running the command in the first place), but IPC::Run's timeout mechanism should work, and should terminate the process if it does timeout.