#!/usr/bin/perl -w
# Parameter
# $1 -> Inputfile : mpg
# $2 --> Target Size in Mbyte ( aka 220 )

use strict ;
## neddet binarys
# transcode
# x264 ( the pure encoder )
# faac
# mp4creator from mpg4
print "Design study for transcoding vdr to mp4 on ultra low video bitrates from alex Richter last edit 2008-08-12\n" ;
print "transcode to h264 mp4 with transcode and piped x264, also transcode Audio from mpg to aac for mp4\n" ;

#transcode_avi_2_h264.pl [Infile] [Targetsize/Mbyte]
#/transcode_avi_2_h264.pl ./Mythbusters-Die_Wissensjäger-Kleinwagenkontakt.mpg myth.mpg 220

my $use="
use myprog File1 TargetSize File2 TargetSize File3 TargetSize\n" ;

unless ( defined @ARGV ) { print $use ; exit (1) } ;


# x264 Coder setting
my $x264_coder_setting="smooth" ; # "smooth" or "sharp"

my $begin=localtime ;

my $z= -1 ;
my @files ;
my @sizes ;
foreach ( @ARGV ) {
	$z++ ;
	if ( $z % 2 == 0 and  -f $_ ) { push @files , $_  }
	elsif ( $z % 2 != 0 and $_ =~ /\d+/ ) { push @sizes , $_  }
}

my $y = -1 ;
foreach ( @files ) { $y++ ; do_transcode($_ , $sizes[$y]) }
#foreach ( @sizes ) { print "$_\n" ; }



