public
Last active

ffmpeg screen recorder

  • Download Gist
ffrec.pl
Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
#!/usr/bin/env perl
use 5.012;
use warnings;
use autodie qw(:default exec);
 
my %window = get_window_info();
my $metadata = sprintf('-metadata title="screencap of window: %s"', $window{title});
chomp(my $datetime = `date +%F_%a_%H.%M.%S.%N`);
 
my $output_dir = "$ENV{HOME}/ffrec";
mkdir($output_dir) unless -d $output_dir;
my $output_file = sprintf('%s/%s-%dx%d-%s.mkv',
$output_dir,
$datetime,
@window{qw(width height)},
flatten_filename($window{title}),
);
 
=pod
 
Few notes:
 
* libx264rgb doesn't multithread and the presets range from too much IO to too much CPU
* libvpx with realtime settings can't keep up, so it destroys the quality instead
 
=cut
 
# Crude framerate limiting heuristic based on pixel count. 480p gets 60fps, 720p gets 25.
sub framelimit {
my ($w, $h, $base) = @_;
$base //= 60;
 
return int( (848/$w) * (480/$h) * $base / 5 ) * 5;
}
 
my @params = (
threads => '-threads 0',
audio_input => '-f pulse -i default',
video_input =>
sprintf('-f x11grab -video_size %dx%d -i :0.0+%d,%d -framerate %d',
@window{qw(width height top left)}, framelimit(@window{qw(width height)}, 60)
),
 
audio_codec => '-codec:a flac',
 
#video_codec => '-codec:v libvpx',
#vp8_flags => '-quality realtime -speed 0 -slices 3',
 
video_codec => '-codec:v libx264rgb -preset ultrafast -tune animation -qp 0',
 
#video_codec => '-codec:v libx264 -preset ultrafast -tune animation -qmax 25',
#x264_flags =>
# '-x264opts '.flatten_ffmpeg_opts({ 'open-gop' => undef, 'non-deterministic' => undef }),
 
metadata => $metadata,
output_file => $output_file,
);
 
my @cmdline;
push @cmdline, qw(schedtool -R -p 1 -e) if -e '/usr/bin/schedtool';
push @cmdline, qw(ionice -c 2 -n 0) if -e '/usr/bin/ionice';
push @cmdline, 'ffmpeg';
 
my $pid = fork // die 'Fork error';
exit if $pid;
 
open(STDOUT, q{>}, "$output_dir/ffrec.out");
open(STDERR, q{>}, "$output_dir/ffrec.err");
 
system('start-pulseaudio-x11');
 
exec '/bin/sh' => '-c', join(q{ }, @cmdline, @params[grep {$_ % 2} 0..$#params]);
 
sub get_window_info {
my $cmd_output = `xwininfo`;
my %window = (
top => 'Absolute upper-left X',
left => 'Absolute upper-left Y',
width => 'Width',
height => 'Height',
);
 
for ( keys %window ) {
($window{$_}) = $cmd_output =~ /\Q$window{$_}\E: \s+ (\d+)/x;
}
 
($window{title}) = $cmd_output =~ /Window id: 0x\w+ "(.+)"$/m;
 
return %window;
}
 
sub flatten_filename {
my $name = shift;
$name =~ tr/[a-zA-Z0-9]/_/cs;
return $name;
}
 
sub flatten_ffmpeg_opts {
my $opts = shift;
my %strings = @_ || (
key_prefix => q{},
opt_sep => q{:},
kv_sep => q{=},
);
 
my $ret = '';
while ( my ($k, $v) = each %{$opts} ) {
$ret .= $strings{opt_sep} if $ret;
$ret .= $strings{key_prefix}.$k;
$ret .= $strings{kv_sep}.$v if defined $v;
}
 
return $ret;
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.