diff options
Diffstat (limited to 'xine_artsplugin')
20 files changed, 2503 insertions, 0 deletions
diff --git a/xine_artsplugin/Makefile.am b/xine_artsplugin/Makefile.am new file mode 100644 index 00000000..799bff29 --- /dev/null +++ b/xine_artsplugin/Makefile.am @@ -0,0 +1,25 @@ +INCLUDES= -I$(kde_includes)/arts $(all_includes) $(XINE_CFLAGS) + +AM_CFLAGS = -U__STRICT_ANSI__ + +lib_LTLIBRARIES = libarts_xine.la + +libarts_xine_la_SOURCES = xinePlayObject.cc \ + xinePlayObject_impl.cpp \ + audio_fifo_out.c +libarts_xine_la_LDFLAGS = $(all_libraries) -module -no-undefined -pthread +libarts_xine_la_LIBADD = $(XINE_LIBS) $(LIB_X11) $(LIB_XEXT) \ + -lkmedia2_idl -lsoundserver_idl -lartsflow + +libarts_xine_la_METASOURCES = AUTO + +$(srcdir)/xinePlayObject_impl.cpp: xinePlayObject.h +xinePlayObject.cc xinePlayObject.h: $(srcdir)/xinePlayObject.idl + $(MCOPIDL) -I$(kde_includes)/arts $(srcdir)/xinePlayObject.idl + +noinst_HEADERS = xinePlayObject_impl.h audio_fifo_out.h + +mcopclassdir = $(libdir)/mcop +mcopclass_DATA = xineAudioPlayObject.mcopclass xineVideoPlayObject.mcopclass + +SUBDIRS = tools diff --git a/xine_artsplugin/audio_fifo_out.c b/xine_artsplugin/audio_fifo_out.c new file mode 100644 index 00000000..eb5e24bc --- /dev/null +++ b/xine_artsplugin/audio_fifo_out.c @@ -0,0 +1,407 @@ +/* + 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 <inttypes.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> +#include <xine/audio_out.h> + +#include "audio_fifo_out.h" + +#define GAP_TOLERANCE 5000 + +typedef struct fifo_driver_s +{ + ao_driver_t ao_driver; + + xine_arts_audio *audio; + + int capabilities; + int mode; + pthread_mutex_t read_mutex; + pthread_mutex_t write_mutex; + pthread_cond_t cond; + + uint32_t bytes_per_frame; + uint8_t *fifo; + int fifo_size; + int fifo_read_ptr; + int fifo_write_ptr; + int fifo_flush; + int fifo_delay; +} fifo_driver_t; + +/* + * open the audio device for writing to + */ +static int ao_fifo_open( ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + + if ((mode & ao->capabilities) == 0) + { + fprintf( stderr, "fifo_audio_out: unsupported mode %08x\n", mode); + return 0; + } + + /* lock read buffer */ + pthread_mutex_lock( &ao->read_mutex ); + + ao->mode = mode; + ao->audio->sample_rate = rate; + ao->audio->bits_per_sample = bits; + + switch (mode) + { + case AO_CAP_MODE_MONO: + ao->audio->num_channels = 1; + break; + case AO_CAP_MODE_STEREO: + ao->audio->num_channels = 2; + break; + } + + ao->bytes_per_frame = (ao->audio->bits_per_sample * ao->audio->num_channels) / 8; + ao->fifo_size = ao->audio->sample_rate * ao->bytes_per_frame; + ao->fifo = malloc( 2*ao->fifo_size ); + ao->fifo_read_ptr = 0; + ao->fifo_write_ptr = 0; + ao->fifo_flush = 0; + ao->fifo_delay = 0; + + /* unlock and enable read buffer for aRts sound server */ + pthread_mutex_unlock( &ao->read_mutex ); + + return ao->audio->sample_rate; +} + +static int ao_fifo_num_channels( ao_driver_t *this_gen ) +{ + return ((fifo_driver_t *)this_gen)->audio->num_channels; +} + +static int ao_fifo_bytes_per_frame( ao_driver_t *this_gen ) +{ + return ((fifo_driver_t *)this_gen)->bytes_per_frame; +} + +static int ao_fifo_get_gap_tolerance( ao_driver_t *this_gen ) +{ + return GAP_TOLERANCE; +} + +static int ao_fifo_bytes_in_buffer( fifo_driver_t *ao ) +{ + int bytes_in_buffer = (ao->fifo_write_ptr - ao->fifo_read_ptr); + + if (bytes_in_buffer < 0) + { + bytes_in_buffer += ao->fifo_size; + } + return bytes_in_buffer; +} + +static int ao_fifo_write( ao_driver_t *this_gen, int16_t *data, uint32_t num_frames ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + uint8_t *src = (uint8_t *)data; + int bytes_in_buffer, bytes_to_write, written; + + bytes_to_write = (num_frames * ao->bytes_per_frame); + + pthread_mutex_lock( &ao->write_mutex ); + + while (!ao->fifo_flush && bytes_to_write > 0) + { + bytes_in_buffer = ao_fifo_bytes_in_buffer( ao ); + written = bytes_to_write; + + if ((bytes_in_buffer + written) >= ao->fifo_size) + { + written = (ao->fifo_size - bytes_in_buffer - 1); + written -= (written % ao->bytes_per_frame); + + if (written == 0) + { + struct timespec ts; + struct timeval tv; + int delay; + + gettimeofday( &tv, 0 ); + + delay = ao_fifo_arts_delay(); + delay += (1000 * num_frames) / ao->audio->sample_rate; + delay = (delay < 20) ? 20 : ((delay >= 250) ? 250 : delay + 1); + + ts.tv_sec = tv.tv_sec + (delay / 1000); + ts.tv_nsec = (1000 * tv.tv_usec) + (1000000 * (delay % 1000)); + + if (ts.tv_nsec >= 1000000000) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + if (pthread_cond_timedwait( &ao->cond, &ao->write_mutex, &ts ) != 0) + { + fprintf( stderr, "fifo_audio_out: blocked for more than %d ms,\n", delay); + fprintf( stderr, "fifo_audio_out: %d sample(s) discarded.\n", num_frames); + pthread_mutex_unlock( &ao->write_mutex ); + return 0; + } + } + } + + if (!ao->fifo_flush && written > 0) + { + int new_write_ptr = (ao->fifo_write_ptr + written); + + if (new_write_ptr >= ao->fifo_size) + { + new_write_ptr -= ao->fifo_size; + + memcpy( &ao->fifo[ao->fifo_write_ptr], src, (written - new_write_ptr) ); + memcpy( ao->fifo, &src[written - new_write_ptr], new_write_ptr ); + } + else + { + memcpy( &ao->fifo[ao->fifo_write_ptr], src, written ); + } + + /* update audio buffer pointer */ + ao->fifo_write_ptr = new_write_ptr; + bytes_to_write -= written; + src += written; + } + } + + /* audio has stopped */ + ao->fifo_delay += bytes_to_write; + + pthread_mutex_unlock( &ao->write_mutex ); + + return 1; +} + +static int ao_fifo_delay( ao_driver_t *this_gen ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + return (ao_fifo_arts_delay() * ao->audio->sample_rate / 1000) + + ((ao_fifo_bytes_in_buffer( ao ) + ao->fifo_delay) / ao->bytes_per_frame); +} + +static void ao_fifo_close( ao_driver_t *this_gen ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + + /* lock read buffer */ + pthread_mutex_lock( &ao->read_mutex ); + + /* disable audio driver */ + ao->fifo_flush = 2; + ao->fifo_delay = 0; + + /* free audio FIFO */ + if (ao->fifo) + { + free( ao->fifo ); + ao->fifo = NULL; + } + pthread_mutex_unlock( &ao->read_mutex ); +} + +static uint32_t ao_fifo_get_capabilities( ao_driver_t *this_gen ) +{ + return ((fifo_driver_t *)this_gen)->capabilities; +} + +static void ao_fifo_exit( ao_driver_t *this_gen ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + + ao_fifo_close( this_gen ); + + pthread_cond_destroy( &ao->cond ); + pthread_mutex_destroy( &ao->read_mutex ); + pthread_mutex_destroy( &ao->write_mutex ); + + free( ao ); +} + +static int ao_fifo_get_property( ao_driver_t *this_gen, int property ) +{ + return 0; +} + +static int ao_fifo_set_property( ao_driver_t *this_gen, int property, int value ) +{ + return ~value; +} + +static int ao_fifo_control( ao_driver_t *this_gen, int cmd, ... ) +{ + fifo_driver_t *ao = (fifo_driver_t *)this_gen; + + switch (cmd) + { + case AO_CTRL_FLUSH_BUFFERS: + case AO_CTRL_PLAY_PAUSE: + /* flush audio FIFO */ + pthread_mutex_lock( &ao->read_mutex ); + + ao->fifo_read_ptr = ao->fifo_write_ptr; + + if (ao->fifo_flush == 1) + { + ao->fifo_flush = 0; + ao->fifo_delay = 0; + } + pthread_mutex_unlock( &ao->read_mutex ); + + break; + + case AO_CTRL_PLAY_RESUME: + break; + } + return 0; +} + +xine_audio_port_t *init_audio_out_plugin( xine_t *xine, xine_arts_audio *audio, + void **ao_driver ) +{ + fifo_driver_t *ao = (fifo_driver_t *)malloc( sizeof(fifo_driver_t) ); + + ao->audio = audio; + ao->fifo = NULL; + ao->fifo_read_ptr = 0; + ao->fifo_write_ptr = 0; + ao->fifo_flush = 2; + ao->fifo_delay = 0; + ao->capabilities = (AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO); + + ao->ao_driver.get_capabilities = ao_fifo_get_capabilities; + ao->ao_driver.get_property = ao_fifo_get_property; + ao->ao_driver.set_property = ao_fifo_set_property; + ao->ao_driver.open = ao_fifo_open; + ao->ao_driver.num_channels = ao_fifo_num_channels; + ao->ao_driver.bytes_per_frame = ao_fifo_bytes_per_frame; + ao->ao_driver.delay = ao_fifo_delay; + ao->ao_driver.write = ao_fifo_write; + ao->ao_driver.close = ao_fifo_close; + ao->ao_driver.exit = ao_fifo_exit; + ao->ao_driver.get_gap_tolerance = ao_fifo_get_gap_tolerance; + ao->ao_driver.control = ao_fifo_control; + + pthread_cond_init( &ao->cond, NULL ); + pthread_mutex_init( &ao->read_mutex, NULL ); + pthread_mutex_init( &ao->write_mutex, NULL ); + + *ao_driver = (void *)ao; + + return _x_ao_new_port( xine, (ao_driver_t *)ao, 0 ); +} + +unsigned long ao_fifo_read( void *ao_driver, unsigned char **buffer, + unsigned long samples ) +{ + fifo_driver_t *ao = (fifo_driver_t *)ao_driver; + int bytes_in_buffer, bytes_to_read; + + /* lock read buffer */ + pthread_mutex_lock( &ao->read_mutex ); + + bytes_in_buffer = ao_fifo_bytes_in_buffer( ao ); + bytes_to_read = (samples * ao->bytes_per_frame); + + if (ao->fifo_flush || bytes_in_buffer == 0) + { + /* unlock read buffer */ + pthread_mutex_unlock( &ao->read_mutex ); + + /* signal blocked writes */ + pthread_mutex_lock( &ao->write_mutex ); + pthread_cond_signal( &ao->cond ); + pthread_mutex_unlock( &ao->write_mutex ); + + /* audio FIFO empty or disabled, return */ + return 0; + } + + if (bytes_to_read > bytes_in_buffer) + { + fprintf( stderr, "fifo_audio_out: audio buffer underflow!\n" ); + bytes_to_read = bytes_in_buffer - (bytes_in_buffer % ao->bytes_per_frame); + } + if ((ao->fifo_read_ptr + bytes_to_read) > ao->fifo_size) + { + /* copy samples from front to end of buffer */ + memcpy( &ao->fifo[ao->fifo_size], ao->fifo, + ((ao->fifo_read_ptr + bytes_to_read) - ao->fifo_size) ); + } + + /* return pointer to audio samples */ + *buffer = &ao->fifo[ao->fifo_read_ptr]; + + return bytes_to_read; +} + +void ao_fifo_flush( void *ao_driver, unsigned long samples ) +{ + fifo_driver_t *ao = (fifo_driver_t *)ao_driver; + int bytes_in_buffer, bytes_to_flush; + + /* flush audio data */ + bytes_in_buffer = ao_fifo_bytes_in_buffer( ao ); + bytes_to_flush = (samples * ao->bytes_per_frame); + + if (bytes_to_flush <= bytes_in_buffer) + { + int new_read_ptr = (ao->fifo_read_ptr + bytes_to_flush); + + if (new_read_ptr >= ao->fifo_size) + { + new_read_ptr -= ao->fifo_size; + } + ao->fifo_read_ptr = new_read_ptr; + } + + /* unlock read buffer */ + pthread_mutex_unlock( &ao->read_mutex ); + + /* signal blocked writes */ + pthread_mutex_lock( &ao->write_mutex ); + pthread_cond_signal( &ao->cond ); + pthread_mutex_unlock( &ao->write_mutex ); +} + +void ao_fifo_clear( void *ao_driver, int clear ) +{ + fifo_driver_t *ao = (fifo_driver_t *)ao_driver; + + pthread_mutex_lock( &ao->write_mutex ); + + /* enable/disable audio driver */ + ao->fifo_flush = clear; + ao->fifo_delay = 0; + + if (clear) + { + /* signal blocked writes */ + pthread_cond_signal( &ao->cond ); + } + pthread_mutex_unlock( &ao->write_mutex ); +} diff --git a/xine_artsplugin/audio_fifo_out.h b/xine_artsplugin/audio_fifo_out.h new file mode 100644 index 00000000..b6629001 --- /dev/null +++ b/xine_artsplugin/audio_fifo_out.h @@ -0,0 +1,44 @@ +/* + 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. +*/ + +#ifndef __AUDIO_FIFO_OUT_H +#define __AUDIO_FIFO_OUT_H + +#include <xine.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + int32_t sample_rate; + uint32_t num_channels; + uint32_t bits_per_sample; +} xine_arts_audio; + + +xine_audio_port_t *init_audio_out_plugin( xine_t *xine, xine_arts_audio *audio, + void **ao_driver ); + +int ao_fifo_arts_delay(); + +unsigned long ao_fifo_read( void *ao_driver, unsigned char **buffer, + unsigned long samples ); + +void ao_fifo_flush( void *ao_driver, unsigned long samples ); + +void ao_fifo_clear( void *ao_driver, int clear ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/xine_artsplugin/configure.in.in b/xine_artsplugin/configure.in.in new file mode 100644 index 00000000..a79269b1 --- /dev/null +++ b/xine_artsplugin/configure.in.in @@ -0,0 +1,257 @@ +dnl Configure paths for XINE +dnl +dnl Copyright (C) 2001 Daniel Caujolle-Bert <segfault@club-internet.fr> +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +dnl +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a configuration +dnl script generated by Autoconf, you may include it under the same +dnl distribution terms that you use for the rest of that program. +dnl +if test "x$build_arts" = "xno"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE xine_artsplugin" +fi + + +dnl AC_PATH_XINE([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for XINE, and define XINE_CFLAGS and XINE_LIBS +dnl +AC_DEFUN([AC_PATH_XINE], +[dnl +dnl Get the cflags and libraries from the xine-config script +dnl +AC_ARG_WITH(xine-prefix, + [ --with-xine-prefix=PFX Prefix where XINE is installed (optional)], + xine_config_prefix="$withval", xine_config_prefix="") +AC_ARG_WITH(xine-exec-prefix, + [ --with-xine-exec-prefix=PFX Exec prefix where XINE is installed (optional)], + xine_config_exec_prefix="$withval", xine_config_exec_prefix="") +AC_ARG_ENABLE(xinetest, + [ --disable-xinetest Do not try to compile and run a test XINE program],, enable_xinetest=yes) + + if test x$xine_config_exec_prefix != x ; then + xine_config_args="$xine_config_args --exec-prefix=$xine_config_exec_prefix" + if test x${XINE_CONFIG+set} != xset ; then + XINE_CONFIG=$xine_config_exec_prefix/bin/xine-config + fi + fi + if test x$xine_config_prefix != x ; then + xine_config_args="$xine_config_args --prefix=$xine_config_prefix" + if test x${XINE_CONFIG+set} != xset ; then + XINE_CONFIG=$xine_config_prefix/bin/xine-config + fi + fi + + min_xine_version=ifelse([$1], ,0.5.0,$1) + if test "x$enable_xinetest" != "xyes" ; then + AC_MSG_CHECKING([for XINE-LIB version >= $min_xine_version]) + else + AC_PATH_PROG(XINE_CONFIG, xine-config, no) + AC_MSG_CHECKING([for XINE-LIB version >= $min_xine_version]) + no_xine="" + if test "$XINE_CONFIG" = "no" ; then + no_xine=yes + else + XINE_CFLAGS=`$XINE_CONFIG $xine_config_args --cflags` + XINE_LIBS=`$XINE_CONFIG $xine_config_args --libs` + xine_config_major_version=`$XINE_CONFIG $xine_config_args --version | \ + sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\1/p'` + xine_config_minor_version=`$XINE_CONFIG $xine_config_args --version | \ + sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\2/p'` + xine_config_sub_version=`$XINE_CONFIG $xine_config_args --version | \ + sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\3/p'` + xine_script_dir=`$XINE_CONFIG $xine_config_args --scriptdir` + xine_plugin_dir=`$XINE_CONFIG $xine_config_args --plugindir` + xine_locale_dir=`$XINE_CONFIG $xine_config_args --localedir` + dnl if test "x$enable_xinetest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $XINE_CFLAGS $all_includes" + LIBS="$XINE_LIBS $LIBS $all_libraries" +dnl +dnl Now check if the installed XINE is sufficiently new. (Also sanity +dnl checks the results of xine-config to some extent +dnl + AC_LANG_SAVE() + AC_LANG_C() + rm -f conf.xinetest + AC_TRY_RUN([ +#include <xine.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int +main () +{ + int major, minor, sub; + char *tmp_version; + + system ("touch conf.xinetest"); + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = (char *) strdup("$min_xine_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &sub) != 3) { + printf("%s, bad version string\n", "$min_xine_version"); + exit(1); + } + + if ((XINE_MAJOR_VERSION != $xine_config_major_version) || + (XINE_MINOR_VERSION != $xine_config_minor_version) || + (XINE_SUB_VERSION != $xine_config_sub_version)) + { + printf("\n*** 'xine-config --version' returned %d.%d.%d, but XINE (%d.%d.%d)\n", + $xine_config_major_version, $xine_config_minor_version, $xine_config_sub_version, + XINE_MAJOR_VERSION, XINE_MINOR_VERSION, XINE_SUB_VERSION); + printf ("*** was found! If xine-config was correct, then it is best\n"); + printf ("*** to remove the old version of XINE. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If xine-config was wrong, set the environment variable XINE_CONFIG\n"); + printf("*** to point to the correct copy of xine-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } + else + { + if ((XINE_MAJOR_VERSION > major) || + ((XINE_MAJOR_VERSION == major) && (XINE_MINOR_VERSION > minor)) || + ((XINE_MAJOR_VERSION == major) && (XINE_MINOR_VERSION == minor) && (XINE_SUB_VERSION >= sub))) + { + return 0; + } + else + { + printf("\n*** An old version of XINE (%d.%d.%d) was found.\n", + XINE_MAJOR_VERSION, XINE_MINOR_VERSION, XINE_SUB_VERSION); + printf("*** You need a version of XINE newer than %d.%d.%d. The latest version of\n", + major, minor, sub); + printf("*** XINE is always available from:\n"); + printf("*** http://xine.sourceforge.net\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the xine-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of XINE, but you can also set the XINE_CONFIG environment to point to the\n"); + printf("*** correct copy of xine-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_xine=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_xine" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$XINE_CONFIG" = "no" ; then + echo "*** The xine-config script installed by XINE could not be found" + echo "*** If XINE was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the XINE_CONFIG environment variable to the" + echo "*** full path to xine-config." + else + if test -f conf.xinetest ; then + : + else + echo "*** Could not run XINE test program, checking why..." + CFLAGS="$CFLAGS $XINE_CFLAGS" + LIBS="$LIBS $XINE_LIBS" + AC_TRY_LINK([ +#include <xine.h> +#include <stdio.h> +], [ return ((XINE_MAJOR_VERSION) || (XINE_MINOR_VERSION) || (XINE_SUB_VERSION)); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding XINE or finding the wrong" + echo "*** version of XINE. If it is not finding XINE, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" + echo "***"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means XINE was incorrectly installed" + echo "*** or that you have moved XINE since it was installed. In the latter case, you" + echo "*** may want to edit the xine-config script: $XINE_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + XINE_CFLAGS="" + XINE_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(XINE_CFLAGS) + AC_SUBST(XINE_LIBS) + AC_LANG_RESTORE() + rm -f conf.xinetest + +dnl Make sure HAVE_STRSEP, HAVE_SETENV and HAVE_STRPBRK are defined as +dnl necessary. + AC_CHECK_FUNCS([strsep strpbrk setenv]) +]) + +dnl Check for XShmGetEventBase +AC_MSG_CHECKING([for XShmGetEventBase]) +AC_LANG_SAVE() +AC_LANG_CPLUSPLUS() +AC_TRY_COMPILE([ +#include <X11/Xlib.h> +#include <X11/extensions/XShm.h> + ],[ +static Display *display = NULL; +int shmCompletionType = XShmGetEventBase( display ); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_XSHMGETEVENTBASE, 1, [Define if you have XShmGetEventBase in <X11/extensions/XShm.h]) + ],[ + AC_MSG_RESULT(no) + ] +) +AC_LANG_RESTORE() + +dnl Check for new internal xine symbol names +KDE_CHECK_LIB(xine, _x_ao_new_port, :, +[ + AC_DEFINE(_x_ao_new_port, ao_new_port, [Compatibility with older version of xine]) +]) +AC_CHECK_FUNC([ao_new_port]) + +AC_ARG_WITH([xine], + [AC_HELP_STRING([--with-xine], + [Enable support for Xine @<:@default=check@:>@])], + [], with_xine=check) + +have_xine=no +if test "x$with_xine" != xno; then + AC_PATH_XINE(1.0.0, have_xine=yes) + + if test "x$with_xine" != xcheck && test "x$have_xine" != xyes; then + AC_MSG_ERROR([--with-xine was given, but test for Xine failed]) + fi +fi + +if test "x$have_xine" != xyes; then + DO_NOT_COMPILE="$DO_NOT_COMPILE xine_artsplugin" +fi diff --git a/xine_artsplugin/tools/Makefile.am b/xine_artsplugin/tools/Makefile.am new file mode 100644 index 00000000..1ed9efda --- /dev/null +++ b/xine_artsplugin/tools/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=thumbnail diff --git a/xine_artsplugin/tools/thumbnail/Makefile.am b/xine_artsplugin/tools/thumbnail/Makefile.am new file mode 100644 index 00000000..40358fda --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/Makefile.am @@ -0,0 +1,24 @@ +## $Id$ +## Makefile.am of kdemultimedia/tools/thumbnail + +INCLUDES = -I$(kde_includes)/arts $(all_includes) $(XINE_CFLAGS) + +AM_CFLAGS = -U__STRICT_ANSI__ + +METASOURCES = AUTO + +kde_module_LTLIBRARIES = videothumbnail.la + +videothumbnail_la_SOURCES = videocreator.cpp videoscaler.cpp +videothumbnail_la_LIBADD = $(XINE_LIBS) $(LIB_KDECORE) +videothumbnail_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -pthread + +noinst_HEADERS = videocreator.h + +xineartsplugin_tools_videothumbnail_DATA = sprocket-small.png sprocket-medium.png sprocket-large.png + +xineartsplugin_tools_videothumbnaildir = $(kde_datadir)/videothumbnail + +services_DATA = videothumbnail.desktop + +servicesdir = $(kde_servicesdir) diff --git a/xine_artsplugin/tools/thumbnail/sprocket-large.png b/xine_artsplugin/tools/thumbnail/sprocket-large.png Binary files differnew file mode 100644 index 00000000..6c0e65a9 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/sprocket-large.png diff --git a/xine_artsplugin/tools/thumbnail/sprocket-medium.png b/xine_artsplugin/tools/thumbnail/sprocket-medium.png Binary files differnew file mode 100644 index 00000000..8868e660 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/sprocket-medium.png diff --git a/xine_artsplugin/tools/thumbnail/sprocket-small.png b/xine_artsplugin/tools/thumbnail/sprocket-small.png Binary files differnew file mode 100644 index 00000000..62fa3fd7 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/sprocket-small.png diff --git a/xine_artsplugin/tools/thumbnail/videocreator.cpp b/xine_artsplugin/tools/thumbnail/videocreator.cpp new file mode 100644 index 00000000..94e87b48 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/videocreator.cpp @@ -0,0 +1,376 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Simon MacMullen + Copyright (C) 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 Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// $Id$ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <pthread.h> +#include <sys/time.h> + +#include <qpixmap.h> +#include <qdialog.h> +#include <qfile.h> +#include <qimage.h> +#include <qpainter.h> +#include <qpaintdevice.h> + +#include <iostream> + +#include <kstandarddirs.h> +#include <kapplication.h> + +#define XINE_ENABLE_EXPERIMENTAL_FEATURES 1 + +#include <xine.h> + +#include "videocreator.h" +#include "videoscaler.h" + +#define TIMEOUT 15 // 15 seconds +#define MAX_ATTEMPTS 25 + + +// 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 void xine_init_routine() +{ + 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 ); + + 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 ); +} + +static QImage createThumbnail( xine_video_frame_t *frame, int width, int height ) +{ + unsigned char *base[3]; + unsigned int pitches[3]; + + if ((frame->aspect_ratio * height) > width) + height = (int)(.5 + (width / frame->aspect_ratio)); + else + width = (int)(.5 + (height * frame->aspect_ratio)); + + QImage image( width, height, 32 ); + + if (frame->colorspace == XINE_IMGFMT_YV12) + { + int y_size, uv_size; + + pitches[0] = (frame->width + 7) & ~0x7; + pitches[1] = (((frame->width + 1) / 2) + 7) & ~0x7; + pitches[2] = pitches[1]; + + y_size = pitches[0] * frame->height; + uv_size = pitches[1] * ((frame->height + 1) / 2); + + base[0] = frame->data; + base[1] = base[0] + y_size + uv_size; + base[2] = base[0] + y_size; + + scaleYuvToRgb32( frame->width, frame->height, base, pitches, + width, height, (unsigned int *)image.bits(), + image.bytesPerLine() ); + } + else if (frame->colorspace == XINE_IMGFMT_YUY2) + { + pitches[0] = 2*((frame->width + 3) & ~0x3); + base[0] = frame->data; + + scaleYuy2ToRgb32( frame->width, frame->height, base[0], pitches[0], + width, height, (unsigned int *)image.bits(), + image.bytesPerLine() ); + } + return image; +} + +// Return the variance of the brightness of the pixels +static double imageVariance( unsigned char *pixels, int pitch, + int width, int height, int step ) +{ + double sigmaX = 0; + double sigmaXSquared = 0; + + for (int y=0; y < height ; y++) + { + unsigned int uSigmaX = 0; + unsigned int uSigmaXSquared = 0; + + for (int x=0, n=(width * step); x < n ; x+=step) + { + int gray = pixels[x]; + + uSigmaX += gray; + uSigmaXSquared += gray * gray; + } + + sigmaX += uSigmaX; + sigmaXSquared += uSigmaXSquared; + + pixels += pitch; + } + + unsigned int total = height * width; + + return sqrt( sigmaXSquared / total - (sigmaX / total) * (sigmaX / total) ); +} + +static bool findBestFrame( xine_video_port_t *vo_port, xine_video_frame_t *frame ) +{ + xine_video_frame_t frames[2], *bestFrame = NULL; + double variance, bestVariance = 0; + + for (int i=0, n=0; i < MAX_ATTEMPTS; i++) + { + xine_video_frame_t *cFrame = &frames[n]; + + // Try to read next frame + if (!xine_get_next_video_frame( vo_port, cFrame )) + { + break; + } + + variance = imageVariance( cFrame->data, ((cFrame->width + 7) & ~0x7), + cFrame->width, cFrame->height, + (cFrame->colorspace == XINE_IMGFMT_YV12) ? 1 : 2 ); + + // Compare current frame to best frame + if (bestFrame == NULL || variance > bestVariance) + { + if (bestFrame != NULL) + { + xine_free_video_frame( vo_port, bestFrame ); + } + + bestFrame = cFrame; + bestVariance = variance; + + n = (1 - n); + } + else + { + xine_free_video_frame( vo_port, cFrame ); + } + + // Stop searching if current frame is interesting enough + if (variance > 40.0) + { + break; + } + } + + // This should be the best frame to create a thumbnail from + if (bestFrame != NULL) + { + *frame = *bestFrame; + } + return (bestFrame != NULL); +} + + +extern "C" +{ + ThumbCreator *new_creator() + { + return new VideoCreator; + } +} + +VideoCreator::VideoCreator() +{ +} + +VideoCreator::~VideoCreator() +{ +} + +bool VideoCreator::create(const QString &path, int width, int height, QImage &img) +{ + if (m_sprocketSmall.isNull()) + { + QString pixmap = locate( "data", "videothumbnail/sprocket-small.png" ); + m_sprocketSmall = QPixmap(pixmap); + pixmap = locate( "data", "videothumbnail/sprocket-medium.png" ); + m_sprocketMedium = QPixmap(pixmap); + pixmap = locate( "data", "videothumbnail/sprocket-large.png" ); + m_sprocketLarge = QPixmap(pixmap); + } + + // The long term plan is to seek to frame 1, create thumbnail, see if is is + // interesting enough, if not seek to frame 2, then 4, then 8, etc. + // "Interesting enough" means the variance of the pixel brightness is high. This + // is because many videos fade up from black and a black rectangle is boring. + // + // But for the time being we can't seek so we just let it play for one second + // then take whatever we find. + + xine_t *xine = xine_shared_init(); + xine_audio_port_t *ao_port = xine_new_framegrab_audio_port( xine ); + xine_video_port_t *vo_port = xine_new_framegrab_video_port( xine ); + xine_stream_t *stream = xine_stream_new( xine, ao_port, vo_port ); + bool success = false; + + if (xine_open( stream, QFile::encodeName ( path ).data() )) + { + xine_video_frame_t frame; + int length; + + // Find 'best' (or at least any) frame + if (!xine_get_pos_length( stream, NULL, NULL, &length ) || length > 5000) + { + if (xine_play( stream, 0, 4000 )) + { + success = findBestFrame( vo_port, &frame ); + } + } + if (!success) + { + // Some codecs can't seek to start, but close/open works + xine_close( stream ); + xine_open( stream, path.ascii() ); + + if (xine_play( stream, 0, 0 )) + { + success = findBestFrame( vo_port, &frame ); + } + } + + // Create thumbnail image + if (success) + { + QPixmap pix( createThumbnail( &frame, width, height ) ); + QPainter painter( &pix ); + QPixmap sprocket; + + if (pix.height() < 60) + sprocket = m_sprocketSmall; + else if (pix.height() < 90) + sprocket = m_sprocketMedium; + else + sprocket = m_sprocketLarge; + + for (int y = 0; y < pix.height() + sprocket.height(); y += sprocket.height()) { + painter.drawPixmap( 0, y, sprocket ); + } + + img = pix.convertToImage(); + + xine_free_video_frame( vo_port, &frame ); + } + + xine_stop( stream ); + } + + xine_dispose( stream ); + xine_close_audio_driver( xine, ao_port ); + xine_close_video_driver( xine, vo_port ); + xine_shared_exit( xine ); + + return (success); +} + +ThumbCreator::Flags VideoCreator::flags() const +{ + return (ThumbCreator::Flags) (DrawFrame); +} + +#include "videocreator.moc" diff --git a/xine_artsplugin/tools/thumbnail/videocreator.h b/xine_artsplugin/tools/thumbnail/videocreator.h new file mode 100644 index 00000000..bd16e6e2 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/videocreator.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Simon MacMullen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _VIDEOCREATOR_H_ +#define _VIDEOCREATOR_H_ "$Id$" + +#include <kio/thumbcreator.h> + +class VideoCreator : public QObject, public ThumbCreator +{ + Q_OBJECT +public: + VideoCreator(); + virtual ~VideoCreator(); + virtual bool create(const QString &path, int width, int height, QImage &img); + virtual Flags flags() const; + +private: + QPixmap m_sprocketSmall; + QPixmap m_sprocketMedium; + QPixmap m_sprocketLarge; +}; + +#endif diff --git a/xine_artsplugin/tools/thumbnail/videoscaler.cpp b/xine_artsplugin/tools/thumbnail/videoscaler.cpp new file mode 100644 index 00000000..2b4b49ad --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/videoscaler.cpp @@ -0,0 +1,258 @@ +/* + This file is part of KDE/aRts - xine integration + Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl> + + This program 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. + + inspired by code from the xine project + + Copyright (C) 2003 the xine project +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <config.h> + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +#include "videoscaler.h" + +#define THUMBNAIL_BRIGHTNESS +32 +#define THUMBNAIL_CONTRAST 128 +#define THUMBNAIL_SATURATION 128 + + +// Colorspace conversion tables +static int tableLY[256]; +static int tableRV[256], tableBU[256], tableGU[256], tableGV[256]; +static int clipR[2240], clipG[2240], clipB[2240]; + +static pthread_once_t once_control = PTHREAD_ONCE_INIT; + + +static void init_once_routine() +{ + int cly = ( 76309 * THUMBNAIL_CONTRAST + 64) / 128; + int crv = (104597 * THUMBNAIL_SATURATION + 64) / 128; + int cbu = (132201 * THUMBNAIL_SATURATION + 64) / 128; + int cgu = ( 25675 * THUMBNAIL_SATURATION + 64) / 128; + int cgv = ( 53279 * THUMBNAIL_SATURATION + 64) / 128; + int i; + + for (i=0; i < 256; i++) + { + tableLY[i] = cly * (i + THUMBNAIL_BRIGHTNESS - 16) + (864 << 16) + 32768; + tableRV[i] = crv * (i - 128); + tableBU[i] = cbu * (i - 128); + tableGU[i] = cgu * (i - 128); + tableGV[i] = cgv * (i - 128); + } + for (i=0; i < 2240; i++) + { + int c = (i < 864) ? 0 : ((i > 1119) ? 255 : (i - 864)); + + clipR[i] = c << 16; + clipG[i] = c << 8; + clipB[i] = c; + } +} + +static void yuvToRgb32( unsigned char *bufy, unsigned char *bufu, unsigned char *bufv, + unsigned int *pixels, int width ) +{ + for (int i=0; i < width; i++) + { + int l = tableLY[bufy[i]]; + + pixels[i] = clipR[(l + tableRV[bufv[i]]) >> 16] | + clipG[(l - tableGU[bufu[i]] - tableGV[bufv[i]]) >> 16] | + clipB[(l + tableBU[bufu[i]]) >> 16]; + } +} + +static inline void scaleLine( unsigned char *src[2], int width, + unsigned char *dst, int scaledWidth, + int scale, int weight, int step, int offset ) +{ + int a, b, c, d; + int x = (scale / 2) - 32768; + unsigned char *p0 = (src[0] + offset); + unsigned char *p1 = (src[1] + offset); + + weight >>= 8; + + if (scaledWidth > width) + { + /* Trailing pixels, no horizontal filtering */ + c = scaledWidth - (((width << 16) - 32768 - (scale / 2)) / scale); + a = p0[(step * width) - step]; + b = p1[(step * width) - step]; + a += (128 + (b - a) * weight) >> 8; + scaledWidth -= c; + memset( &dst[scaledWidth], a, c ); + + /* Leading pixels, no horizontal filtering */ + c = (32767 + (scale / 2)) / scale; + a = p0[0]; + b = p1[0]; + a += (128 + (b - a) * weight) >> 8; + scaledWidth -= c; + memset( dst, a, c ); + + /* Adjust source and destination */ + dst += c; + x += (c * scale); + } + + for (int i=0; i < scaledWidth; i++) + { + int xhi = (step == 1) ? (x >> 16) + : ((step == 2) ? (x >> 15) & ~0x1 + : (x >> 14) & ~0x3); + int xlo = (x & 0xFFFF) >> 8; + + /* Four nearest points for bilinear filtering */ + a = p0[xhi]; + b = p0[xhi + step]; + c = p1[xhi];\ + d = p1[xhi + step]; + + /* Interpolate horizontally */ + a = (256 * a) + (b - a) * xlo; + c = (256 * c) + (d - c) * xlo; + + /* Interpolate vertically and store bilinear filtered sample */ + *(dst++) = ((256 * a) + (c - a) * weight + 32768) >> 16; + + x += scale; + } +} + +void scaleYuvToRgb32( int width, int height, + unsigned char *base[3], unsigned int pitches[3], + int scaledWidth, int scaledHeight, + unsigned int *pixels, unsigned int bytesPerLine ) +{ + int chromaWidth = (width + 1) / 2; + int chromaHeight = (height + 1) / 2; + int scaleX = (width << 16) / scaledWidth; + int scaleY = (height << 16) / scaledHeight; + int scaleCX = (scaleX / 2); + int y = (scaleY / 2) - 32768; + + // Temporary line buffers (stack) + unsigned char *bufy = (unsigned char *)alloca( scaledWidth ); + unsigned char *bufu = (unsigned char *)alloca( scaledWidth ); + unsigned char *bufv = (unsigned char *)alloca( scaledWidth ); + + pthread_once( &once_control, init_once_routine ); + + for (int i=0; i < scaledHeight; i++) + { + unsigned char *twoy[2], *twou[2], *twov[2]; + int y2 = (y / 2) - 32768; + + // Calculate luminance scanlines for bilinear filtered scaling + if (y < 0) + { + twoy[0] = twoy[1] = base[0]; + } + else if (y >= ((height - 1) << 16)) + { + twoy[0] = twoy[1] = base[0] + (height - 1) * pitches[0]; + } + else + { + twoy[0] = base[0] + (y >> 16) * pitches[0]; + twoy[1] = twoy[0] + pitches[0]; + } + + // Calculate chrominance scanlines for bilinear filtered scaling + if (y2 < 0) + { + twou[0] = twou[1] = base[1]; + twov[0] = twov[1] = base[2]; + } + else if (y2 >= ((chromaHeight - 1) << 16)) + { + twou[0] = twou[1] = base[1] + (chromaHeight - 1) * pitches[1]; + twov[0] = twov[1] = base[2] + (chromaHeight - 1) * pitches[2]; + } + else + { + twou[0] = base[1] + (y2 >> 16) * pitches[1]; + twov[0] = base[2] + (y2 >> 16) * pitches[2]; + twou[1] = twou[0] + pitches[1]; + twov[1] = twov[0] + pitches[2]; + } + + // Bilinear filtered scaling + scaleLine( twoy, width, bufy, scaledWidth, scaleX, (y & 0xFFFF), 1, 0 ); + scaleLine( twou, chromaWidth, bufu, scaledWidth, scaleCX, (y2 & 0xFFFF), 1, 0 ); + scaleLine( twov, chromaWidth, bufv, scaledWidth, scaleCX, (y2 & 0xFFFF), 1, 0 ); + + // YUV to RGB32 comnversion + yuvToRgb32( bufy, bufu, bufv, pixels, scaledWidth ); + + pixels = (unsigned int *)(((char *)pixels) + bytesPerLine); + y += scaleY; + } +} + +void scaleYuy2ToRgb32( int width, int height, + unsigned char *base, unsigned int pitch, + int scaledWidth, int scaledHeight, + unsigned int *pixels, unsigned int bytesPerLine ) +{ + int chromaWidth = (width + 1) / 2; + int scaleX = (width << 16) / scaledWidth; + int scaleY = (height << 16) / scaledHeight; + int scaleCX = (scaleX / 2); + int y = (scaleY / 2) - 32768; + + // Temporary line buffers (stack) + unsigned char *bufy = (unsigned char *)alloca( scaledWidth ); + unsigned char *bufu = (unsigned char *)alloca( scaledWidth ); + unsigned char *bufv = (unsigned char *)alloca( scaledWidth ); + + pthread_once( &once_control, init_once_routine ); + + for (int i=0; i < scaledHeight; i++) + { + unsigned char *two[2]; + + // Calculate scanlines for bilinear filtered scaling + if (y < 0) + { + two[0] = two[1] = base; + } + else if (y >= ((height - 1) << 16)) + { + two[0] = two[1] = base + (height - 1) * pitch; + } + else + { + two[0] = base + (y >> 16) * pitch; + two[1] = two[0] + pitch; + } + + // Bilinear filtered scaling + scaleLine( two, width, bufy, scaledWidth, scaleX, (y & 0xFFFF), 2, 0 ); + scaleLine( two, chromaWidth, bufu, scaledWidth, scaleCX, (y & 0xFFFF), 4, 1 ); + scaleLine( two, chromaWidth, bufv, scaledWidth, scaleCX, (y & 0xFFFF), 4, 3 ); + + // YUV to RGB32 comnversion + yuvToRgb32( bufy, bufu, bufv, pixels, scaledWidth ); + + pixels = (unsigned int *)(((char *)pixels) + bytesPerLine); + y += scaleY; + } +} diff --git a/xine_artsplugin/tools/thumbnail/videoscaler.h b/xine_artsplugin/tools/thumbnail/videoscaler.h new file mode 100644 index 00000000..fd4d51db --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/videoscaler.h @@ -0,0 +1,24 @@ +/* + This file is part of KDE/aRts - xine integration + Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl> + + This program 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. +*/ + +#ifndef __VIDEOSCALER_H +#define __VIDEOSCALER_H + +void scaleYuvToRgb32( int width, int height, + unsigned char *base[3], unsigned int pitches[3], + int scaledWidth, int scaledHeight, + unsigned int *pixels, unsigned int bytesPerLine ); + +void scaleYuy2ToRgb32( int width, int height, + unsigned char *base, unsigned int pitch, + int scaledWidth, int scaledHeight, + unsigned int *pixels, unsigned int bytesPerLine ); + +#endif diff --git a/xine_artsplugin/tools/thumbnail/videothumbnail.desktop b/xine_artsplugin/tools/thumbnail/videothumbnail.desktop new file mode 100644 index 00000000..e437b2a3 --- /dev/null +++ b/xine_artsplugin/tools/thumbnail/videothumbnail.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Type=Service +Name=Video Files +Name[af]=Video Lêers +Name[ar]=ملفات مرئيات +Name[bg]=Видео файлове +Name[bn]=ভিডিও ফাইল +Name[br]=Restroù Video +Name[bs]=Video datoteke +Name[ca]=Fitxers de vídeo +Name[cs]=Video soubory +Name[cy]=Ffeiliau Fideo +Name[da]=Videofiler +Name[de]=Video-Dateien +Name[el]=Αρχεία βίντεο +Name[eo]=Vidaj dosieroj +Name[es]=Archivos de vídeo +Name[et]=Videofailid +Name[eu]=Bideo fitxategiak +Name[fa]=پروندههای ویدیویی +Name[fi]=Videotiedostot +Name[fr]=Fichiers vidéo +Name[ga]=Comhaid Fhíse +Name[gl]=Ficheiros de Video +Name[he]=קבצי וידאו +Name[hi]=वीडियो फ़ाइलें +Name[hr]=Video datoteke +Name[hu]=Videófájlok +Name[is]=Kvikmyndaskrár +Name[it]=File Video +Name[ja]=ビデオファイル +Name[kk]=Бейне файлдар +Name[km]=ឯកសារវីដេអូ +Name[ko]=비디오 파일 +Name[lt]=Video bylos +Name[mk]=Видео датотеки +Name[nb]=Videofiler +Name[nds]=Videodateien +Name[ne]=भिडियो फाइल +Name[nl]=Videobestanden +Name[nn]=Videofiler +Name[pa]=ਵੀਡਿਓ ਫਾਇਲਾਂ +Name[pl]=Pliki wideo +Name[pt]=Ficheiros de Vídeo +Name[pt_BR]=Arquivos de vídeo +Name[ro]=Fişiere video +Name[ru]=Видеофайлы +Name[sk]=Video súbory +Name[sl]=Video datoteke +Name[sr]=Видео фајлови +Name[sr@Latn]=Video fajlovi +Name[sv]=Videofiler +Name[ta]=படக்காட்சி கோப்புகள் +Name[tg]=Файлҳои Видео +Name[th]=แฟ้มวิดีโอ +Name[tr]=Video Dosyaları +Name[uk]=Відеофайли +Name[uz]=Video fayllar +Name[uz@cyrillic]=Видео файллар +Name[ven]=Dzifaela dza Video +Name[wa]=Fitchîs videyo +Name[xh]=Iifayile ze Video +Name[zh_CN]=视频文件 +Name[zh_HK]=視訊檔案 +Name[zh_TW]=視訊檔案 +Name[zu]=Amafayela Evidiyo +ServiceTypes=ThumbCreator +MimeTypes=video/*,application/vnd.ms-asf,application/vnd.rn-realmedia +X-KDE-Library=videothumbnail +CacheThumbnail=true +IgnoreMaximumSize=true diff --git a/xine_artsplugin/xineAudioPlayObject.mcopclass b/xine_artsplugin/xineAudioPlayObject.mcopclass new file mode 100644 index 00000000..5a2f2225 --- /dev/null +++ b/xine_artsplugin/xineAudioPlayObject.mcopclass @@ -0,0 +1,7 @@ +Interface=xinePlayObject,Arts::PlayObject,Arts::SynthModule +Author=Ewald Snel <ewald@rambo.its.tudelft.nl> +Extension=flac,m4a,spx,ac3,aac,ogg,mp1,mp2,mp3,ra,wma,mpc,mp+ +MimeType=audio/vnd.rn-realaudio,audio/x-pn-realaudio,audio/x-flac,audio/x-oggflac,audio/x-speex,audio/mp4,audio/ac3,audio/aac,audio/vorbis,audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/mpeg,audio/x-ms-wma,audio/x-musepack +Language=C++ +Library=libarts_xine.la +Preference=4 diff --git a/xine_artsplugin/xinePlayObject.idl b/xine_artsplugin/xinePlayObject.idl new file mode 100644 index 00000000..7ba429d6 --- /dev/null +++ b/xine_artsplugin/xinePlayObject.idl @@ -0,0 +1,13 @@ +#include <kmedia2.idl> +#include <soundserver.idl> + +interface xinePlayObject : Arts::PlayObject, Arts::SynthModule +{ + out audio stream left, right; +}; + +interface xineAudioPlayObject : xinePlayObject, Arts::PlayObject, Arts::SynthModule +{}; + +interface xineVideoPlayObject : xinePlayObject, Arts::PlayObject, Arts::VideoPlayObject, Arts::SynthModule +{}; diff --git a/xine_artsplugin/xinePlayObject.mcopclass b/xine_artsplugin/xinePlayObject.mcopclass new file mode 100644 index 00000000..21536a78 --- /dev/null +++ b/xine_artsplugin/xinePlayObject.mcopclass @@ -0,0 +1,7 @@ +Interface=xinePlayObject,Arts::PlayObject,Arts::VideoPlayObject,Arts::SynthModule,Arts::Object +Author=Ewald Snel <ewald@rambo.its.tudelft.nl> +Extension=vob,mpg,mpeg,m1v,m2v,m1s,m2s,m2p,avi,asf,asx,wmv,qt,mov,moov,mp4,rv,ra,ram,rm,smi,flac,m4v,m4a,spx,ac3,aac +MimeType=video/x-mpg,video/x-dat,video/x-mpeg,video/mpeg,video/x-msvideo,video/x-ms-asf,video/x-ms-wmv,video/quicktime,video/x-theora,video/mp4,video/x-ogm,application/vnd.ms-asf,application/vnd.rn-realmedia,video/vnd.rn-realvideo,audio/vnd.rn-realaudio,audio/x-pn-realaudio,audio/x-flac,audio/x-speex,audio/mp4,audio/ac3,audio/aac,audio/vorbis,audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/mpeg +Language=C++ +Library=libarts_xine.la +Preference=4 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); diff --git a/xine_artsplugin/xinePlayObject_impl.h b/xine_artsplugin/xinePlayObject_impl.h new file mode 100644 index 00000000..94482dac --- /dev/null +++ b/xine_artsplugin/xinePlayObject_impl.h @@ -0,0 +1,158 @@ +/* + 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. +*/ + +#ifndef __XINEPLAYOBJECT_IMPL_H +#define __XINEPLAYOBJECT_IMPL_H + +#include <string> +#include <pthread.h> +#include <stdsynthmodule.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> +#include <xine.h> + +#include "audio_fifo_out.h" +#include "xinePlayObject.h" + + +using namespace std; +using Arts::poState; +using Arts::poTime; +using Arts::poCapabilities; + +class xinePlayObject_impl : virtual public xinePlayObject_skel, public Arts::StdSynthModule +{ +public: + xinePlayObject_impl(bool audioOnly=false); + virtual ~xinePlayObject_impl(); + + bool loadMedia( const string &url ); + string description(); + poTime currentTime(); + poTime overallTime(); + poCapabilities capabilities(); + string mediaName(); + poState state(); + void play(); + void halt(); + void seek( const class poTime &t ); + void pause(); + void calculateBlock( unsigned long samples ); + +protected: + void xineEvent( const xine_event_t &event ); + void clearWindow(); + void frameOutput( int &x, int &y, + int &width, int &height, double &ratio, + int displayWidth, int displayHeight, + double displayPixelAspect, bool dscb ); + void resizeNotify(); + void eventLoop(); + + // C -> C++ wrapper for pthread API + static inline void *pthread_start_routine( void *obj ) + { + ((xinePlayObject_impl *)obj)->eventLoop(); + pthread_exit( 0 ); + } + + // C -> C++ wrapper for xine API + static inline void xine_handle_event( void *obj, const xine_event_t *event ) + { + ((xinePlayObject_impl *)obj)->xineEvent( *event ); + } + + // C -> C++ wrapper for xine API + static inline void frame_output_cb( void *obj, + int video_width, int video_height, + double video_pixel_aspect, + int *dest_x, int *dest_y, + int *dest_width, int *dest_height, + double *dest_pixel_aspect, + int *win_x, int *win_y ) + { + ((xinePlayObject_impl *)obj)->frameOutput( *win_x, *win_y, + *dest_width, *dest_height, + *dest_pixel_aspect, + video_width, video_height, + video_pixel_aspect, false ); + + *dest_x = 0; + *dest_y = 0; + } + + // C -> C++ wrapper for xine API + static inline void dest_size_cb( void *obj, + int video_width, int video_height, + double video_pixel_aspect, + int *dest_width, int *dest_height, + double *dest_pixel_aspect ) + { + int win_x, win_y; + + ((xinePlayObject_impl *)obj)->frameOutput( win_x, win_y, + *dest_width, *dest_height, + *dest_pixel_aspect, + video_width, video_height, + video_pixel_aspect, true ); + } + +private: + double flpos; + string mrl; + +protected: + pthread_mutex_t mutex; + pthread_t thread; + + // xine data + xine_t *xine; + xine_stream_t *stream; + xine_event_queue_t *queue; + xine_audio_port_t *ao_port; + xine_video_port_t *vo_port; + void *ao_driver; + x11_visual_t visual; + xine_arts_audio audio; + + Display *display; + Window xcomWindow; + Atom xcomAtomQuit; + Atom xcomAtomResize; + int screen; + int width; + int height; + int dscbTimeOut; + int shmCompletionType; + +private: + int streamLength; + int streamPosition; + bool audioOnly; +}; + +class xineAudioPlayObject_impl : virtual public xineAudioPlayObject_skel, public xinePlayObject_impl +{ +public: + xineAudioPlayObject_impl() : xinePlayObject_impl(true) {}; +}; + +class xineVideoPlayObject_impl : virtual public xineVideoPlayObject_skel, public xinePlayObject_impl +{ +public: + xineVideoPlayObject_impl() : xinePlayObject_impl(false) {}; + long x11Snapshot(); + long x11WindowId(); + void x11WindowId( long window ); +}; + + +#endif diff --git a/xine_artsplugin/xineVideoPlayObject.mcopclass b/xine_artsplugin/xineVideoPlayObject.mcopclass new file mode 100644 index 00000000..5863fcf1 --- /dev/null +++ b/xine_artsplugin/xineVideoPlayObject.mcopclass @@ -0,0 +1,7 @@ +Interface=xinePlayObject,Arts::PlayObject,Arts::VideoPlayObject,Arts::SynthModule +Author=Ewald Snel <ewald@rambo.its.tudelft.nl> +Extension=vob,mpg,mpeg,m1v,m2v,m4v,m1s,m2s,m2p,avi,asf,asx,wmv,qt,mov,moov,mp4,rv,ram,rm,smi,ogm +MimeType=video/x-mpg,video/x-dat,video/x-mpeg,video/mpeg,video/x-msvideo,video/x-ms-asf,video/x-ms-wmv,video/quicktime,video/x-theora,video/mp4,video/x-ogm,application/vnd.ms-asf,application/vnd.rn-realmedia,video/vnd.rn-realvideo,application/ogg +Language=C++ +Library=libarts_xine.la +Preference=4 |