]> git.sesse.net Git - nageru/blob - experiments/measure-x264.pl
Rework the x264 speedcontrol presets.
[nageru] / experiments / measure-x264.pl
1 #! /usr/bin/perl
2
3 #
4 # A script to measure the quality and speed of the x264 presets used in speed control.
5 #
6
7 use strict;
8 use warnings;
9 use Time::HiRes;
10
11 my $ssim_mode = 1;
12 my $output_cpp = 1;
13 my $flags = "--bitrate 4000 --frames 1000";
14 my $override_flags = "--weightp 1 --mbtree --rc-lookahead 20";
15 my $file = "elephants_dream_1080p24.y4m";  # https://media.xiph.org/video/derf/y4m/elephants_dream_1080p24.y4m
16
17 if ($ssim_mode) {
18         # This can be run on a faster machine if you want to. It just measures SSIM;
19         # don't trust the timings, not even which modes are faster than others.
20         # The mode where $output_cpp=0 is just meant as a quick way to test new presets
21         # to see if they are good candidates.
22         $flags .= " --threads 40 --ssim";
23         $override_flags .= " --tune ssim";
24         open my $fh, "<", "presets.txt"
25                 or die "presets.txt: $!";
26         my $preset_num = 0;
27         for my $preset (<$fh>) {
28                 chomp $preset;
29                 my ($ssim, $elapsed) = measure_preset($file, $flags, $override_flags, $preset);
30                 if ($output_cpp) {
31                         output_cpp($file, $flags, $override_flags, $preset, $ssim, $preset_num++);
32                 } else {
33                         printf "%sdb %.3f %s\n", $ssim, $elapsed, $preset;
34                 }
35         }
36         close $fh;
37 } else {
38         # Actual benchmarking.
39         my $repeat = 1;
40         $flags .= " --threads 4";
41         open my $fh, "<", "presets.txt"
42                 or die "presets.txt: $!";
43         my $base = undef;
44         for my $preset (<$fh>) {
45                 chomp $preset;
46                 my $sum_elapsed = 0.0;
47                 for my $i (1..$repeat) {
48                         my (undef, $elapsed) = measure_preset($file, $flags, $override_flags, $preset);
49                         $sum_elapsed += $elapsed;
50                 }
51                 my $avg = $sum_elapsed / $repeat;
52                 $base //= $avg;
53                 printf "%.3f %s\n", $avg / $base, $preset;
54         }
55         close $fh;
56 }
57
58 sub measure_preset {
59         my ($file, $flags, $override_flags, $preset) = @_;
60
61         my $now = [Time::HiRes::gettimeofday];
62         my $ssim;
63         open my $x264, "-|", "/usr/bin/x264 $flags $preset $override_flags -o /dev/null $file 2>&1";
64         for my $line (<$x264>) {
65                 $line =~ /SSIM Mean.*\((\d+\.\d+)db\)/ and $ssim = $1;
66         }
67         close $x264;
68         my $elapsed = Time::HiRes::tv_interval($now);
69         return ($ssim, $elapsed);
70 }
71
72 sub output_cpp {
73         my ($file, $flags, $override_flags, $preset, $ssim, $preset_num) = @_;
74         unlink("tmp.h264");
75         system("/usr/bin/x264 $flags $preset $override_flags --frames 1 -o tmp.h264 $file >/dev/null 2>&1");
76         open my $fh, "<", "tmp.h264"
77                 or die "tmp.h264: $!";
78         my $raw;
79         {
80                 local $/ = undef;
81                 $raw = <$fh>;
82         }
83         close $fh;
84
85         $raw =~ /subme=(\d+)/ or die;
86         my $subme = $1;
87
88         $raw =~ /me=(\S+)/ or die;
89         my $me = "X264_ME_" . uc($1);
90
91         $raw =~ /ref=(\d+)/ or die;
92         my $refs = $1;
93
94         $raw =~ /mixed_ref=(\d+)/ or die;
95         my $mix = $1;
96
97         $raw =~ /trellis=(\d+)/ or die;
98         my $trellis = $1;
99
100         $raw =~ /analyse=0x[0-9a-f]+:(0x[0-9a-f]+)/ or die;
101         my $partitions_hex = oct($1);
102         my @partitions = ();
103         push @partitions, 'I8' if ($partitions_hex & 0x0002);
104         push @partitions, 'I4' if ($partitions_hex & 0x0001);
105         push @partitions, 'P8' if ($partitions_hex & 0x0010);
106         push @partitions, 'B8' if ($partitions_hex & 0x0100);
107         push @partitions, 'P4' if ($partitions_hex & 0x0020);
108         my $partitions = join('|', @partitions);
109
110         $raw =~ /bframes=(\d+)/ or die;
111         my $bframes = $1;
112
113         my ($badapt, $direct);
114         if ($bframes > 0) {
115                 $raw =~ /b_adapt=(\d+)/ or die;
116                 $badapt = $1;
117                 $raw =~ /direct=(\d+)/ or die;
118                 $direct = $1;
119         } else {
120                 $badapt = $direct = 0;
121         }
122
123         $raw =~ /me_range=(\d+)/ or die;
124         my $merange = $1;
125
126         print "\n";
127         print "\t// Preset $preset_num: ${ssim}db, $preset\n";
128         print "\t{ .time= 0.000, .subme=$subme, .me=$me, .refs=$refs, .mix=$mix, .trellis=$trellis, .partitions=$partitions, .badapt=$badapt, .bframes=$bframes, .direct=$direct, .merange=$merange },\n";
129
130 #x264 - core 148 r2705 3f5ed56 - H.264/MPEG-4 AVC codec - Copyleft 2003-2016 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=34 lookahead_threads=5 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=24 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
131 }