/usr/local/CPAN/Video-PlaybackMachine/Video/PlaybackMachine/PlayerBackEnd/XineBackEnd.pm
package Video::PlaybackMachine::PlayerBackEnd::XineBackEnd;
use strict;
use warnings;
use X11::FullScreen;
use Video::PlaybackMachine::EventWheel::FullScreen;
use Video::Xine;
use POSIX 'ceil';
use Carp;
use constant X_DISPLAY => ':0.0';
## Status codes backend will report
use constant PLAYER_STATUS_STOP => 0;
use constant PLAYER_STATUS_PLAY => 1;
use base 'Video::PlaybackMachine::PlayerBackEnd';
sub initialize {
my $self = shift;
my $display = X11::FullScreen::Display->new(X_DISPLAY);
$self->{'display'} = $display;
$self->{'window'} = $display->createWindow();
$display->sync();
my $xine = Video::Xine->new();
$self->{'xine'} = $xine;
my $x11_visual = Video::Xine::Util::make_x11_visual($display,
$display->getDefaultScreen(),
$self->{'window'},
$display->getWidth(),
$display->getHeight(),
$display->getPixelAspect()
);
my $driver = Video::Xine::Driver::Video->new($xine,"auto",1,$x11_visual);
my $s = $xine->stream_new(undef, $driver)
or croak "Unable to open video stream";
$self->{'stream'} = $s;
$self->{'stream_queue'} =
Video::PlaybackMachine::Player::EventWheel->new($s);
my $fq =
Video::PlaybackMachine::EventWheel::FullScreen->new($display, $self->{'window'});
$fq->set_expose_handler(
sub { $s->get_video_port()->send_gui_data(XINE_GUI_SEND_EXPOSE_EVENT, $_[1]); } );
$fq->spawn();
$self->{'fullscreen_queue'} = $fq;
}
sub get_stream_queue { return $_[0]->{'stream_queue'}; }
sub get_status {
my $self = shift;
$self->{'stream'}->get_status() == XINE_STATUS_PLAY
and return PLAYER_STATUS_PLAY;
return PLAYER_STATUS_STOP;
}
sub stop {
my $self = shift;
if ( $self->{stream}->get_status() == XINE_STATUS_PLAY ) {
$self->{'stream'}->stop();
$self->{'stream'}->close();
}
}
sub play_movie {
my $self = shift;
my ($file, $offset) = @_;
my $s = $self->{'stream'};
$s->open($file)
or return;
$s->play(0,$offset * 1000)
or return;
# Tell the system to refresh the window
# Drawable changed
$s->get_video_port()->send_gui_data(XINE_GUI_SEND_DRAWABLE_CHANGED, $self->{'window'});
$s->get_video_port()->send_gui_data(XINE_GUI_SEND_VIDEOWIN_VISIBLE, 1);
return 1;
}
sub play_still {
my $self = shift;
my ($still) = @_;
eval {
$self->{'display'}->displayStill($self->{'window'}, $still);
};
if ($@) {
return;
}
return 1;
}
sub play_music {
my $self = shift;
my ($song_file) = @_;
$self->{'stream'}->open($song_file) or return;
$self->{'stream'}->play(0,0) or return;
$self->{'stream_queue'}->spawn();
return 1;
}
sub get_error {
return $_[0]->{'stream'}->get_error();
}
sub movie_length {
my $self = shift;
my ($filename) = @_;
my $xine = Video::Xine->new(config_file => '/dev/null');
my $null_ao_driver = Video::Xine::Driver::Audio->new($xine, 'none')
or die "Couldn't open audio driver\n";
my $stream = $xine->stream_new($null_ao_driver);
$stream->open($filename)
or croak "Couldn't open '$filename'";
my (undef, undef, $length_millis) = $stream->get_pos_length();
return ceil($length_millis / 1000);
}
package Video::PlaybackMachine::Player::EventWheel;
# TODO: Make a subclass of EventWheel
###
### When spawned, these will pass along events from the given
### streams to the appropriate callbacks.
###
use strict;
use POE;
use Video::Xine;
## How-the-movie-played status codes
# OK == played through and stopped at the end
use constant PLAYBACK_OK => 1;
# ERROR == problem in trying to play
use constant PLAYBACK_ERROR => 2;
## How often to check to see if Xine has stopped, in seconds
use constant XINE_CHECK_INTERVAL_SECS => 2;
sub new {
my $type = shift;
my ($stream, %handlers) = @_;
my $self = {
type => $type,
stream => $stream,
handlers => { %handlers },
logger => Log::Log4perl->get_logger('Video.PlaybackMachine.Player.EventWheel'),
};
bless $self, $type;
}
sub spawn {
my $self = shift;
my ($callback) = @_;
POE::Session->create(
object_states => [$self=>[qw(_start get_events)]]
);
}
sub _start {
my ($self, $heap, $kernel) = @_[OBJECT, HEAP, KERNEL];
$heap->{queue} = Video::Xine::Event::Queue->new($self->{'stream'})
or die "Couldn't create Xine::Event::Queue";
$kernel->yield('get_events');
}
sub get_events {
my ($self, $heap, $kernel) = @_[OBJECT, HEAP, KERNEL];
# Translate all events into callbacks
while ( my $event = $heap->{queue}->get_event() ) {
$self->{'logger'}->debug("Received event: ", $event->get_type(), "\n");
if ( $event->get_type() == XINE_EVENT_UI_PLAYBACK_FINISHED ) {
$self->{'stream'}->close();
}
if ( exists $self->{'handlers'}{$event->get_type()} ) {
$self->{'logger'}->debug("Invoking handler for ", $event->get_type(), "\n");
$self->{'handlers'}{$event->get_type()}->($self->{'stream'}, $event);
}
}
# Keep checking so long as we're playing
if ( $self->{'stream'}->get_status() == XINE_STATUS_PLAY ) {
$kernel->delay('get_events', XINE_CHECK_INTERVAL_SECS);
}
else {
delete $heap->{queue};
}
}
sub set_handler {
my $self = shift;
my ($event, $callback) = @_;
$self->{'handlers'}{$event} = sub { $callback->($_[0], PLAYBACK_OK) };
}
# Convenience method
sub set_stop_handler {
$_[0]->set_handler(XINE_EVENT_UI_PLAYBACK_FINISHED, $_[1]);
}
1;