なので勝手にmodしました
・exe版と同じように-Lオプション対応させた
・日本語のダメ文字に・・・自分が困らない程度に対応
・mp4boxが新しい場合に対応(nhmlの出力が最近はなんか違う)
そういえば、vfrにする時って多分プロファイル(最大ビットレート)が不正になるんですよね。
なんせ普通にエンコードしたのに2.5(60/24)倍のフレームを詰め込むことになるので。
x264で直接tcfileinを指定していたときはどうだったんだろう・・・
#!/usr/bin/perl
# author: zmi
# version: 01/24/2007 mod 03/09/2018
# info: http://d.hatena.ne.jp/zmi/ (japanese)
# info: http://forum.doom9.org/showthread.php?t=112199 (english)
#使い方:
# mp4box -nhml 1 cfr.mp4
# (VFR変換スクリプト) -i mkv-timecodesfile.txt -n cfr_track1.nhml -o vfr_track1.nhml
# mp4box -add vfr_track1.nhml -new vfr_track1.mp4
# mp4box -add vfr_track1.mp4 -add cfr.aac -new vfr.mp4
use Getopt::Std;
use Math::BigInt;
use File::Basename;
use Encode;
$usage = <<USAGE
tc2mp4 (01/24/2007 mod 03/09/2018)
usage: tc2mp4 -i InputFile -o OutputFile -t TimecodeFile [-k] [-n TrackNumber] [-L mp4boxpath]
USAGE
;
getopts('i:t:o:n:kL:h');
die $usage if $opt_h;
$srcMP4 = $opt_i or die $usage;
$dstMP4 = $opt_o or die $usage;
$srcTC = $opt_t or die $usage;
$number = ($opt_n or 1);
$keep = $opt_k;
$mp4box = ($opt_L or 'mp4box');
@extlist = ('\....$');
($basename, $dir) = fileparse(Encode::decode('cp932',"$opt_i"), @extlist);
$basename=Encode::encode('cp932',$basename);
$dir=Encode::encode('cp932',$dir);
$srcNHML = "${dir}${basename}_track$number.nhml";
# $tcNHML = "${dir}${basename}_track$number"."_tc.nhml";mp4boxには0.72rev359現在バグがあるようで、wide characterのファイルをnhmlとしてインポートできない(file errorになる) 出力ファイルにはできるが・・・
$tcNHML = "${dir}_track$number\_tc.nhml"; #ファイル名に2byte文字を含まないように なのでテンポラリファイルがカレントフォルダに作られる
doCmd("\"$mp4box\" -nhml $number \"$srcMP4\"");
$tcfv = detectTCFVersion($srcTC);
print "[Timecode info]\n";
print " Timecode format(v$tcfv)\n";
if($tcfv == 1){
@timescales = parseTimecodeV1($srcTC);
$ntsc = detectNTSC($srcTC);
for($frame = 0; $frame < @timescales; $frame++){
$factors{$timescales[$frame]}++;
}
$lcmTimescale = Math::BigInt::blcm(keys %factors);
# generate CTS from timecode
@cts = toCTS(\@timescales, $lcmTimescale, $ntsc);
print " Total frames(".@cts."), LCM Timescale($lcmTimescale)\n";
print " " . ($ntsc ? 'NTSC, ' : '') ."Factors(" . join('/',sort keys %factors) . ")\n";
}elsif($tcfv == 2){
$lcmTimescale = 1000000;
@cts = parseTimecodeV2($srcTC);
print " Total frames(".@cts."), Timescale($lcmTimescale)\n";
}
# read Timebase from source nhml
$timebase = readTimebase($srcNHML);
print "[NHML info]\n";
print " Timebase($timebase)\n";
applyTimecode($srcNHML, $tcNHML, $timebase, \@cts);
doCmd("\"$mp4box\" -add \"$tcNHML\" -new \"$dstMP4\"");
unlink("$srcNHML");
unlink("$tcNHML") unless $keep;
unlink("${dir}${basename}_track$number.info","${dir}${basename}_track$number.media");
doCmd("\"$mp4box\" -info \"$dstMP4\"");
print "Completed.\n";
exit 0;
sub doCmd{
my $cmd = shift @_;
print "$cmd\n";
system($cmd) and die $!;
}
sub applyTimecode{
my $srcNHML = shift @_;
my $tcNHML = shift @_;
my $timebase = shift @_;
my @cts = @{shift @_};
my $dtsFrame = 0,$dts;
my $ctsFrame,$ctsOffset,$cts;
my $delayFrame,$delayTC;
open SRC_NHML, "<$srcNHML" or die $!;
open TC_NHML, ">$tcNHML" or die $!;
while(<SRC_NHML>){
s/timeScale="\d+"/timeScale="$lcmTimescale"/ if(/<NHNTStream.*?>/);
((print TC_NHML), next) unless /<NHNTSample.*>/;
next unless $dtsFrame < @cts;
/CTSOffset="(\d+)"/;
$ctsOffset = ($1 or 0);
$ctsFrame = $dtsFrame + int($ctsOffset / $timebase + 0.5);
if($dtsFrame == 0){
$delayFrame = $ctsFrame;
$delayTC = $cts[$delayFrame];
print "[Delay]\n";
print " Frames($delayFrame), Timecode($delayTC) Milliseconds(" . ($delayTC * 1000 / $lcmTimescale) . ")\n";
}
if($dtsFrame < $delayFrame){
$dts = $cts[$dtsFrame];
$cts = $cts[$ctsFrame];
}else{
$dts = $cts[$dtsFrame - $delayFrame] + $delayTC;
$cts = $cts[$ctsFrame - $delayFrame] + $delayTC;
}
$ctsOffset = $cts - $dts;
s/DTS="\d+"/DTS="$dts"/;
s/CTSOffset="\d+"/CTSOffset="$ctsOffset"/;
#s/(dataLength="\d+")/$1 CTSOffset="$ctsOffset"/ if $ctsOffset == 0; # hack for mp4box's bug
print TC_NHML;
$dtsFrame++;
}
close SRC_NHML;
close TC_NHML;
}
sub toCTS{
my @timescales = @{shift @_};
my $lcmTimescale = shift @_;
my $ntsc = shift @_;
my @cts, $frame, $duration;
my $step = $ntsc ? 1001 : 1000;
$cts[0] = 0;
for($frame = 1; $frame < @timescales; $frame++){
$duration = ($lcmTimescale / $timescales[$frame -1]) * $step;
$cts[$frame] = $cts[$frame -1] + $duration;
}
return @cts;
}
sub readTimebase{
my $nhmlFile = shift @_;
my @dts;
my $timebase;
my $count = 0;
open NHML, "<$nhmlFile" or die $!;
while(<NHML>){
next unless /<NHNTSample.*DTS="(\d+)".*>/;
$dts[$count] = $1;
last if (++$count >= 10);
}
close NHML;
$timebase = $dts[1] - $dts[0];
$avgTimebase = ($dts[$count - 1] - $dts[0]) / ($count - 1);
die "Input file is not CFR." if ($timebase != $avgTimebase);
return $timebase;
}
sub toTimescale{
my $fps = shift @_;
my $timescale = int($fps * 1.001 + 0.5) * 1000;
return $timescale;
}
sub detectTCFVersion{
my $timecodeFile = shift @_;
my $tcfv;
open TC, "<$timecodeFile" or die $!;
while(<TC>){
next unless /^\#\s+timecode format v([1-2])/;
$tcfv = $1;
}
close TC;
defined $tcfv or die "Only timecode format v1/v2 is supported.";
return $tcfv;
}
sub detectNTSC{
my $timecodeFile = shift @_;
my $assume;
open TC, "<$timecodeFile" or die $!;
while(<TC>){
next if /^\#/;
next unless /^Assume ([0-9.]+)/;
$assume = $1;
}
close TC;
if(toTimescale($assume) == $assume * 1000){
return 0;
}else{
return 1;
}
}
sub parseTimecodeV1{
my $timecodeFile = shift @_;
my $ntsc = shift @_;
my @timescale;
my $assume;
my $begin,$end,$last,$fps;
my $frame = 0;
open TC, "<$timecodeFile" or die $!;
while(<TC>){
($last = $1, next)
if /^\# TDecimate Mode.*Last Frame = (\d+)/;
next if /^\#/;
($assume = $1, next)
if /^Assume ([0-9.]+)/;
next unless /(\d+),(\d+),([\d.]+)/;
($begin, $end, $fps) = ($1, $2, $3);
for($frame; $frame < $begin; $frame++){
$timescale[$frame] = toTimescale($assume);
}
for($frame = $begin; $frame <= $end; $frame++){
$timescale[$frame] = toTimescale($fps);
}
}
for(;$frame <= $last; $frame++){
$timescale[$frame] = toTimescale($assume);
}
close TC;
$ntsc = 1111;
return @timescale;
}
sub parseTimecodeV2{
my $timecodeFile = shift @_;
my @cts;
my $frame = 0;
open TC, "<$timecodeFile" or die $!;
while(<TC>){
next if /^\#/;
next unless /([\d.]+)/;
$cts[$frame++] = int($1 * 1000 + 0.5);
}
close TC;
return @cts;
}
0 件のコメント:
コメントを投稿