#!/usr/bin/perl

use Shell;
use strict;
use POSIX qw(floor);

# Written by Bob Igo from the MythTV Store at http://MythiC.TV
# including some original code and contributions from Nick C.
# and graysky.
# Email: bob@stormlogic.com
#
# If you run into problems with this script, please send me email

# PURPOSE:
# --------------------------
# This is a wrapper script that tries to find the best parameters
# for calling an underlying video player.  The outer layer determines
# the best playback parameters, while the inner layer picks the best
# player to call.

# RATIONALE:
# --------------------------
# Default video playback options are not optimal on all hardware or
# for all video types.  In addition, common video players do not
# offer to bookmark video so that you can resume where you left off.
# Both of these problems can be addressed by this wrapper.

# PARAMETERS:
# --------------------------
# The same parameters you'd use for mplayer, some of which may be
# translated automatically for use with smplayer.

# FILES:
# --------------------------
# $mediafile, the file to play

sub run () {
    my $mediafile = @ARGV[$#ARGV];
    my $player = &pick_player();

    # mplayer evaluates configuration options in the following order, with
    # later-evaluated options overriding earlier-evaluated ones, both within
    # a given configuration location and between them:
    # 1) system-wide configuration/profiles, /etc/mplayer/mplayer.conf
    # 2) user-specific configuration/profiles, ~/.mplayer/config
    # 3) commandline configuration parameters
    # 4) file-specific configuration, ~/.mplayer/[filename].conf
    # 5) any nonstandard configuration file, included via "-include" parameter
    #
    # This script's dynamic configuration options fall in at 2.5 above,
    # so commandline options, file-specific configuration options,
    # or a nonstandard configuration file will override the options that
    # the script creates, but system-wide and user-specific configuration
    # will be overridden by the dynamic configuration options.
    #
    # This is sub-optimal, as the easiest way for a user to compensate for
    # a misfiring configuration rule would be to override it in a configuration
    # file.  Instead, they will have to change the way they run this script.

    my $player_parameters = join(' ',
				 &dynamic_parameters($mediafile),
				 &translate_parameters($player,@ARGV[0..$#ARGV-1]));
    &player($player,$player_parameters,$mediafile);
}

&run();

# Translates any parameters into ones that will be compatible with the given player.
sub translate_parameters() {
    my($player,@parameters)=@_;

    if ($player eq "smplayer") {
	# Stupidly, smplayer uses a different set of command-line parameters than generic
	# mplayer, so we need to translate mplayer-centric ones into the few that are
	# available in smplayer-ese.
	my %smplayer_parameter_translation_array = (
	    "-fs" => "-fullscreen",
	    "-zoom" => " "
	    );
	
	sub translate() {
	    my($flag)=@_;
	    return $smplayer_parameter_translation_array{$flag};
	}
	
	return map(&translate($_), @parameters);
    } else {
	# currently, all other players used by this wrapper work with mplayer parameters
	return @parameters;
    }
}

# Returns an array of dynamic parameters based on the media type,
# the presence of special playback decoding hardware, and the
# general capability of the CPU.
sub dynamic_parameters () {
    my($mediafile)=@_;
    my @parameters = ();
    my $codec="";
    my %vdpau_supported_modes=();

    # See if the GPU and driver support vdpau for GPU-based accelerated decoding
    my $command="vdpinfo |";
    # On supported hardware, vdpinfo produces relevant results that look something like this (see
    # http://www.phoronix.com/forums/showthread.php?t=14493 for additional details, or run
    # vdpinfo on vdpau-capable hardware yourself):
    #
    #MPEG1             0  2  4096  4096
    #MPEG2_SIMPLE      3  2  4096  4096
    #MPEG2_MAIN        3  2  4096  4096
    #H264_MAIN        41  4  4096  4096
    #H264_HIGH        41  4  4096  4096
    
    my $grabbing_modes=0;
    open(SHELL, $command);
    while (<SHELL>) {
	chop;
	if (m/Decoder Capabilities/gi) {
	    $grabbing_modes=1;
	    #print "*** MODES START NOW"
	} elsif (m/Output Surface/gi) {
	    $grabbing_modes=0;
	} elsif ($grabbing_modes) {
	    if (m/[A-Z]+[0-9]+/g) {
		s/(_.*)//g;
		#print "*** GRABBED MODE $_\n";
		$vdpau_supported_modes{$_} = 1;
	    }
	}
    }
    close(SHELL);
    
    # figure out what codec the video uses
    my $command="mplayer -identify -frames 0 \"$mediafile\" | grep ID_VIDEO_CODEC | cut -c 16- |";
    open(SHELL, $command);
    while (<SHELL>) {
	chop;
	$codec = $_;
	#print "DEBUG: codec is $codec\n";
    }
    close(SHELL);

    # We should use vdpau if it's available and helps with the codec we need to decode.
    if ($codec eq "ffh264") { # h.264
	if ($vdpau_supported_modes{"H264"}) {
	    push(@parameters, "-vo vdpau");
	    push(@parameters, "-vc ffh264vdpau");
	}
    } elsif ($codec eq "ffmpeg2") { # MPEG2
	if ($vdpau_supported_modes{"MPEG2"}) {
	    push(@parameters, "-vo vdpau");
	    push(@parameters, "-vc ffmpeg12vdpau");
	}

	# ??? although MPEG1 is rare, it seems as if it should work with -vc ffmpeg12vdpau as well
	
	# problems have been reported with WMV3 support
	
#    } elsif ($codec eq "ffwmv3") { # WMV3
#	if ($vdpau_supported) {
#	push(@parameters, "-vo vdpau");
#	push(@parameters, "-vc ffwmv3vdpau");
#    }
	# problems have been reported with WVC1 support
	
#    } elsif ($codec eq "ffvc1") { # WVC1
#	if ($vdpau_supported) {
#	push(@parameters, "-vo vdpau");
#	push(@parameters, "-vc ffvc1vdpau");
#    }

    } else {
	push(@parameters, "-vo xv,x11,");
	push(@parameters, "-vc ,");
	push(@parameters, "-vf pp=lb,"); # doesn't actually work with vdpau, but doesn't break anything
    }
    return(@parameters);
}

# Find the best player for use on this system.  The script prefers smplayer,
# which has built-in bookmarking, falling back to mplayer-resumer.pl, which
# implements bookmarking as an mplayer wrapper, if smplayer cannot be found.
# Finally, if no bookmarking players can be found, a barebones mplayer is used.
sub pick_player () {
    #my @possible_players = ("smplayer", "mplayer-resumer.pl", "mplayer");
    my @possible_players = ("mplayer-resumer.pl", "mplayer");
    my $command;
    my $candidate_player;
    foreach (@possible_players) {
	$candidate_player = $_;
	$command = "which $candidate_player |";
	open(SHELL, $command);
	if (<SHELL>) {
	    #print "player $candidate_player : $_\n";
	    return $candidate_player;
	}
	close(SHELL);
    }
}

# Calls player
sub player () {
    my($player,$parameters,$mediafile)=@_;
    my $command = "$player $parameters \"$mediafile\" 2>&1 |";

    print "DEBUG: $0's player command is: *** $command ***\n";
    open(SHELL, $command);
    while (<SHELL>) {
	print $_;
    }
    close(SHELL);
}