#!/usr/bin/perl

# (c) G. Finch (salsaman) 2009 - 2015

# released under the GNU GPL 3 or later
# see file COPYING or www.gnu.org for details


#######################################################################
# LiVES ffmpeg plugin v2.10
# v 2.1 - add experimental 3gp support
# v 2.2 - format changed in ffmpeg from image to image2, update
# v 2.3 add youtube and flv formats
# v 2.4 allow encoding of .png images
# v 2.5 add threading support and experimental webm encoding
# v 2.6 made threading support optional because of instability problems; versioning string changed in ffmpeg
# v 2.7 fixed webm encoding support, removed asf format
# v 2.8 always prompt for threads
# v 2.9 check for avconv, change -quality to -qscale
# v 2.10 move -meta options after png/audio
# v 2.11 change fussy order of -pass option
#######################################################################

if (!defined($standalone)) {
    my $smogrify;
    if ($^O eq "MSWin32") {
	$smogrify="smogrify";
    }
    else {
	$smogrify=`which smogrify`;
	chomp($smogrify);
    }
    if ($smogrify ne "") {
	require $smogrify;
    }
}
else {
    $command=$ARGV[0];
}


if ($command eq "version") {
    print "ffmpeg encoder plugin v2.11\n";
    exit 0;
}


if ($command eq "init") {

    # perform any initialisation needed
    # On error, print error message and exit 1
    # otherwise exit 0
    
    if (&location("ffmpeg") eq "" && &location("avconv") eq "") {
        print "The ffmpeg or avconv binary was not found, please install it and try again !";
	exit 1;
    }

    ##############################
    print "initialised\n";
    exit 0;
}
    
    


if ($command eq "get_capabilities") {
    # return capabilities - this is a bitmap field
    # bit 0 - takes extra parameters (using RFX request)
    # bit 1 - unused
    # bit 2 - can encode png
    # bit 3 - not pure perl
    print "5\n";
    exit 0;
}


if ($command eq "get_formats") {
   # for each format: -
   # return format_name|display_name|audio_types|restrictions|default_extension|

   # audio types are: 0 - cannot encode audio, 1 - can encode using
   #  mp3, 2 - can encode using pcm, 3 - can encode using pcm and mp3
   # 4 - mp2, 8 - vorbis, 16 - AC3, 32 - AAC, 64 - AMR-NB

   # restrictions: 'fps=xx.yy' which
   # means "can only encode at xx.yy frames per second", size=x X y, arate=audio rate 
    # - otherwise set it to 'none'


    print "webmh|webm high quality|8|none|webm|\n";
    print "webmm|webm medium quality|8|none|webm|\n";
    print "webml|webm low (stream) quality|8|none|webm|\n";
    print "flv|flv|9|arate=44100;22050;11025|flv|\n";
    print "divx|divx (25 fps)|5|fps=25.00|avi|\n";
    print "3gp_h263|3gp (h263)|96|size=176x144,arate=8000|3gp|\n";
    print "3gp_mp4|3gp (mp4)|96|size=176x144,arate=8000|3gp|\n";
    print "flv-youtube|flv (425x318, optimised for youtube)|33|arate=44100;22050;11025,size=425x318|flv|\n";
    print "flv-youtubex|flv (other sizes, optimised for youtube)|33|arate=44100;22050;11025,aspect=1.3333:1|flv|\n";
    exit 0;
}




if ($command eq "get_rfx") {
    # nice example of how to give the user a choice of options

    if ($otype eq "flv"||$otype eq "flv-youtube"||$otype eq "flv-youtubex"||$otype eq "divx") {
	$aq=&rc_get("encoder_acodec");
	if ($aq==0) {
	    # mandatory section
	    print "<define>\n";
	    print "|1.7\n";
	    print "</define>\n";
	    
	    # mandatory section
	    print "<language_code>\n";
	    print "0xF0\n";
	    print "</language_code>\n";
	    
	    # optional section
	    print "<params>\n";
	    print "threads|Number of threads|num0|1|1|16|\n";
	    print "mp3|Use _mp3 audio codec|bool|1|1\n";
	    print "mp3lame|Use mp3_lame audio codec|bool|0|1\n";
	    print "</params>\n";

	    print "<param_window>\n";
	    print "layout|\$p0|\n";
	    print "</param_window>\n";
	}
	else {
	    # mandatory section
	    print "<define>\n";
	    print "|1.7\n";
	    print "</define>\n";
	    
	    # mandatory section
	    print "<language_code>\n";
	    print "0xF0\n";
	    print "</language_code>\n";
	    
	    # optional section
	    print "<params>\n";
	    print "threads|Number of threads|num0|1|1|16|\n";
	    print "</params>\n";

	    print "<param_window>\n";
	    print "layout|\$p0|\n";
	    print "</param_window>\n";
	}
    }
    else {
	# mandatory section
	print "<define>\n";
	print "|1.7\n";
	print "</define>\n";
	
	# mandatory section
	print "<language_code>\n";
	print "0xF0\n";
	print "</language_code>\n";
	
	# optional section
	print "<params>\n";
	print "threads|Number of threads|num0|1|1|16|\n";
	print "</params>\n";

	print "<param_window>\n";
	print "layout|\$p0|\n";
	print "</param_window>\n";
	
    }
}







if ($command eq "encode") {
    # encode

    if ($^O eq "MSWin32") {
	$nulfile="NUL";
	$exe=".exe";
    } else {
	$nulfile="/dev/null";
	$exe="";
    }

    # test first for avconv; otherwise Ubuntu complains
    $encoder_command="avconv$exe";
    system("$encoder_command -version >testfile 2>$nulfile");

    if (-z "testfile") {
	$encoder_command="ffmpeg$exe";
	system("$encoder_command -version >testfile 2>$nulfile");
    }

    $ffver=`grep libavformat testfile | grep -v configuration`;

    $ffver=(split(/\./,$ffver))[0];

    $ffver=(split(" ",$ffver))[-1];

    unlink "testfile";

    $nthreads=$ARGV[13];

    $usemp3=$ARGV[14];
    $usemp3lame=$ARGV[15];

    $vid_length=($end-$start+1)/$fps;

    $err=">/dev/null 2>&1";
    if (defined($DEBUG_ENCODERS)) {
	$err="1>&2";
    }

    if ($otype eq "") {
	$otype="divx";
	&rc_set("output_type",$otype);
    }

    $ncpus=&get_num_procs;


    # default seems to be divx
    $vcodec="";
    if ($otype eq "asf") {
	$vcodec="-f asf";
    }
    elsif ($otype eq "3gp_h263") {
	$vcodec="-f h263";
    }
    elsif ($otype eq "3gp_mp4") {
	$vcodec="-f mp4";
    }
    elsif ($otype eq "flv"||$otype eq "flv-youtube"||$otype eq "flv-youtubex") {
	$vcodec="-f flv";
    }
    elsif ($otype eq "webmh") {
	$vcodec="-f webm -vcodec libvpx -g 60 -qscale 1 -b 1G";
    }
    elsif ($otype eq "webmm") {
	$vcodec="-f webm -vcodec libvpx -g 60 -qscale 1 -b 500M";
    }
    elsif ($otype eq "webml") {
	$vcodec="-f webm -vcodec libvpx -g 60 -qscale 31";
    }

    if ($otype eq "flv-youtube"||$otype eq "flv-youtubex") {
	$vcodec.=" -b 1024k -bt 256k -maxrate 1024k -minrate 1024k -bufsize 8192k";
    }

    # video stream
    $audio_com="";
    unless ($audiofile eq "") {
	$aq=&rc_get("encoder_acodec");
	$audio_com="-i $audiofile";
	if ($aq==1) {
	    #pcm
	    $audio_com.=" -acodec copy";
	}
	elsif ($aq==5) {
	    $audio_com.=" -acodec aac -strict experimental";
	}
	elsif ($aq==6) {
	    $audio_com.=" -acodec amr_nb";
	}
	elsif ($aq==0) {
	    if ($usemp3lame) {
		$audio_com.=" -acodec libmp3lame";
	    }
	    else {
		$audio_com.=" -acodec mp3";
	    }
	}
	elsif ($aq==3) {
	    $audio_com.=" -acodec libvorbis";
	    if ($otype eq "webmh") {
		$audio_com.=" -aq 10";
	    }
	    elsif ($otype eq "webmm") {
		$audio_com.=" -aq 5";
	    }
	    elsif ($otype eq "webml") {
		$audio_com.=" -aq 1";
	    }
	}
	else {
	    $audio_com.=" -acodec mp2";
	}

	if ($otype eq "flv-youtube"||$otype eq "flv-youtubex") {
	    $audio_com.=" -ab 128k -ar $arate";
	}
	elsif ($otype eq "flv") {
	    $audio_com.=" -ab 192k -ar $arate";
	}
	elsif ($otype eq "3gp_h263" || $otype eq "3gp_mp4") {
	    $audio_com.=" -ac 1 -ab 12 -ar 8000";
	}

    }
    
    # unfortunately this does not work yet...
    if ($otype eq "theora") {
	$syscom="ffmpeg2theora -o \"$nfile\" -f image2 -i %8d$img_ext $audio_com -d off $err";
    }
    elsif ($otype eq "3gp") {
	$passfile="passfile";
	for $pass (1,2) {
	    $syscom="$encoder_command -strict 1 -y -r $fps -f image2 -i %8d$img_ext $audio_com -t $vid_length $vcodec -pass $pass \"$nfile\" $err";
		if (defined($DEBUG_ENCODERS)) {
		    print STDERR "ffmpeg_encoder command is: $syscom\n";
		}
	    
	    system ($syscom);
	}
    }
    else {
	if ($nthreads>1) {
	    $threads=" -threads $nthreads";
	}
	else {
	    $threads="";
	}

	if ($otype eq "webmh") {
	    for $pass (1,2) {
		if ($ffver>=52) {
		    $syscom="$encoder_command $threads -y -r $fps -f image2 -i %8d$img_ext $audio_com -t $vid_length $vcodec -metadata comment=\"$comment\" -metadata author=\"$author\" -metadata title=\"$title\" -pass $pass \"$nfile\" $err";
		}
		else {
		    $syscom="$encoder_command -pass $pass $threads -comment \"$comment\" -author \"$author\" -title \"$title\" -y -r $fps -f image2 -i %8d$img_ext $audio_com -t $vid_length $vcodec \"$nfile\" $err";
		}

		if (defined($DEBUG_ENCODERS)) {
		    print STDERR "ffmpeg_encoder command is: $syscom\n";
		}

		system ($syscom);
	    }
	}
	else {
	    if ($ffver>=52) {
		$syscom="$encoder_command $threads -y -r $fps -f image2 -i %8d$img_ext $audio_com -t $vid_length $vcodec -metadata comment=\"$comment\" -metadata author=\"$author\" -metadata title=\"$title\" \"$nfile\" $err";
	    }
	    else {
		$syscom="$encoder_command $threads -comment \"$comment\" -author \"$author\" -title \"$title\" -y -r $fps -f image2 -i %8d$img_ext $audio_com -t $vid_length $vcodec \"$nfile\" $err";
	    }

	    if (defined($DEBUG_ENCODERS)) {
		print STDERR "ffmpeg_encoder command is: $syscom\n";
	    }
	    
	    system ($syscom);

	}

    }


    &sig_complete;
    exit 0;
}



if ($command eq "clear") {
    # this is called after "encode"
    # note that encoding could have been stopped at any time

    unlink "temp.vid";

    unlink "passfile";
    &sig_complete;
    exit 0;
}



if ($command eq "finalise") {
    # do any finalising code
    
    # end finalising code
    print "finalised\n";
    exit 0;
}

exit 0;



###### subroutines #######

sub get_format_request {
    # return the code for how we would like audio and video delivered
    # this is a bitmap field composed of:
    # bit 0 - unset=raw pcm audio; set=pcm audio with wav header
    # bit 1 - unset=all audio; set=clipped audio
    # bit 2 - unset=all frames; set=frames for selection only

    return 7; # clipped pcm wav, frames start at selection
}


sub get_num_procs {
    my $result=`cat /proc/cpuinfo|grep processor`;

    $result=(split (/\n/,$result))[-1];
    $result=(split (/: /,$result))[1];
    return ++$result;
}


sub location {
    # return the location of an executable
    my ($command)=shift;

    if ($^O eq "MSWin32") {
	return "$command.exe";
    }

    my ($location)=smog_system_direct("which \"$command\" 2>$nulfile");
    chomp($location);

    $location;
}
