Archive

Archive for November, 2020

Parallel errors

November 15, 2020 1 comment

I wanted to know how long I have to wait for ffmpeg to turn * into AV1. To do so I have to capture STDERR because that’s where ffmpeg outputs the status information while encoding.

my $ffmpeg-out = Channel.new;
my $ffmpeg-err = Channel.new;

react {
    whenever $ffmpeg-err -> [ $stream-num, $_ ] {
        .say if $verbose;
        # boring parsing code goes here
    }
}

px{'nice', '/usr/local/bin/ffmpeg', '-i', $input, '-c:v', 'libsvtav1', '-q', '60', '-preset', '5', '-y', $output} |» $ffmpeg-out :stderr($ffmpeg-err);

The solution is quite simple. I use a Channel to transport lines of text from STDERR to a react block that does the parsing. A nice solution until ffmpeg decides to use STDERR to actually communicate error messages. Those will be gobbled up by the parsing code. As a result the exception that is thrown out of Shell::Piping has no error message to display, beside that a non-zero error code has been returned.

There are quite a few programs that output status to STDERR while operating, so a general approach would be nice. Where messages land is controlled by the adverb :stderr. To capture text to be displayed in the exception is done with :stderr(Capture). So my first impulse was to use a mixin like :stderr($ffmpeg-err but Capture). This would allow me to solve this problem. More complex solutions would be desirable. A user of my module might want to parse STDERR and write everything to a file. Both are currently possible but not at the same time. Providing a list of stream targets would imply order. I’m not sure if I can guarantee that. In the case of multiple channels it would not make sense for sure. In Raku we do have a parallel data type called Junction. In its current form it is not possible to pull a Junction apart. A pull request by lizmat to change that did not find a large fellowship. Luckily she also wrote a module called eigenstates.

use eigenstates;

sub prefix:<\foo> (Str $_, Junction :$err = all Whatever) {
     say $_;
     say eigenstates $err;
 }
 \foo "ello" :err(1|2);

# OUTPUT: ello
          (1 2)

This allows me to do :stderr($ffmpeg-err & Capture) and many more complex operations. For now I don’t see a use in the type of Junction beside all. Since there are still 95 years left in Rakus life cycle, I’m quite sure something will come along.

Categories: Raku