Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use strict;
- use warnings "all";
- use JSON;
- use Time::HiRes qw(tv_interval gettimeofday);
- my $file = "@ARGV";
- my $target = 67108864;
- my $abr = 64000;
- my $ac = 2;
- my @vf;
- my @flags;
- die "can't open file" unless -f $file;
- if ($file =~ /\/|\\/) {
- $file =~ s/^(.+)[\\\/]//;
- chdir $1;
- }
- my $safename = $file;
- $safename =~ s/([\[\]:,])/\\$1/g;
- my $time = time();
- my $json = decode_json `ffprobe -v panic -print_format json -show_format \"$file\"`;
- my $format = $json->{format};
- my $duration = $format->{duration} || 1;
- my $oldbr = $format->{bit_rate} || 1;
- my $ssreams = $format->{nb_streams} || 1;
- $json = decode_json `ffprobe -v panic -print_format json -show_streams \"$file\"`;
- my $ss = &q('starting time[00:00]> ');
- $ss =~ s/[^0-9:.]//g;
- my $to = &q('ending time[' . &sec2time($duration) . ']> ');
- $to =~ s/[^0-9:.]//g;
- my $elapsed = 0;
- my $ss_seconds = 0;
- my $ss_auto = 0;
- my $to_auto = 0;
- my $slice = "";
- if ($ss eq "") { $ss = 0; $ss_auto = 1; }
- if ($to eq "") { $to = &sec2time($duration); $to_auto = 1; }
- {
- my @s = reverse split /:/, $ss;
- my @e = reverse split /:/, $to;
- if ($#e == 2) { $s[2] //= 0; $elapsed += (($e[2] - $s[2]) * 3600); $ss_seconds += 3600 * $s[2]; }
- if ($#e >= 1) { $s[1] //= 0; $elapsed += (($e[1] - $s[1]) * 60); $ss_seconds += 60 * $s[1]; }
- $elapsed += ($e[0] - $s[0]);
- $ss_seconds += $s[0];
- }
- die "couldn't calculate duration\n" unless $elapsed;
- $slice = "-ss $ss -t $elapsed ";# if ($ss_auto == 0 || $to_auto == 1);
- my $maxbv = sprintf "%d", $target / $elapsed;
- my $audio = "";
- my $map = "";
- my ($framerate, $width, $height, $substream);
- if ($ssreams > 1) {
- my $vstream = -1;
- my $astream = -1;
- for (@{$json->{streams}}) {
- if ($vstream == -1 && $_->{codec_type} eq "video") { $vstream = $_->{index}; }
- if ($_->{codec_type} eq "audio") {
- if ($astream == -1) { $astream = $_->{index}; }
- if (defined $_->{tags}->{language} && $_->{tags}->{language} eq "jpn") { $astream = $_->{index}; }
- }
- if ($_->{codec_type} eq "subtitle") {
- my $subs = &q('subtitles[Y/n]> ');
- if ($subs !~ /n/i) {
- if ($ss_seconds > 0) {
- push @vf, "setpts=PTS+$ss_seconds/TB";
- push @vf, "subtitles=\"$safename\"";
- push @vf, "setpts=PTS-STARTPTS";
- } else { push @vf, "subtitles=\"$safename\""; }
- }
- }
- }
- #$astream = 1; ??? why was this here
- $json->{streams}[$vstream]->{r_frame_rate} =~ m/^([0-9.]+?)\/([0-9.]+?)$/;
- $framerate = $1 / $2;
- $width = $json->{streams}[$vstream]->{width};
- $height = $json->{streams}[$vstream]->{height};
- $map = "-map 0:" . $vstream;
- if ($astream != -1) {
- $audio = &q('audio[64k/n]> ');
- if ($audio !~ /n/) {
- if ($audio =~ /^(\d+?)$/) { $abr = $1 * 1000; }
- if ($json->{streams}[$astream]->{channels} == 1) { $ac = 1; $abr /= 2; }
- $map .= " -map 0:" . $astream;
- $audio = " -c:a libopus -b:a ${abr} -ac $ac";
- $maxbv -= $abr;
- } else { $audio = " -an"; }
- }
- }
- my $crop = &q('crop[w:h:x:y/N]> ');
- if ($crop !~ /n/i && $crop ne "") { push @vf, "crop=" . $crop; }
- my $scale = &q('scale[w:h/N]> ');
- if ($scale !~ /n/i && $scale ne "") { push @vf, "scale=" . $scale; push @flags, " -sws_flags lanczos"; }
- if ($framerate > 24) {
- my $fps = &q("fps[$framerate]> ");
- if ($fps ne "") { push @vf, "fps=fps=$fps"; $framerate = $fps; }
- }
- my $name = &q('Name file[newname/' . $time . ']> ');
- if ($name eq "") { $name = $time; }
- print "runtime : $elapsed seconds\n";
- my $bv;
- #hacky
- if ($elapsed < 20) { $bv = "0 -crf 18"; }
- else {
- if ($oldbr == 0) { $oldbr = 1; }
- my $delta = ($maxbv/$oldbr) * 100;
- printf "\n\nold bitrate: %d\nmax bitrate: %d\nbr change: %d %%\n", $oldbr, $maxbv, $delta;
- for (qw(70 65 60 55)) {
- my $n = sprintf "%d", ($_ / 100) * $oldbr;
- if ($n > $maxbv) { next; }
- if (!$bv) { $bv = $n; }
- print "bitrate $_%: $n\n";
- }
- if (!$bv) { $bv = $maxbv; }
- my $minbv = int(.4 * $oldbr);
- print "min bitrate: $minbv (40%)\n";
- if ($minbv > $maxbv) { $bv = $maxbv; }
- if ($bv > $oldbr) { $bv = $minbv; }
- print "bitrate[$bv/#]> ";
- my $go = <STDIN>;
- if ($go =~ /^([0-9.]+?)\s?$/) { $bv = $1; }
- }
- my $cmd = "";
- $cmd .= $map;
- my $sauce = $file;
- $sauce =~ s/^.+[\/\\]//;
- if ($to_auto == 0) { $sauce .= " -- $ss to $to"; }
- if ($#vf != -1) { $cmd .= " -vf " . join(",",@vf) . join("", @flags); }
- $cmd .= " -pix_fmt +yuv420p -c:v libvpx-vp9 -b:v " . $bv . " -frame-parallel 0 -tile-columns 2 -threads 4";
- #my $other = &q('add stuff to cmd[]> ');
- #if ($other ne "") { $cmd .= " " . $other; }
- my $okay = &q('Continue[Y/n]> ');
- if ($okay =~ /n/i) { exit 0; }
- my $frames = sprintf "%d", $elapsed * $framerate;
- print "ffmpeg -y -v panic " . $slice . "-i \"$file\" " . $cmd . " -speed 4 -an -f webm -passlogfile $time -pass 1 NUL\n";
- {
- my $pid = open IN, "-|", "ffmpeg -y -progress - -v panic " . $slice . "-i \"$file\" " . $cmd . " -speed 4 -an -f webm -passlogfile $time -pass 1 NUL";
- local $SIG{KILL} = $SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {
- print "Interrupted, exiting\n";
- kill "TERM", $pid;
- unlink $time . '-0.log';
- exit 1;
- };
- local $| = 1;
- my $timelast = [gettimeofday];
- my $speed = 0;
- my $eta = "N/A";
- while (1) {
- $_ = <IN>;
- last if m/progress=end/;
- next unless m/frame=(\d+?)$/;
- my $index = $1;
- my $pct = $index * 100 / $frames;
- my $interval = tv_interval($timelast);
- if ($interval > 0.2) {
- #$speed = (.9 * $speed) + ($index / $interval) * .1;
- $speed = $index / $interval;
- $eta = &sec2eta(sprintf "%.2f", ($frames - $index) / $speed) if ($speed > 0);
- }
- printf "\r[pass 1] %.2f%% / %.4f fps / ETA %-12s", $pct, $speed, $eta;
- }
- #printf "\r%-60s\n", "[pass 1] finished";
- }
- die "can't find log from first pass, probably failed!\n" unless -f $time . '-0.log';
- print "\rffmpeg -y -v panic -progress - " . $slice . "-i \"$file\" " . $cmd . " -speed 1" . $audio . " -metadata title=\"$sauce\" -f webm -passlogfile $time -pass 2 " . $time . ".webm\n";
- {
- my $pid = open IN, "-|", "ffmpeg -y -v panic -progress - " . $slice . "-i \"$file\" " . $cmd . " -speed 1" . $audio . " -metadata title=\"$sauce\" -f webm -passlogfile $time -pass 2 " . $time . ".webm";
- local $SIG{KILL} = $SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {
- print "Interrupted, exiting\n";
- kill "TERM", $pid;
- unlink $time . '-0.log';
- exit 1;
- };
- local $| = 1;
- my $timelast = [gettimeofday];
- my $speed = 0;
- my $eta = "N/A";
- while (1) {
- $_ = <IN>;
- last if m/progress=end/;
- next unless m/frame=(\d+?)$/;
- my $index = $1;
- my $pct = $index * 100 / $frames;
- my $interval = tv_interval($timelast);
- if ($interval > 0.2) {
- #$speed = (.9 * $speed) + ($index / $interval) * .1;
- $speed = $index / $interval;
- $eta = &sec2eta(sprintf "%.2f", ($frames - $index) / $speed) if ($speed > 0);
- }
- printf "\r[pass 2] %.2f%% / %.4f fps / ETA %-12s", $pct, $speed, $eta;
- }
- #printf "\r%-60s\n", "[pass 2] finished";
- unlink $time . '-0.log';
- }
- #print "encode took " . tv_interval($encode_start) . " seconds\n";
- print "\r", "=" x 80, "\n";
- `mkvmerge -w -o ${time}.1.webm ${time}.webm`;
- rename "${time}.1.webm", "${time}.webm";
- my $out_size = (-s $time . ".webm")/(1024*1024);
- print $name . ".webm @ ${out_size} MiB completed!\n";
- if ($bv =~ m/crf/) { exit 0; }
- if ($out_size < 7.9) {
- printf "could try with a larger bitrate, suggested = %d\n", ($bv * 8 / $out_size);
- if ($name ne $time) { rename $time . ".webm", $name . ".webm"; }
- exit 0;
- }
- while ($out_size > 8) {
- #my $retry = &q("it was too big, reencode the audio? [Y/n]> ");
- #if ($retry !~ /n/i) {
- $abr = int($abr * .9);
- print "output too large, reencoding audio new bitrate = $abr\n";
- `ffmpeg -v panic -y -i \"$file\" -ss $ss -to $to -vn -c:a libopus -b:a $abr -ac $ac ${time}.ogg`;
- `mkvmerge -w -o ${time}.1.webm -A ${time}.webm ${time}.ogg`;
- rename "$time.1.webm", "$time.webm";
- unlink "$time.ogg";
- $out_size = (-s $time . ".webm")/(1024*1024);
- print "new size $out_size\n";
- #} else { last; }
- }
- $out_size = (-s $time . ".webm")/(1024*1024);
- print "final size $out_size MiB\n";
- if ($name ne $time) { rename $time . ".webm", $name . ".webm"; }
- sub sec2eta {
- my $seconds = shift;
- if ($seconds < 0) { $seconds *= -1; }
- my ($minutes,$hours,$days,$weeks) = (0,0,0,0);
- while ($seconds >= 86400*7) { $weeks++; $seconds -= 86400*7; }
- while ($seconds >= 86400) { $days++; $seconds -= 86400; }
- while ($seconds >= 3600) { $hours++; $seconds -= 3600; }
- while ($seconds >= 60) { $minutes++; $seconds -= 60; }
- my $out = "";
- if ($weeks) { $out .= $weeks . "w"; }
- if ($days) { $out .= $days . "d"; }
- if ($hours) { $out .= $hours . "h"; }
- if ($minutes) { $out .= $minutes . "m"; }
- if ($out ne "") { $seconds = int($seconds); }
- if ($seconds) { $out .= $seconds . "s"; }
- return $out;
- }
- sub q {
- print $_[0];
- $_ = <STDIN>;
- chomp $_;
- return $_;
- }
- sub sec2time {
- my $s = shift;
- my $h = my $m = 0;
- while ($s >= 3600) { $s -= 3600; ++$h; }
- while ($s >= 60) { $s -= 60; ++$m; }
- return $h > 0 ? sprintf "%02d:%02d:%s", $h, $m, $s<10?"0".$s:$s : sprintf "%02d:%s", $m, $s<10?"0".$s:$s;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement