summaryrefslogtreecommitdiffstats
path: root/xine_artsplugin/xinePlayObject_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xine_artsplugin/xinePlayObject_impl.cpp')
-rw-r--r--xine_artsplugin/xinePlayObject_impl.cpp784
1 files changed, 784 insertions, 0 deletions
diff --git a/xine_artsplugin/xinePlayObject_impl.cpp b/xine_artsplugin/xinePlayObject_impl.cpp
new file mode 100644
index 00000000..21e3dff5
--- /dev/null
+++ b/xine_artsplugin/xinePlayObject_impl.cpp
@@ -0,0 +1,784 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002-2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <math.h>
+#include <sys/time.h>
+#include <audiosubsys.h>
+#include <convert.h>
+#include <debug.h>
+
+#include "xinePlayObject_impl.h"
+
+#ifndef HAVE_XSHMGETEVENTBASE
+extern "C" {
+extern int XShmGetEventBase( Display* );
+};
+#endif
+
+#define TIMEOUT 15 // 15 seconds
+
+using namespace Arts;
+
+
+// Global xine pointer
+static xine_t *xine_shared = NULL;
+static pthread_mutex_t xine_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t xine_cond = PTHREAD_COND_INITIALIZER;
+static int xineRefCount = 0;
+static bool xineForceXShm = false;
+
+static void xine_init_routine()
+{
+ const char *id;
+ char cfgFileName[272];
+
+ xine_shared = (xine_t *)xine_new();
+
+ snprintf( cfgFileName, 272, "%s/.xine/config", getenv( "HOME" ) );
+
+ xine_config_load( xine_shared, (const char *)cfgFileName );
+
+ // Check default video output driver
+ id = xine_config_register_string (xine_shared, "video.driver",
+ "auto", "video driver to use",
+ NULL, 10, NULL, NULL);
+
+ xineForceXShm = (id && !strcasecmp( id, "XShm" ));
+
+ xine_init( xine_shared );
+}
+
+static void *xine_timeout_routine( void * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ while (xine_shared != 0)
+ {
+ if (xineRefCount == 0)
+ {
+ struct timespec ts;
+ struct timeval tv;
+
+ gettimeofday( &tv, 0 );
+
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ ts.tv_sec += TIMEOUT;
+
+ if (pthread_cond_timedwait( &xine_cond, &xine_mutex, &ts ) != 0 &&
+ xineRefCount == 0)
+ {
+ xine_exit( xine_shared );
+ xine_shared = NULL;
+ break;
+ }
+ }
+ else
+ {
+ pthread_cond_wait( &xine_cond, &xine_mutex );
+ }
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return NULL;
+}
+
+static xine_t *xine_shared_init()
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ ++xineRefCount;
+
+ if (xine_shared == 0)
+ {
+ pthread_t thread;
+
+ xine_init_routine();
+
+ if (pthread_create( &thread, NULL, xine_timeout_routine, NULL ) == 0)
+ {
+ pthread_detach( thread );
+ }
+ }
+ else
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return xine_shared;
+}
+
+static void xine_shared_exit( xine_t * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ if (--xineRefCount == 0)
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+}
+
+int ao_fifo_arts_delay()
+{
+ return (int)(1000 * Arts::AudioSubSystem::the()->outputDelay());
+}
+
+xinePlayObject_impl::xinePlayObject_impl(bool audioOnly)
+ : mrl( "" ), xine( 0 ), stream( 0 ), queue( 0 ), ao_port( 0 ), vo_port( 0 ), audioOnly(audioOnly)
+{
+
+ if (!audioOnly)
+ {
+ XInitThreads();
+
+ if (!(display = XOpenDisplay( NULL )))
+ {
+ arts_fatal( "could not open X11 display" );
+ }
+
+ XFlush( display );
+
+ // Create a special window for uninterrupted X11 communication
+ xcomWindow = XCreateSimpleWindow( display, DefaultRootWindow( display ),
+ 0, 0, 1, 1, 0, 0, 0 );
+
+ XSelectInput( display, xcomWindow, ExposureMask );
+ }
+ pthread_mutex_init( &mutex, 0 );
+
+ if (!audioOnly)
+ {
+ // Initialize X11 properties
+ xcomAtomQuit = XInternAtom( display, "VPO_INTERNAL_EVENT", False );
+ xcomAtomResize = XInternAtom( display, "VPO_RESIZE_NOTIFY", False );
+ screen = DefaultScreen( display );
+ shmCompletionType = (XShmQueryExtension( display ) == True)
+ ? XShmGetEventBase( display ) + ShmCompletion : -1;
+
+ width = 0;
+ height = 0;
+ dscbTimeOut = 0;
+
+ // Initialize xine visual structure
+ visual.display = display;
+ visual.screen = screen;
+ visual.d = xcomWindow;
+ visual.dest_size_cb = &dest_size_cb;
+ visual.frame_output_cb = &frame_output_cb;
+ visual.user_data = this;
+ }
+
+ // Initialize audio and video details
+ Arts::SoundServerV2 server = Arts::Reference( "global:Arts_SoundServerV2" );
+ audio.sample_rate = 0;
+ audio.num_channels = 0;
+ audio.bits_per_sample = 0;
+
+ flpos = 0.0;
+ if (!audioOnly)
+ if (pthread_create( &thread, 0, pthread_start_routine, this ))
+ {
+ arts_fatal( "could not create thread" );
+ }
+}
+
+xinePlayObject_impl::~xinePlayObject_impl()
+{
+ XEvent event;
+
+ halt();
+
+ // Send stop event to thread (X11 client message)
+ memset( &event, 0, sizeof(event) );
+
+ event.type = ClientMessage;
+ event.xclient.window = xcomWindow;
+ event.xclient.message_type = xcomAtomQuit;
+ event.xclient.format = 32;
+
+ if (!audioOnly)
+ {
+
+ XSendEvent( display, xcomWindow, True, 0, &event );
+
+ XFlush( display );
+
+ // Wait for the thread to die
+ pthread_join( thread, 0 );
+
+ }
+
+ // Destroy stream, xine and related resources
+ if (stream != 0)
+ {
+ halt();
+
+ xine_event_dispose_queue( queue );
+ xine_dispose( stream );
+ xine_close_audio_driver( xine, ao_port );
+ xine_close_video_driver( xine, vo_port );
+ }
+ if (xine != 0)
+ {
+ xine_shared_exit( xine );
+ }
+
+ pthread_mutex_destroy( &mutex );
+
+ if (!audioOnly)
+ {
+ XSync( display, False );
+ XDestroyWindow( display, xcomWindow );
+ XCloseDisplay( display );
+ }
+}
+
+bool xinePlayObject_impl::loadMedia( const string &url )
+{
+ bool result = false;
+
+ pthread_mutex_lock( &mutex );
+
+ mrl = "";
+
+ if (stream == 0)
+ {
+ if (xine == 0)
+ {
+ xine = xine_shared_init();
+ }
+
+ ao_port = init_audio_out_plugin( xine, &audio, &ao_driver );
+
+ if (xineForceXShm && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "XShm",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "Xv",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "XShm",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "OpenGL",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0)
+ {
+ vo_port = xine_open_video_driver( xine, 0,
+ XINE_VISUAL_TYPE_NONE, 0 );
+ }
+
+ if (ao_port != 0 && vo_port != 0 )
+ {
+ stream = xine_stream_new( xine, ao_port, vo_port );
+
+ if (stream != 0)
+ {
+ xine_set_param( stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, 0 );
+ xine_set_param( stream, XINE_PARAM_SPU_CHANNEL, -1 );
+
+ queue = xine_event_new_queue( stream );
+ xine_event_create_listener_thread( queue, xine_handle_event, this );
+ }
+ }
+ if (stream == 0)
+ {
+ if (ao_port != 0)
+ {
+ xine_close_audio_driver( xine, ao_port );
+ ao_port = 0;
+ }
+ if (vo_port != 0)
+ {
+ xine_close_video_driver( xine, vo_port );
+ vo_port = 0;
+ }
+ }
+ }
+
+ if (stream != 0)
+ {
+ if (xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 2 );
+
+ xine_stop( stream );
+
+ clearWindow();
+ }
+ if ((result = xine_open( stream, url.c_str() )))
+ {
+ mrl = url;
+ }
+
+ streamLength = 0;
+ streamPosition = 0;
+
+ width = 0;
+ height = 0;
+ }
+
+ pthread_mutex_unlock( &mutex );
+
+ return result;
+}
+
+string xinePlayObject_impl::description()
+{
+ return "xine aRts plugin";
+}
+
+poTime xinePlayObject_impl::currentTime()
+{
+ poTime time;
+ int pos_time;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && !mrl.empty())
+ {
+ if (xine_get_pos_length( stream, 0, &pos_time, 0 ))
+ {
+ streamPosition = pos_time;
+ }
+ else
+ {
+ pos_time = streamPosition;
+ }
+
+ time.seconds = pos_time / 1000;
+ time.ms = pos_time % 1000;
+ }
+ else
+ {
+ time.seconds = 0;
+ time.ms = 0;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return time;
+}
+
+poTime xinePlayObject_impl::overallTime()
+{
+ poTime time;
+ int length_time;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && !mrl.empty())
+ {
+ if (xine_get_pos_length( stream, 0, 0, &length_time ))
+ {
+ streamLength = length_time;
+ }
+ else
+ {
+ length_time = streamLength;
+ }
+
+ if (length_time <= 0)
+ {
+ length_time = 1;
+ }
+
+ time.seconds = length_time / 1000;
+ time.ms = length_time % 1000;
+ }
+ else
+ {
+ time.seconds = 0;
+ time.ms = 1;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return time;
+}
+
+poCapabilities xinePlayObject_impl::capabilities()
+{
+ int n;
+
+ pthread_mutex_lock( &mutex );
+
+ n = (stream == 0) ? 0 : xine_get_stream_info( stream, XINE_STREAM_INFO_SEEKABLE );
+
+ pthread_mutex_unlock( &mutex );
+
+ return static_cast<poCapabilities>( capPause | ((n == 0) ? 0 : capSeek) );
+}
+
+string xinePlayObject_impl::mediaName()
+{
+ return mrl;
+}
+
+poState xinePlayObject_impl::state()
+{
+ poState state;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream == 0 || xine_get_status( stream ) != XINE_STATUS_PLAY)
+ state = posIdle;
+ else if (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE)
+ state = posPaused;
+ else
+ state = posPlaying;
+
+ pthread_mutex_unlock( &mutex );
+
+ return state;
+}
+
+void xinePlayObject_impl::play()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ if (xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ if (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE)
+ {
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );
+ }
+ }
+ else if (!mrl.empty())
+ {
+ xine_play( stream, 0, 0 );
+ }
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::halt()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 2 );
+
+ xine_stop( stream );
+
+ clearWindow();
+
+ streamLength = 0;
+ streamPosition = 0;
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::seek( const class poTime &t )
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ int seekPosition = (1000 * t.seconds) + t.ms;
+ int paused = (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE);
+
+ ao_fifo_clear( ao_driver, 1 );
+
+ if (xine_play( stream, 0, seekPosition ))
+ {
+ if (seekPosition >= 0 && seekPosition <= streamLength)
+ {
+ streamPosition = seekPosition;
+ }
+ }
+
+ if (paused)
+ {
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
+ }
+
+ ao_fifo_clear( ao_driver, 0 );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::pause()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 1 );
+
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::calculateBlock( unsigned long samples )
+{
+ unsigned int skip, received = 0, converted = 0, xSamples = 0;
+ unsigned char *buffer;
+ double speed = 1.0;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ // Calculate resampling parameters
+ speed = (double)audio.sample_rate / samplingRateFloat;
+ xSamples = (unsigned int)((double)samples * speed + 8.0);
+ received = ao_fifo_read( ao_driver, &buffer, xSamples );
+ }
+
+ pthread_mutex_unlock( &mutex );
+
+ // Convert samples and fill gaps with zeroes
+ if (received)
+ {
+ converted = uni_convert_stereo_2float( samples, buffer, received,
+ audio.num_channels,
+ audio.bits_per_sample,
+ left, right, speed, flpos );
+ flpos += (double)converted * speed;
+ skip = (int)floor( flpos );
+ skip = (received < (xSamples - 8)) ? (xSamples - 8) : skip;
+ flpos = flpos - floor( flpos );
+ ao_fifo_flush( ao_driver, skip );
+ }
+ for (unsigned long i=converted; i < samples; i++)
+ {
+ left[i] = 0;
+ right[i] = 0;
+ }
+}
+
+void xinePlayObject_impl::xineEvent( const xine_event_t &event )
+{
+ if (event.type == XINE_EVENT_UI_PLAYBACK_FINISHED)
+ {
+ clearWindow();
+ }
+}
+
+void xinePlayObject_impl::clearWindow()
+{
+ if (audioOnly) return;
+
+ Window root;
+ unsigned int u, w, h;
+ int x, y, screen;
+
+ XLockDisplay( display );
+
+ screen = DefaultScreen( display );
+
+ XGetGeometry( display, visual.d, &root, &x, &y, &w, &h, &u, &u );
+
+ XSetForeground( display, DefaultGC( display, screen ),
+ BlackPixel( display, screen ) );
+ XFillRectangle( display, visual.d,
+ DefaultGC( display, screen ), x, y, w, h );
+
+ XUnlockDisplay( display );
+}
+
+void xinePlayObject_impl::frameOutput( int &x, int &y,
+ int &width, int &height, double &ratio,
+ int displayWidth, int displayHeight,
+ double displayPixelAspect, bool dscb )
+{
+ if (audioOnly) return;
+
+ Window child, root;
+ unsigned int u;
+ int n;
+
+ XLockDisplay( display );
+
+ XGetGeometry( display, visual.d, &root, &n, &n,
+ (unsigned int *)&width, (unsigned int *)&height, &u, &u );
+
+ if (!dscb)
+ {
+ XTranslateCoordinates( display, visual.d, root, 0, 0, &x, &y, &child );
+ }
+
+ // Most displays use (nearly) square pixels
+ ratio = 1.0;
+
+ // Correct for display pixel aspect
+ if (displayPixelAspect < 1.0)
+ {
+ displayHeight = (int)((displayHeight / displayPixelAspect) + .5);
+ }
+ else
+ {
+ displayWidth = (int)((displayWidth * displayPixelAspect) + .5);
+ }
+
+ if (dscb || dscbTimeOut == 0 || --dscbTimeOut == 0)
+ {
+ // Notify client of new display size
+ if (displayWidth != this->width || displayHeight != this->height)
+ {
+ this->width = displayWidth;
+ this->height = displayHeight;
+
+ resizeNotify();
+ }
+
+ // Reset 'seen dest_size_cb' time out
+ if (dscb)
+ {
+ dscbTimeOut = 25;
+ }
+ }
+ XUnlockDisplay( display );
+}
+
+void xinePlayObject_impl::resizeNotify()
+{
+ if (audioOnly) return;
+ XEvent event;
+
+ // Resize notify signal for front-ends
+ memset( &event, 0, sizeof(event) );
+
+ event.type = ClientMessage;
+ event.xclient.window = visual.d;
+ event.xclient.message_type = xcomAtomResize;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = width;
+ event.xclient.data.l[1] = height;
+
+ XSendEvent( display, visual.d, True, 0, &event );
+
+ XFlush( display );
+}
+
+void xinePlayObject_impl::eventLoop()
+{
+ XEvent event;
+
+ do
+ {
+ XNextEvent( display, &event );
+
+ if (event.type == Expose && event.xexpose.count == 0 &&
+ event.xexpose.window == visual.d)
+ {
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_EXPOSE_EVENT,
+ &event );
+ }
+ else
+ {
+ clearWindow();
+ }
+ pthread_mutex_unlock( &mutex );
+ }
+ else if (event.type == shmCompletionType)
+ {
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_COMPLETION_EVENT,
+ &event );
+ }
+ pthread_mutex_unlock( &mutex );
+ }
+ }
+ while (event.type != ClientMessage ||
+ event.xclient.message_type != xcomAtomQuit ||
+ event.xclient.window != xcomWindow);
+}
+
+void xineVideoPlayObject_impl::x11WindowId( long window )
+{
+ pthread_mutex_lock( &mutex );
+
+ if (window == -1)
+ {
+ window = xcomWindow;
+ }
+
+ if ((Window)window != visual.d)
+ {
+ XLockDisplay( display );
+
+ // Change window and set event mask of new window
+ visual.d = window;
+
+ XSelectInput( display, window, ExposureMask );
+
+ if (stream != 0)
+ {
+ resizeNotify();
+
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_DRAWABLE_CHANGED,
+ (void *)window );
+ }
+
+ XUnlockDisplay( display );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+long xineVideoPlayObject_impl::x11WindowId()
+{
+ return (visual.d == xcomWindow) ? (long)-1 : visual.d;
+}
+
+long xineVideoPlayObject_impl::x11Snapshot()
+{
+ long pixmap = -1;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ // FIXME: snapshot...
+ pixmap = (long)-1;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return pixmap;
+}
+
+REGISTER_IMPLEMENTATION(xinePlayObject_impl);
+REGISTER_IMPLEMENTATION(xineAudioPlayObject_impl);
+REGISTER_IMPLEMENTATION(xineVideoPlayObject_impl);