Advertisement
Guest User

Untitled

a guest
Aug 28th, 2016
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 8.99 KB | None | 0 0
  1. use strict;
  2. use warnings "all";
  3. use JSON;
  4. use Time::HiRes qw(tv_interval gettimeofday);
  5.  
  6. my $file = "@ARGV";
  7. my $target = 67108864;
  8.  
  9. my $abr = 64000;
  10. my $ac = 2;
  11.  
  12. my @vf;
  13. my @flags;
  14.  
  15. die "can't open file" unless -f $file;
  16. if ($file =~ /\/|\\/) {
  17.     $file =~ s/^(.+)[\\\/]//;
  18.     chdir $1;
  19. }
  20. my $safename = $file;
  21. $safename =~ s/([\[\]:,])/\\$1/g;
  22. my $time = time();
  23.  
  24. my $json = decode_json `ffprobe -v panic -print_format json -show_format \"$file\"`;
  25. my $format = $json->{format};
  26. my $duration = $format->{duration} || 1;
  27. my $oldbr = $format->{bit_rate} || 1;
  28. my $ssreams = $format->{nb_streams} || 1;
  29. $json = decode_json `ffprobe -v panic -print_format json -show_streams \"$file\"`;
  30.  
  31.  
  32. my $ss = &q('starting time[00:00]> ');
  33. $ss =~ s/[^0-9:.]//g;
  34. my $to = &q('ending time[' . &sec2time($duration) . ']> ');
  35. $to =~ s/[^0-9:.]//g;
  36.  
  37. my $elapsed = 0;
  38. my $ss_seconds = 0;
  39. my $ss_auto = 0;
  40. my $to_auto = 0;
  41. my $slice = "";
  42. if ($ss eq "") { $ss = 0; $ss_auto = 1; }
  43. if ($to eq "") { $to = &sec2time($duration); $to_auto = 1; }
  44. {
  45.     my @s = reverse split /:/, $ss;
  46.     my @e = reverse split /:/, $to;
  47.     if ($#e == 2) { $s[2] //= 0; $elapsed += (($e[2] - $s[2]) * 3600); $ss_seconds += 3600 * $s[2]; }
  48.     if ($#e >= 1) { $s[1] //= 0; $elapsed += (($e[1] - $s[1]) * 60); $ss_seconds += 60 * $s[1]; }
  49.     $elapsed += ($e[0] - $s[0]);
  50.     $ss_seconds += $s[0];
  51. }
  52.  
  53. die "couldn't calculate duration\n" unless $elapsed;
  54. $slice = "-ss $ss -t $elapsed ";# if ($ss_auto == 0 || $to_auto == 1);
  55.  
  56. my $maxbv = sprintf "%d", $target / $elapsed;
  57. my $audio = "";
  58. my $map = "";
  59. my ($framerate, $width, $height, $substream);
  60. if ($ssreams > 1) {
  61.     my $vstream = -1;
  62.     my $astream = -1;
  63.     for (@{$json->{streams}}) {
  64.         if ($vstream == -1 && $_->{codec_type} eq "video") { $vstream = $_->{index}; }
  65.         if ($_->{codec_type} eq "audio") {
  66.             if ($astream == -1) { $astream = $_->{index}; }
  67.             if (defined $_->{tags}->{language} && $_->{tags}->{language} eq "jpn") { $astream = $_->{index}; }
  68.         }
  69.         if ($_->{codec_type} eq "subtitle") {
  70.             my $subs = &q('subtitles[Y/n]> ');
  71.             if ($subs !~ /n/i) {
  72.                 if ($ss_seconds > 0) {
  73.                     push @vf, "setpts=PTS+$ss_seconds/TB";
  74.                     push @vf, "subtitles=\"$safename\"";
  75.                     push @vf, "setpts=PTS-STARTPTS";
  76.                 } else { push @vf, "subtitles=\"$safename\""; }
  77.             }
  78.         }
  79.     }
  80.     #$astream = 1; ??? why was this here
  81.     $json->{streams}[$vstream]->{r_frame_rate} =~ m/^([0-9.]+?)\/([0-9.]+?)$/;
  82.     $framerate = $1 / $2;
  83.     $width = $json->{streams}[$vstream]->{width};
  84.     $height = $json->{streams}[$vstream]->{height};
  85.     $map = "-map 0:" . $vstream;
  86.     if ($astream != -1) {
  87.         $audio = &q('audio[64k/n]> ');
  88.         if ($audio !~ /n/) {
  89.             if ($audio =~ /^(\d+?)$/) { $abr = $1 * 1000; }
  90.             if ($json->{streams}[$astream]->{channels} == 1) { $ac = 1; $abr /= 2; }
  91.             $map .= " -map 0:" . $astream;
  92.             $audio = " -c:a libopus -b:a ${abr} -ac $ac";
  93.             $maxbv -= $abr;
  94.         } else { $audio = " -an"; }
  95.     }
  96. }
  97.  
  98. my $crop = &q('crop[w:h:x:y/N]> ');
  99. if ($crop !~ /n/i && $crop ne "") { push @vf, "crop=" . $crop; }
  100. my $scale = &q('scale[w:h/N]> ');
  101. if ($scale !~ /n/i && $scale ne "") { push @vf, "scale=" . $scale; push @flags, " -sws_flags lanczos"; }
  102. if ($framerate > 24) {
  103.     my $fps = &q("fps[$framerate]> ");
  104.     if ($fps ne "") { push @vf, "fps=fps=$fps"; $framerate = $fps; }
  105. }
  106.  
  107. my $name = &q('Name file[newname/' . $time . ']> ');
  108. if ($name eq "") { $name = $time; }
  109. print "runtime : $elapsed seconds\n";
  110. my $bv;
  111. #hacky
  112. if ($elapsed < 20) { $bv = "0 -crf 18"; }
  113. else {
  114.     if ($oldbr == 0) { $oldbr = 1; }
  115.     my $delta = ($maxbv/$oldbr) * 100;
  116.     printf "\n\nold bitrate: %d\nmax bitrate: %d\nbr change: %d %%\n", $oldbr, $maxbv, $delta;
  117.  
  118.     for (qw(70 65 60 55)) {
  119.         my $n = sprintf "%d", ($_ / 100) * $oldbr;
  120.         if ($n > $maxbv) { next; }
  121.         if (!$bv) { $bv = $n; }
  122.         print "bitrate $_%: $n\n";
  123.     }
  124.     if (!$bv) { $bv = $maxbv; }
  125.     my $minbv = int(.4 * $oldbr);
  126.     print "min bitrate: $minbv (40%)\n";
  127.  
  128.     if ($minbv > $maxbv) { $bv = $maxbv; }
  129.     if ($bv > $oldbr) { $bv = $minbv; }
  130.  
  131.     print "bitrate[$bv/#]> ";
  132.     my $go = <STDIN>;
  133.     if ($go =~ /^([0-9.]+?)\s?$/) { $bv = $1; }
  134. }
  135.  
  136. my $cmd = "";
  137. $cmd .= $map;
  138. my $sauce = $file;
  139. $sauce =~ s/^.+[\/\\]//;
  140. if ($to_auto == 0) { $sauce .= " -- $ss to $to"; }
  141. if ($#vf != -1) { $cmd .= " -vf " . join(",",@vf) . join("", @flags); }
  142. $cmd .= " -pix_fmt +yuv420p -c:v libvpx-vp9 -b:v " . $bv . " -frame-parallel 0 -tile-columns 2 -threads 4";
  143.  
  144. #my $other = &q('add stuff to cmd[]> ');
  145. #if ($other ne "") { $cmd .= " " . $other; }
  146.  
  147. my $okay = &q('Continue[Y/n]> ');
  148. if ($okay =~ /n/i) { exit 0; }
  149.  
  150. my $frames = sprintf "%d", $elapsed * $framerate;
  151.  
  152. print "ffmpeg -y -v panic " . $slice . "-i \"$file\" " . $cmd . " -speed 4 -an -f webm -passlogfile $time -pass 1 NUL\n";
  153.  
  154. {
  155.     my $pid = open IN, "-|", "ffmpeg -y -progress - -v panic " . $slice . "-i \"$file\" " . $cmd . " -speed 4 -an -f webm -passlogfile $time -pass 1 NUL";
  156.     local $SIG{KILL} = $SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {
  157.         print "Interrupted, exiting\n";
  158.         kill "TERM", $pid;
  159.         unlink $time . '-0.log';
  160.         exit 1;
  161.     };
  162.     local $| = 1;
  163.     my $timelast = [gettimeofday];
  164.     my $speed = 0;
  165.     my $eta = "N/A";
  166.     while (1) {
  167.         $_ = <IN>;
  168.         last if m/progress=end/;
  169.         next unless m/frame=(\d+?)$/;
  170.         my $index = $1;
  171.         my $pct = $index * 100 / $frames;
  172.         my $interval = tv_interval($timelast);
  173.         if ($interval > 0.2) {
  174.             #$speed = (.9 * $speed) + ($index / $interval) * .1;
  175.             $speed = $index / $interval;
  176.             $eta = &sec2eta(sprintf "%.2f", ($frames - $index) / $speed) if ($speed > 0);
  177.         }
  178.         printf "\r[pass 1] %.2f%% / %.4f fps / ETA %-12s", $pct, $speed, $eta;
  179.     }
  180.     #printf "\r%-60s\n", "[pass 1] finished";
  181. }
  182.  
  183. die "can't find log from first pass, probably failed!\n" unless -f $time . '-0.log';
  184. 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";
  185.  
  186. {
  187.     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";
  188.     local $SIG{KILL} = $SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {
  189.         print "Interrupted, exiting\n";
  190.         kill "TERM", $pid;
  191.         unlink $time . '-0.log';
  192.         exit 1;
  193.     };
  194.     local $| = 1;
  195.     my $timelast = [gettimeofday];
  196.     my $speed = 0;
  197.     my $eta = "N/A";
  198.     while (1) {
  199.         $_ = <IN>;
  200.         last if m/progress=end/;
  201.         next unless m/frame=(\d+?)$/;
  202.         my $index = $1;
  203.         my $pct = $index * 100 / $frames;
  204.         my $interval = tv_interval($timelast);
  205.         if ($interval > 0.2) {
  206.             #$speed = (.9 * $speed) + ($index / $interval) * .1;
  207.             $speed = $index / $interval;
  208.             $eta = &sec2eta(sprintf "%.2f", ($frames - $index) / $speed) if ($speed > 0);
  209.         }
  210.         printf "\r[pass 2] %.2f%% / %.4f fps / ETA %-12s", $pct, $speed, $eta;
  211.     }
  212.     #printf "\r%-60s\n", "[pass 2] finished";
  213.     unlink $time . '-0.log';
  214. }
  215. #print "encode took " . tv_interval($encode_start) . " seconds\n";
  216. print "\r", "=" x 80, "\n";
  217.  
  218. `mkvmerge -w -o ${time}.1.webm ${time}.webm`;
  219. rename "${time}.1.webm", "${time}.webm";
  220.  
  221. my $out_size = (-s $time . ".webm")/(1024*1024);
  222. print $name . ".webm @ ${out_size} MiB completed!\n";
  223. if ($bv =~ m/crf/) { exit 0; }
  224. if ($out_size < 7.9) {
  225.     printf "could try with a larger bitrate, suggested = %d\n", ($bv * 8 / $out_size);
  226.     if ($name ne $time) { rename $time . ".webm", $name . ".webm"; }
  227.     exit 0;
  228. }
  229.  
  230. while ($out_size > 8) {
  231.     #my $retry = &q("it was too big, reencode the audio? [Y/n]> ");
  232.     #if ($retry !~ /n/i) {
  233.         $abr = int($abr * .9);
  234.         print "output too large, reencoding audio new bitrate = $abr\n";
  235.         `ffmpeg -v panic -y -i \"$file\" -ss $ss -to $to -vn -c:a libopus -b:a $abr -ac $ac ${time}.ogg`;
  236.         `mkvmerge -w -o ${time}.1.webm -A ${time}.webm ${time}.ogg`;
  237.         rename "$time.1.webm", "$time.webm";
  238.         unlink "$time.ogg";
  239.         $out_size = (-s $time . ".webm")/(1024*1024);
  240.         print "new size $out_size\n";
  241.     #} else { last; }
  242. }
  243. $out_size = (-s $time . ".webm")/(1024*1024);
  244. print "final size $out_size MiB\n";
  245. if ($name ne $time) { rename $time . ".webm", $name . ".webm"; }
  246.  
  247.  
  248. sub sec2eta {
  249.     my $seconds = shift;
  250.     if ($seconds < 0) { $seconds *= -1; }
  251.     my ($minutes,$hours,$days,$weeks) = (0,0,0,0);
  252.     while ($seconds >= 86400*7) { $weeks++; $seconds -= 86400*7; }
  253.     while ($seconds >= 86400) { $days++; $seconds -= 86400; }
  254.     while ($seconds >= 3600) { $hours++; $seconds -= 3600; }
  255.     while ($seconds >= 60) { $minutes++; $seconds -= 60; }
  256.     my $out = "";
  257.     if ($weeks) { $out .= $weeks . "w"; }
  258.     if ($days) { $out .= $days . "d"; }
  259.     if ($hours) { $out .= $hours . "h"; }
  260.     if ($minutes) { $out .= $minutes . "m"; }
  261.     if ($out ne "") { $seconds = int($seconds); }
  262.     if ($seconds) { $out .= $seconds . "s"; }
  263.     return $out;
  264. }
  265.  
  266.  
  267. sub q {
  268.     print $_[0];
  269.     $_ = <STDIN>;
  270.     chomp $_;
  271.     return $_;
  272. }
  273.  
  274. sub sec2time {
  275.     my $s = shift;
  276.     my $h = my $m = 0;
  277.     while ($s >= 3600) { $s -= 3600; ++$h; }
  278.     while ($s >= 60) { $s -= 60; ++$m; }
  279.     return $h > 0 ? sprintf "%02d:%02d:%s", $h, $m, $s<10?"0".$s:$s : sprintf "%02d:%s", $m, $s<10?"0".$s:$s;
  280. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement