#!/usr/bin/perl
# $Id: mplayer.pl,v 1.18 2006/03/15 19:23:04 khlut Exp $
# Bind mplayer and mencoder with HTML object
# ElphelOgm is used to receive and record rtp mjpeg video
#
# Copyright 2005 Elphel, Inc.
#
# This file is part of GenReS.
#
#    GenReS is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    GenReS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with GenReS; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

use IPC::Open2;
use threads;
use threads::shared;
use POSIX ":sys_wait_h";

## Parameters from the config file ##
$conffile="$ENV{HOME}/.mozilla/genres/mplayer.conf";
%cfg=(
  vo=>[x11, 'text', "Video output driver (x11,vx, see man mplayer)"],
  not_ask=> [0, 'checkbox', "Never show file save dialog again."],
  out_file=>['%@-%Y-%m-%d_%H-%M-%S.avi', 'file', "Write video to file(s)",
     "%@  in the filename will be replaced to source URL\n"
    ."%Y - year \t%m - month number\t%d - day of month\n"
    ."%H - hour \t%M - minute  \t%S - second"
  ],
  out_file_once=>['%@-%Y-%m-%d_%H-%M-%S.avi', 'file', "Save video to file",
     "%@  in the filename will be replaced to source URL\n"
    ."%Y - year \t%m - month number\t%d - day of month\n"
    ."%H - hour \t%M - minute  \t%S - second"
  ],
  frames=>  [10000, 'text', "Frames per file limit"],
);
$_=$0;  s|[^/]*$||;  @INC=( "$_../..",@INC);
require "config.pl";
load_config();
## *** ##

$v{timeout}=10;    #shut down mplayer after 10 seconds of silent
$v{quittimeout}=2; #wait 2 seconds after send quit command or interruptable signal
$v{play}=1;

$|=1; #forces a flush right away and after every write or print on the currently selected output channel
my $shutstate:shared=0;
my $pausestate:shared=0;
my $queue:shared="";
my $thrid:shared;

$^F=100; #to pass pipe to new processes
pipe PIPE_OUT, PIPE_IN;

sub shut{
    lock $queue,$shutstate;
    !$shutstate++ && $v{save} && $shutstate++;
    alarm $v{quittimeout};
    if($shutstate==1){		#interrupt read
	print SLVW "\nquit\n";
	close(PIPE_IN);
    }elsif($shutstate==2){	#tell mplayer to shut
        kill 2 => $slvp;
	kill 2 => $sl2p if $sl2p;
    }else{			#if it again not respond kill it
        kill -9 => $slvp;
	kill -9 => $sl2p if $sl2p;
	print "\nrun=0\n";    
	exit 0;	
    }
    return;
}

sub normal_exit{
    $SIG{PIPE}='IGNORE';
    lock $queue,$shutstate;
    alarm $v{$shutstate?'quittimeout':'timeout'};
    close(PIPE_IN);
    close(PIPE_OUT);
    waitpid $slvp, 0;
    waitpid $slvp, 0;
    waitpid $slvp, 0;
    if(waitpid($slvp, 0)<0 && kill(0,$slvp)>0 && threads->self->tid == $thrid) {
	alarm 0;
	return;
    }
    if($sl2p) {
	kill 9 => $sl2p;
	waitpid $sl2p, 0;
    }
    print "\nrun=0\n";
    exit 0;
}

$SIG{PIPE}=\&normal_exit;
$SIG{ALRM}=\&shut;


sub ifilt{
    if($v{pause} || !$v{play}) {
	$pausestate=1;
	print SLVW "pause\n";
	lock $queue;
	$queue.="pause=1\nplay=0\n";
    }
    while(<STDIN>){
	($var,$val)= split '=', $_, 2;
	$val=~s/\n//;
	if( $_ eq "run=0\n" ) {
	    shut();
	    last;
	}elsif( $var eq "pause" ) {
	    $val=0+$val;
	    $val=1 if $val!=0 && $val!=1 ;
	    if($val != $pausestate) {
		$pausestate=$val;
		print SLVW "pause\n";
		lock $queue;
		$queue.="pause=$val\nplay=".(1-$val)."\n";
	    }	    
	}elsif( $var eq "filepos" ) {
	    if($val =~ s/%//) {
		print SLVW "seek $val 1\n";
	    }else{
		print SLVW "seek $val 2\n";
	    }
	}elsif( $var eq "framestep" ) {
	    print SLVW "frame_step\n";	    
	    $pausestate=1;
	    lock $queue;
	    $queue.="pause=1\nplay=0\n";
	}elsif( $var eq "src" or $var eq "href") {
	    $v{href}=$val;
	    print SLVW "loadfile $val\n";
	    lock $queue;
	    $queue.="$_";
	}else{
	    $v{$var}=$val;
	    lock $queue;
	    $queue.="$_";
	}
    }
}

sub ofilt_quit{ #child hangs
    $SIG{ALRM}=\&shut;
    kill ALRM, $ifilt;
}

sub ofilt{
    local $s;
    local $SIG{ALRM}=\&ofilt_quit;
    alarm $v{timeout};
    $s="";
    print "run=1\n";
    do {
      while($_='', sysread( SLVR, $_, 1 ) || $!{EINTR}){
	if( tr/\r/\n/ ){
	    alarm($pausestate? 0 : $v{timeout});
	}
	$s.=$_;
	{lock $queue;
	  if($queue ne "") {
	    print $queue;
	    $queue="";
	  }
	}
	if($_ eq "\n") {
	    if( $s =~ 'Failed' ) {
	        print "error=$s";
	    }elsif($s =~ m/^Playing \(.*\)\.$/){
		$s=$1;
		if($s !~ m!-|/dev!) {
		    $s="file://".$s if $s !~ m|^[a-z0-9]*://|;
		    print "url=$s";
		}
	    }elsif($s =~ m/^[AV]:/ || $s =~ m/^Pos:/){
		print "frame=$s";
	    }else{
		print $s;
	    }
	    $s="";
	}
      }
    }while($restart && !$shutstate && $restart && &$restart);
    ofilt_quit() unless $SIG{ALRM} eq \&shut;;
    normal_exit();
}
sub runslv{
    $slvp = open2(*SLVR, *SLVW, "exec @_ 2>&1");
}
sub suburl{
    local $_=$v{href};
    s|^[a-z0-9]*://||i;
    s|[?].*||;
    s|/|-|g;
    s/^-+//;
    s/-+$//;
    my $x=$_[0];
    $x =~ s/%@/$_/g;
    return $x;
}
sub run_mencoder{
    use POSIX qw(strftime);
    if(!$v{fps}) { $v{fps}=25;  print "fps=25\n"; }
    my $fname= strftime $out_file, localtime;
    $fname = suburl($fname);
    $cmd="mencoder -frames '$cfg{frames}[0]' -fps '$v{fps}' -ovc copy '$v{href}' -o '$fname'";
    print "cmd=$cmd\n";
    runslv $cmd;
    return $slvp>0;
}
sub rtpcmd{
    return $v{type} =~ m!^video/m|^application/.*elphel-ogm! && $v{href} =~ m|^rtp://([0-9.]+):([0-9]+)/?$|
	&& "ElphelOgm -a$1 -p$2 -s2000000";
}

print "debug=1\n", # list of control variables
      "pause=0\n",
      "framestep=\n",
      "filepos=\n",
      "play=1\n",
      "save=\n",
      "fps=\n",
      "lowres=\n",
      "src=\n",
      "href=\n" unless($ARGV[0] eq '--no-vars');
      
while (<STDIN>){
    s/\n//;
    if($_ !~ /.*=/){
	print "error=1\n";
	next;
    }
    s/'/'\\''/g; #'
    ($x,$y) = split '=', $_, 2;
    $x='href' if($x eq 'src' || $x eq 'SRC');
    $v{$x}=$y;
    if("$_" eq "run=1"){
	$OPTS="";
	$v{baseURI}=~s|/[^/]*$||;
	$v{href}=$v{baseURI}.'/'.$v{href}	if( $v{href}!~m|[a-zA-Z0-9]+:| );
	$v{href}=~s|^file://||;
	if($v{save}) { #Recording TODO: ask about overwriting file
	    $cmd=rtpcmd();
	    if($cmd) {
		$cfg{out_file_once}[0]=~s/\.avi/\.ogm/;
		$cfg{out_file}[0]=~s/\.avi/\.ogm/;
	    }else{
		$cfg{out_file_once}[0]=~s/\.ogm/\.avi/;
		$cfg{out_file}[0]=~s/\.ogm/\.avi/;
	    }
	    if(uc $v{save} ne 'AUTO'){
		if(!file_dialog(out_file_once, frames)) { print "run=0"; last};
		$out_file=$cfg{out_file_once}[0];
	    }elsif( $cfg{not_ask}[0] ){
		$out_file=$cfg{out_file}[0];
	    }else{
		if(!file_dialog(out_file, not_ask, frames)) { print "run=0"; last};
		$out_file=$cfg{out_file}[0];
	    }
	    if($cmd) {
		$cmd="$cmd -F '$cfg{frames}[0]' -o".suburl($out_file);
		print "cmd=$cmd\n";
		runslv $cmd;
	    } else {
		$restart=\&run_mencoder;
		run_mencoder();
	    }
	}else{
	    if( $v{xid} ) {
		$OPTS.=" -wid 0x$v{xid}";
		if( !$v{width} || $v{width}=~"%" ) {
		    use Gtk2 ; #-init;
		    (undef,undef,$v{width},$v{height}) = Gtk2::Gdk::Window->foreign_new(hex $v{xid})->get_geometry;
		}
	    }
	    $OPTS.=" -xy '$v{width}'"	if( $v{width} && $v{width}!~"%" );
	    $OPTS.=" -lavdopts lowres='$v{lowres}'" if( $v{lowres} );
	    $OPTS.=" -fps '$v{fps}'"		if( $v{fps} );
	    $OPTS.=" -fixed-vo -loop ".(0+$v{loop}) if( exists $v{loop} );
	    if(( $cmd2=rtpcmd() )) {
		$cmd2.=" -k $v{frameskip}" if $v{frameskip};
	        $cmd2="|exec $cmd2 2>/dev/null ".fileno(PIPE_OUT)."<&- >/dev/fd/".fileno(PIPE_IN);
		$cmd="mplayer $OPTS -vo $cfg{vo}[0] -zoom -nojoystick -slave -nomouseinput -cookies -nocache /dev/fd/".fileno(PIPE_OUT);
	        print "cmd=$cmd\n";
	        print "cmd2=$cmd2\n";
		runslv $cmd;		
		$sl2p=open(SL2W,$cmd2);
	    } else {
		$cmd="mplayer $OPTS -vo $cfg{vo}[0] -zoom -nojoystick -slave -nomouseinput -cookies '$v{href}'";
	        print "cmd=$cmd\n";
	        runslv $cmd;
	    }
	}
	$ifilt=$$;
	$thrid = threads->new(\&ofilt)->tid;
	ifilt();
	normal_exit();
    }elsif("$_" eq "run=0"){
	print "$_\n";
	last;
    }else{
	print "$x=$y\n";
	if($x eq "href") { print "src=$y\n"; }
    }
}