sub do_transcode {

my ($InFile , $target_size) = @_ ;
my $OutFile ;
( $OutFile = $InFile ) =~ s/\.\w+$/\.mp4/ ;
$OutFile = dosh ($OutFile) ;
$InFile = dosh ($InFile) ;

print "$InFile --> $OutFile TargetSize --> $target_size\n" ;

print "\n";
# frames ermitteln
my $frames ;
#unless ( defined $ARGV[3] and $ARGV[3] =~ /^\d+$/ ) {
	print "° get Frames..." ;
	$frames = getframesall("$InFile") ; print "$frames " ;
	print  "OK\n" ;
#}
# Breite x Höhe im original Bildverhältnis
print "° get Orig Picture Sizes..." ;
my ($OBreite , $OHoehe , $Bildfaktor , $Show) = AVPicSize("$InFile") ; print " $OBreite x $OHoehe , $Show " ;
print  "OK\n" ;
# Stauchfaktor transcode
my $stretch = getstretch( $OBreite , $OHoehe , $Bildfaktor ) ;
# Beschnitt und deinterlace
print "° get Black Border, Deinterlace Informations..." ;
my ($YCrop , $Deint ) = deint_crop( $InFile ,  $stretch ); print " $YCrop , $Deint " ;
print  "OK\n" ;

my @Crop = split ("," , $YCrop) ;
my $ZielBreite = $OBreite - $Crop[1] - $Crop[3] ;
my @stretcharr = split ("," , $stretch ) ;
my $ZielHoehe = $OHoehe - ( $stretcharr[0] * $stretcharr[2] ) -  $Crop[0] - $Crop[2] ; print "° Target Picture Size : $ZielBreite x $ZielHoehe\n" ;


# overwrite max frames per fourth argument
#if ( defined $ARGV[3] and $ARGV[3] =~ /^\d+$/ ) { $frames = $ARGV[3] } ;






#--pass 2 --bitrate 277 --stats ".stats" --ref 5 --mixed-refs --bframes 5 --b-pyramid --b-rdo --bime --weightb --direct temporal --filter -2,2 --subme 7 --trellis 1 --analyse all --8x8dct --vbv-maxrate 25000 --me esa --threads auto --thread-input --progress --no-psnr --no-ssim

#taken from http://www.yuvsoft.com/pdf/x264_comparison.html#_Toc158095452 excersice 46
#-b 4 ??b-pyramid -r 8 ??analyse=all ??direct auto ??weightb ??me umh ??subme 7 ??b-rdo ??bime -8 --threads auto --no-psnr --no-ssim ??pass 1
#-b 4 ??b-pyramid -r 8 ??analyse=all ??direct auto ??weightb ??me umh ??subme 7 ??b-rdo ??bime -8 --threads auto --no-psnr --no-ssim ??pass 2
my $opts_pass_1="-b 4 --b-pyramid -r 8 --analyse=all --direct auto --weightb --me umh --subme 7 --b-rdo --bime -8 --threads auto --no-psnr --no-ssim" ;
my $opts_pass_2="-b 4 --b-pyramid -r 8 --analyse=all --direct auto --weightb --me umh --subme 7 --b-rdo --bime -8 --threads auto --no-psnr --no-ssim" ;

if ( $x264_coder_setting=~/sharp/ ) {
$opts_pass_1="--level 3.1 -I 250 --b-pyramid --no-psnr --no-ssim --qcomp 0.71 -r 1  --partitions all --8x8dct --bframes 16  --no-dct-decimate --trellis 1 --me dia --subme 7 --ratetol 1.5  --threads auto --direct auto" ;
$opts_pass_2="--level 3.1 -I 250 --b-pyramid --no-psnr --no-ssim --qcomp 0.71 -r 4  --bime --weightb --partitions all --8x8dct  --bframes 16 --b-rdo --no-dct-decimate --trellis 1 --me umh --subme 7 --ratetol 1.5 --mixed-refs --threads auto --direct auto" ;
}

#my $size="480x256" ;
#my $target_size="$ARGV[2]" ;
my $threads="4" ;
my $count="-c 1-$frames" ;
#my $count="-c 1-3000" ;
#opts_pass_1="--level 3.1 -I 250 --b-pyramid --no-psnr --no-ssim --qcomp 0.71 -r 1 --merange 24 --partitions none --bframes 16 --deblock 2:1 --no-dct-decimate --trellis 1 --me dia --subme 1 --ratetol 2.0  --threads auto --direct auto"
#opts_pass_2="--level 3.1 -I 250 --b-pyramid --no-psnr --no-ssim --qcomp 0.71 -r 4 --merange 24 --bime --weightb --partitions all --8x8dct --deblock 2:1 --bframes 16 --b-rdo --no-dct-decimate --trellis 1 --me umh --subme 7 --ratetol 2.0 --mixed-refs --threads auto --direct auto"

# Audio first, we need the size of the resulting aac Files
unlink "test.wav" ;
system ("mkfifo -m 660 test.wav") ;
system ("transcode -i $InFile $count --progress_off -x vob  -y null,wav -u 1024 $count -s 0.9 -J normalize -g 0x0  -a 0 -O -o test.wav 1>&2 2>/dev/null & nice -10 faac -q 75 -c 14000 --mpeg-vers 4 -o out.aac test.wav") ;
# ~ 384 fps
unlink "test.wav" ;

# Target Video Data rate
my $aac_size = ( -s "out.aac") / 1024 ; # Size of resulting aac File in kbyte
my $bitrate = Bitrateberech ( $target_size , $aac_size , $frames ) ;

#my $end = localtime ;
#print "$begin\n$end\n" ;
#exit 1 ;



unlink "test.y4m" ;
system ("mkfifo -m 660 test.y4m") ;
#-B 10,0,16 720x416
# -Z 640x368 640x368
# -B 10,0,16
system ("transcode -i $InFile $Deint -B $stretch -Y $YCrop -u 1024,$threads -k -y yuv4mpeg $count  -o ./test.y4m 1>&2 2>/dev/null & nice -n 5 x264 $opts_pass_1 -B $bitrate --pass 1 -o /dev/null test.y4m ${ZielBreite}x${ZielHoehe}") ;
print "\n° first done\n\n" ;

system ("transcode -i $InFile $Deint -B $stretch -Y $YCrop -u 1024,$threads -k -y yuv4mpeg $count  -o ./test.y4m 1>&2 2>/dev/null & nice -n 5 x264 $opts_pass_2 -B $bitrate --pass 2 -o out.264 test.y4m ${ZielBreite}x${ZielHoehe}") ;
# ~ 23 fps on 4 threads
unlink "test.y4m" ;
unlink "x264_2pass.log" ;
print  "\n° second done\n\n" ;

system ("mp4creator -create=out.264 -rate 25 $OutFile && mp4creator -create=out.aac  $OutFile && mp4creator -hint=1 $OutFile && mp4creator -hint=2 $OutFile &&  mp4creator -optimize $OutFile && mp4creator -list $OutFile") ;
unlink "out.264" ;
unlink "out.aac" ;

my $end = localtime ;
print "$begin\n$end\n" ;
}
#------------------------------------------------------------------------------
sub dosh {
my $in = $_[0] ;
$in =~ s/\s+/\ /g ; ## doppelte oder mehrere Leerzeichen auf eins reduzieren
$in =~ s/([\s \( \) \$ \& \§ \" \! \? \[  \] \' \,])/\\$1/g; # viele absonderliche Sonderzeichen für die shell qouten
$in =~ s/\\\\/\\/g ; # wenn ausdruck vorher schon geqotet war, die doppelten backslashes wieder auf einen kompenisieren
return $in ;
}
#------------------------------------------------------------------------------
sub getframesall {
# bekommt $Info{QFile} , $Info{InfoDir}
### ermittelt die Anzahl der Frames zu einem File
	my $QFile = dosh($_[0]) ;
#	my $InfoDir = $_[1] ;
	my $Frames="undef" ; # immer erst undef setzen , da wenn nicht erster Durchlauf der Erfassung last if $Frames immer erfüllt !
	my $frames ;
#		print "\$QFile bei getframesall $QFile\n\$InfoDir bei getframesall $InfoDir\n" ;

# ${Home}mpeg2avi/Info/${$NameOut}/frames.dat ;

		open TCS, "nice -n 2 tcdemux -i $QFile -W 2>&1 1>/dev/null |" ;
		while ( my $Line = <TCS> ) {
#	print "$Line" ;
			if ( $Line =~ /.*\ (\d+)\ .*video.*/ ) {
			$frames = $1 ;
			} ;
		}
		close TCS  ;
#		open LOG ,  ">${InfoDir}/frames.dat" ;
#		print LOG "$frames\n" ;
#		close LOG ;
		$Frames = $frames ;
	return $Frames ;
}
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
sub AVPicSize {
	my ( $OBreite , $OHoehe ) ;
	my ( $Bildfaktor , $Show ) ;
	my $QFile = dosh($_[0]) ;
	open TRANSSIZE, "tcprobe -i $QFile 2>&1 |" ;
	while ( my $OutTrans = <TRANSSIZE> ) {
		if ( $OutTrans =~ /import frame size: -g (\d+)x(\d+) / ) { $OBreite=$1 ; $OHoehe=$2 ; } # Breit und Höhe feststellen

		 my @ZeileSplit= split " ", $OutTrans  ;
			if ( $ZeileSplit[0] =~ /aspect/ && $ZeileSplit[2] =~ /4:3/ ) {
			 $Bildfaktor = 4 / 3 ; $Show = "4:3" ;
			}
			elsif ( $ZeileSplit[0] =~ /aspect/ && $ZeileSplit[2] =~ /16:9/ ) {
		 	$Bildfaktor = 16 / 9 ; $Show = "16:9" ;
			}
			elsif ( $ZeileSplit[0] =~ /aspect/ && $ZeileSplit[2] =~ /1:1/ ) {
		 	$Bildfaktor = 16 / 9 ; $Show = "16:9" ;
			}
	}
	close TRANSSIZE ;
	return $OBreite , $OHoehe , $Bildfaktor , $Show ;
}
#------------------------------------------------------------------------------
sub getstretch {
my ( $OBreite , $OHoehe , $Bildfaktor ) = @_ ;
# erklärende Berechnung :
# 704 x 576 4:3 orig Bildformatdaten
#576 * 1.3333333 # = 768
#704 / 768 * 576 # = 528 -> resultierende Höhe 4:3 bei 704 Breite
my $Hoehe4z3= int(( $OBreite / (  $OHoehe * $Bildfaktor ) ) *  $OHoehe )  ;
my $Stretch = int (( $OHoehe - $Hoehe4z3 ) / 16 ) ; # 8er blöcke
return "${Stretch},0,16" ;
}
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
sub deint_crop {
#gibt zurück : $YCrop , $Deint
	my ( $QFile ,  $Stretch ) =@_ ;
	$QFile = dosh($QFile) ;
	my ( $Deint , @VorDaten ) ;

# den File bis Frame 5750 durchlaufen
# ab 0 frames deinterlaced suchen
# ab 3500 frames detectclipping suchen
# alle Antwortzeilen in ein Array schaufeln
# dann Array nach den interessanten Dingen durchsuchen

	my @CropArr ;
	$| = 1 ; ## Schreibpuffer aussschalten
#	open CROP , "nice -n 5 $glob->{TranscodeBin} -i $QFile -M 3 -u 100,2  -y null -B ${Stretch},off,8 -J fieldanalysis=verbose,detectclipping=post:range=3500 -c 5750 --print_status 250 -o /dev/null 2>&1 |" ;
	open CROP , "nice -n 5 transcode -i $QFile -M 3 -u 100,2  -y null -B ${Stretch},off,8 -J fieldanalysis=verbose,detectclipping=post -c 10-3000 --print_status 250 -o /dev/null 2>&1 |" ;
	while ( my $CropZeile = <CROP> ) {
#	print "--> $CropZeile" ;
		chomp $CropZeile ;
		push @CropArr , $CropZeile  ;
	### part prozentanzeige
#			if ( $CropZeile =~ /(\d+\.\d\%).*ETA:\ (\d+:\d+\:\d+)/ ) {
#				print "\r" ;
#				print "INFO : finde schwarze Balken / Halbild - Vollbilder ---> $1\tverbleibend : $2" ;
#			}
	}
	foreach  my $Crop ( @CropArr ) {
#	print "\$Crop : $Crop\n" ;
	### part deinterlace
		if ( $Crop =~ /filter_fieldanalysis.so.*CONCLUSION: (.*) video.*$/ ) {
		my $deinterleace_word = "$1" ;
			if ( $deinterleace_word eq "interlaced" ) { $Deint="-I 5" ;  }
			elsif ( $deinterleace_word eq "progressive" ) { $Deint="-I off" ; }
		}
	### part detectclipping
		# $Crop : [detectclipping#0] valid area: X: 8..473 Y: 0..359  -> -Y 0,8,0,6
		if ( $Crop =~ /\[detectclipping\#0\].*-> -Y (\d+\,\d+\,\d+\,\d+)/ ) {
		my @RohDaten = split "," , $1 ;
			if ( $#VorDaten > 0 ) {
				my $Zaehl ;
				for ( $Zaehl = 0 ; $Zaehl <= 3 ; $Zaehl++ ){
					if ( $RohDaten[$Zaehl] < $VorDaten[$Zaehl] ) { $VorDaten[$Zaehl] = $RohDaten[$Zaehl] ; }
				}
			}
			else { @VorDaten = @RohDaten };
		}
	}
	$Deint = $Deint || "-I 5" ; ### wenn fieldanalysis scheitern sollte , default auf interleace setzten

### Bug workaround für interlaced "CSI"
#	if ( $QFile =~ /CSI.Den\ Tätern\ auf\ der\ Spur/ ) { $Deint="-I 5" ; }
	if ( $QFile =~ /Crossing\ Jordan/ ) { $Deint="-I 5" ; }


#dedectclipping : runden auf Vielfache von 8 --> mpeg4 encoder vorgabe
	my $Zaehl ;
	for ( $Zaehl = 0 ; $Zaehl <= 3 ; $Zaehl++ ) {
		$VorDaten[$Zaehl] = 8 * ( sprintf "%.0f" , ( $VorDaten[$Zaehl] / 8 ) ) ;
	}
# bei 16:9 : wenn logo oben und daher geringer clippingwert, wert von unten nehmen
	if ( $VorDaten[2] > $VorDaten[0] ) { $VorDaten[0] = $VorDaten[2] }

	my $YCrop="$VorDaten[0],$VorDaten[1],$VorDaten[2],$VorDaten[3]" ;
	close CROP ;
	$| = 0 ; ### Schreibpuffer wieder einschalten
	return $YCrop , $Deint ;
}
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
sub Bitrateberech {
	my ( $target_size , $aac_size , $GFrames ) = @_  ;
	$target_size = $target_size - ( $target_size / 25 ) ; ## orig statt 66 --> war 50 , bitte nochmal prüfen 02.09.2006
	my $CdSizeKbyte = sprintf ( "%.8f" , ( $target_size * 1024 ) ) ; # from Mbyte to Kbyte
	my $DivxRate = sprintf ( "%.0f" , ( ( $CdSizeKbyte - $aac_size ) / $GFrames * 25 * 8 ) ) ; # ohne Overhead
	$DivxRate = 20000 if $DivxRate > 20000 ;
	if ( $DivxRate <= 200 ) {
		print "° Beta Code Warning ---> h264Rate lower than 200 Kbit/sec  ( $DivxRate Kbit/sec ) !!!!!\n" ;
	}
	print "° H264 Data Rate set at $DivxRate KBit/sec\n\n" ;
	return $DivxRate ;
}