diff options
author | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-24 20:53:00 +0000 |
---|---|---|
committer | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-24 20:53:00 +0000 |
commit | b45eeec7a33e6b7475ebf02e3635e7d37ed33544 (patch) | |
tree | 5dbd28fee8ed5052b96bf9ebaa02cc8c8aa1320b | |
parent | 8f4426252dba8db47beaea9b9956c03668c1a640 (diff) | |
download | xine-lib-b45eeec7a33e6b7475ebf02e3635e7d37ed33544.tar.gz xine-lib-b45eeec7a33e6b7475ebf02e3635e7d37ed33544.tar.bz2 |
Reunification of xine-lib and xine-output. Some code cleanups.
CVS patchset: 21
CVS date: 2001/04/24 20:53:00
28 files changed, 6283 insertions, 40 deletions
diff --git a/acconfig.h b/acconfig.h index 37dcbb455..aaf2d438b 100644 --- a/acconfig.h +++ b/acconfig.h @@ -27,3 +27,18 @@ /* Define this to skins directory location */ #undef XINE_SKINDIR + +/* Define this if you have X11R6 installed */ +#undef HAVE_X11 + +/* Define this if you have libXv installed */ +#undef HAVE_XV + +/* Define this if you have libXinerama installed */ +#undef HAVE_XINERAMA + +/* Define this if you have Alsa (libasound) installed */ +#undef HAVE_ALSA + +/* Define this if you have ESD (libesd) installed */ +#undef HAVE_ESD diff --git a/configure.in b/configure.in index fcc58e22d..a897a82bc 100644 --- a/configure.in +++ b/configure.in @@ -140,6 +140,58 @@ fi dnl +dnl Checks for X11 +dnl +AC_PATH_XTRA +if test x"$no_x" != "xyes"; then + AC_DEFINE(HAVE_X11) +fi +AM_CONDITIONAL(HAVE_X11, test x"$no_x" != "xyes") + + +dnl +dnl Checks for Xv extension +dnl + +AC_CHECK_LIB(Xv, XvShmCreateImage, + X_LIBS="$X_LIBS -lXv" + AC_DEFINE(HAVE_XV) + ac_have_xv="yes",, + $X_LIBS $X_PRE_LIBS -lXext $X_EXTRA_LIBS) +AM_CONDITIONAL(HAVE_XV, test x$ac_have_xv = "xyes") + + +dnl +dnl Checks for Xinerama extension +dnl + +AC_CHECK_LIB(Xinerama, XineramaQueryExtension, + X_LIBS="$X_LIBS -lXinerama" + AC_DEFINE(HAVE_XINERAMA) + ac_have_xinerama="yes",, + $X_LIBS $X_PRE_LIBS -lXext $X_EXTRA_LIBS) +AM_CONDITIONAL(HAVE_XINERAMA, test x$ac_have_xinerama = "xyes") + + +dnl +dnl Alsa support +dnl +AM_PATH_ALSA(0.5.5, + AC_DEFINE(HAVE_ALSA), + AC_MSG_RESULT(*** All of ALSA dependent parts will be disabled ***)) +AM_CONDITIONAL(HAVE_ALSA, test x"$no_alsa" != "xyes") + + +dnl +dnl ESD support +dnl +AM_PATH_ESD(0.2.8, + AC_DEFINE(HAVE_ESD), + AC_MSG_RESULT(*** All of ESD dependent parts will be disabled ***)) +AM_CONDITIONAL(HAVE_ESD, test x"$no_esd" != "xyes") + + +dnl dnl dnl AC_CHECK_FUNC(getpwuid_r,AC_DEFINE(HAVE_GETPWUID_R)) @@ -314,7 +366,7 @@ AC_SUBST(w32_path) dnl dnl Some include paths ( !!! DO NOT REMOVE !!! ) dnl -INCLUDES='-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/xine-engine -I$(top_builddir)/src/xine-engine -I$(top_srcdir)/include -I$(top_builddir/)/include' +INCLUDES='-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/xine-engine -I$(top_builddir)/src/xine-engine' AC_SUBST(INCLUDES) @@ -341,6 +393,8 @@ Makefile include/Makefile include/xine.h.tmpl src/Makefile +src/audio_out/Makefile +src/video_out/Makefile src/demuxers/Makefile src/libmpeg2/Makefile src/libac3/Makefile diff --git a/cvscompile.sh b/cvscompile.sh index 3ac246e01..d671af41c 100755 --- a/cvscompile.sh +++ b/cvscompile.sh @@ -1,7 +1,7 @@ #!/bin/sh # Run this to generate all the initial Makefiles, etc. -m4_files="_xine.m4" +m4_files="_xine.m4 alsa.m4 esd.m4" if test -d m4; then rm -f acinclude.m4 for m4f in $m4_files; do diff --git a/include/Makefile.am b/include/Makefile.am index 9f16758c9..78923b1e5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,8 +4,8 @@ EXTRA_DIST = xine.h.tmpl.in -include_HEADERS = audio_out.h video_out.h xine.h - +includedir = $(prefix)/include +include_HEADERS = xine.h xine.h: xine.h.tmpl @echo "creating xine.h"; \ @@ -17,35 +17,6 @@ xine.h: xine.h.tmpl debug: -install-includeHEADERS: $(include_HEADERS) - @$(NORMAL_INSTALL) - $(mkinstalldirs) $(DESTDIR)$(includedir)/xine - @list='$(include_HEADERS)'; for p in $$list; do \ - if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \ - if test "$$p" != "xine.h"; then \ - echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p"; \ - $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p; \ - else \ - echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/$$p"; \ - $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/$$p; \ - fi; \ - done - - -## -## Remove them -## -uninstall-includeHEADERS: - @$(NORMAL_UNINSTALL) - list='$(include_HEADERS)'; for p in $$list; do \ - if test "$$p" != "xine.h"; then \ - rm -f $(DESTDIR)$(includedir)/xine/$$p; \ - else \ - rm -f $(DESTDIR)$(includedir)/$$p; \ - fi; \ - done - - mostlyclean-generic: -rm -f *~ \#* .*~ .\#* diff --git a/m4/Makefile.am b/m4/Makefile.am index a06ac6530..ecad1ffc2 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -4,7 +4,7 @@ m4datadir = @ACLOCAL_DIR@ m4data_DATA = xine.m4 -EXTRA_DIST = _xine.m4 $(m4data_DATA) +EXTRA_DIST = _xine.m4 alsa.m4 esd.m4 $(m4data_DATA) debug: diff --git a/m4/alsa.m4 b/m4/alsa.m4 new file mode 100644 index 000000000..1bf79078b --- /dev/null +++ b/m4/alsa.m4 @@ -0,0 +1,175 @@ +dnl Configure paths/version for ALSA +dnl +dnl Copyright (C) 2000 Daniel Caujolle-Bert <lobadia@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 +dnl USAGE: +dnl AM_PATH_ALSA([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for ALSA, then +dnl AC_SUBST() for ALSA_CFLAGS, ALSA_LIBS and ALSA_STATIC_LIB, +dnl AC_DEFINE() HAVE_GL, +dnl $no_alsa is set to "yes" if alsa isn't found. +dnl +AC_DEFUN(AM_PATH_ALSA, + [ + AC_ARG_ENABLE(alsa, [ --disable-alsa Do not build ALSA support],,) + AC_ARG_WITH(alsa-prefix,[ --with-alsa-prefix=pfx Prefix where alsa is installed (optional)], + alsa_prefix="$withval", alsa_prefix="") + AC_ARG_WITH(alsa-exec-prefix,[ --with-alsa-exec-prefix=pfx Exec prefix where alsa is installed (optional)], + alsa_exec_prefix="$withval", alsa_exec_prefix="") + AC_ARG_ENABLE(alsatest, [ --disable-alsatest Do not try to compile and run a test alsa program],, enable_alsatest=yes) + + no_alsa="yes" + +if test x"$enable_alsa" != "xno"; then + + if test x$alsa_prefix != x ; then + ALSA_LIBS="-L$alsa_prefix/lib" + ALSA_STATIC_LIB="$alsa_prefix" + ALSA_CFLAGS="-I$alsa_prefix/include" + fi + if test x$alsa_exec_prefix != x ; then + ALSA_LIBS="-L$alsa_exec_prefix/lib" + ALSA_STATIC_LIB="$alsa_exec_prefix" + ALSA_CFLAGS="-I$alsa_exec_prefix/include" + fi + + ALSA_LIBS="-lasound $ALSA_LIBS" + if test x$ALSA_STATIC_LIB != x; then + ALSA_STATIC_LIB="$ALSA_STATIC_LIB/lib/libasound.a" + else + ALSA_STATIC_LIB="/usr/lib/libasound.a" + fi + ALSA_CFLAGS="$ALSA_CFLAGS" + + min_alsa_version=ifelse([$1], ,0.1.1,$1) + AC_MSG_CHECKING(for ALSA version >= $min_alsa_version) + if test "x$enable_alsatest" = "xyes" ; then + no_alsa="" + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $ALSA_CFLAGS" + LIBS="$ALSA_LIBS $LIBS" +dnl +dnl Now check if the installed ALSA is sufficiently new. +dnl + AC_LANG_SAVE() + AC_LANG_C() + rm -f conf.alsatest + AC_TRY_RUN([ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/asoundlib.h> + +int main() { + int major, minor, micro; + char *tmp_version; + + system("touch conf.alsatest"); + + tmp_version = strdup("$min_alsa_version"); + if(sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_alsa_version"); + exit(1); + } + + #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) + #define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR + #endif + #if !defined(SND_LIB_MINOR) && defined(SOUNDLIB_VERSION_MINOR) + #define SND_LIB_MINOR SOUNDLIB_VERSION_MINOR + #endif + #if !defined(SND_LIB_SUBMINOR) && defined(SOUNDLIB_VERSION_SUBMINOR) + #define SND_LIB_SUBMINOR SOUNDLIB_VERSION_SUBMINOR + #endif + + if((SND_LIB_MAJOR > major) || + ((SND_LIB_MAJOR == major) && (SND_LIB_MINOR > minor)) || + ((SND_LIB_MAJOR == major) && (SND_LIB_MINOR == minor) && (SND_LIB_SUBMINOR >= micro))) { + return 0; + } + else { + printf("\n*** An old version of ALSA (%d.%d.%d) was found.\n", + SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR); + printf("*** You need a version of ALSA newer than %d.%d.%d. The latest version of\n", major, minor, micro); + printf("*** ALSA is always available from: http://www.alsa-drivers.org/\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version\n"); + printf("*** the easiest way to fix this is to remove the old version, and\n"); + printf("*** install a new one.\n"); + } + return 1; +} +],, no_alsa=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_alsa" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test -f conf.alsatest ; then + : + else + echo "*** Could not run ALSA test program, checking why..." + CFLAGS="$CFLAGS $ALSA_CFLAGS" + LIBS="$LIBS $ALSA_LIBS" + AC_TRY_LINK([ +#include <sys/asoudlib.h> +#include <stdio.h> +], + [return ((SND_LIB_MAJOR) || (SND_LIB_MINOR) || (SND_LIB_SUBMINOR));], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding ALSA or finding the wrong" + echo "*** version of ALSA. If it is not finding ALSA, 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 ALSA was incorrectly installed."]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + ALSA_CFLAGS="" + ALSA_STATIC_LIB="" + ALSA_LIBS="" + ifelse([$3], , :, [$3]) + fi + +fi + + AC_SUBST(ALSA_CFLAGS) + AC_SUBST(ALSA_STATIC_LIB) + AC_SUBST(ALSA_LIBS) + AC_LANG_RESTORE() + rm -f conf.alsatest +]) + diff --git a/m4/esd.m4 b/m4/esd.m4 new file mode 100644 index 000000000..39b378988 --- /dev/null +++ b/m4/esd.m4 @@ -0,0 +1,164 @@ +# Configure paths for ESD +# Manish Singh 98-9-30 +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_ESD([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for ESD, and define ESD_CFLAGS and ESD_LIBS +dnl +AC_DEFUN(AM_PATH_ESD, +[dnl +dnl Get the cflags and libraries from the esd-config script +dnl +AC_ARG_WITH(esd-prefix,[ --with-esd-prefix=PFX Prefix where ESD is installed (optional)], + esd_prefix="$withval", esd_prefix="") +AC_ARG_WITH(esd-exec-prefix,[ --with-esd-exec-prefix=PFX Exec prefix where ESD is installed (optional)], + esd_exec_prefix="$withval", esd_exec_prefix="") +AC_ARG_ENABLE(esdtest, [ --disable-esdtest Do not try to compile and run a test ESD program], + , enable_esdtest=yes) + + if test x$esd_exec_prefix != x ; then + esd_args="$esd_args --exec-prefix=$esd_exec_prefix" + if test x${ESD_CONFIG+set} != xset ; then + ESD_CONFIG=$esd_exec_prefix/bin/esd-config + fi + fi + if test x$esd_prefix != x ; then + esd_args="$esd_args --prefix=$esd_prefix" + if test x${ESD_CONFIG+set} != xset ; then + ESD_CONFIG=$esd_prefix/bin/esd-config + fi + fi + + AC_PATH_PROG(ESD_CONFIG, esd-config, no) + min_esd_version=ifelse([$1], ,0.2.7,$1) + AC_MSG_CHECKING(for ESD - version >= $min_esd_version) + no_esd="" + if test "$ESD_CONFIG" = "no" ; then + no_esd=yes + else + ESD_CFLAGS=`$ESD_CONFIG $esdconf_args --cflags` + ESD_LIBS=`$ESD_CONFIG $esdconf_args --libs` + + esd_major_version=`$ESD_CONFIG $esd_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + esd_minor_version=`$ESD_CONFIG $esd_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + esd_micro_version=`$ESD_CONFIG $esd_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_esdtest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $ESD_CFLAGS" + LIBS="$LIBS $ESD_LIBS" +dnl +dnl Now check if the installed ESD is sufficiently new. (Also sanity +dnl checks the results of esd-config to some extent +dnl + rm -f conf.esdtest + AC_TRY_RUN([ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <esd.h> + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.esdtest"); + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_esd_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_esd_version"); + exit(1); + } + + if (($esd_major_version > major) || + (($esd_major_version == major) && ($esd_minor_version > minor)) || + (($esd_major_version == major) && ($esd_minor_version == minor) && ($esd_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'esd-config --version' returned %d.%d.%d, but the minimum version\n", $esd_major_version, $esd_minor_version, $esd_micro_version); + printf("*** of ESD required is %d.%d.%d. If esd-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If esd-config was wrong, set the environment variable ESD_CONFIG\n"); + printf("*** to point to the correct copy of esd-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_esd=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_esd" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$ESD_CONFIG" = "no" ; then + echo "*** The esd-config script installed by ESD could not be found" + echo "*** If ESD was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the ESD_CONFIG environment variable to the" + echo "*** full path to esd-config." + else + if test -f conf.esdtest ; then + : + else + echo "*** Could not run ESD test program, checking why..." + CFLAGS="$CFLAGS $ESD_CFLAGS" + LIBS="$LIBS $ESD_LIBS" + AC_TRY_LINK([ +#include <stdio.h> +#include <esd.h> +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding ESD or finding the wrong" + echo "*** version of ESD. If it is not finding ESD, 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 "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means ESD was incorrectly installed" + echo "*** or that you have moved ESD since it was installed. In the latter case, you" + echo "*** may want to edit the esd-config script: $ESD_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + ESD_CFLAGS="" + ESD_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(ESD_CFLAGS) + AC_SUBST(ESD_LIBS) + rm -f conf.esdtest +]) diff --git a/src/Makefile.am b/src/Makefile.am index 822ab3e18..f4864d330 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = input libmpeg2 libspudec demuxers \ +SUBDIRS = audio_out video_out input libmpeg2 libspudec demuxers \ libac3 libmpg123 libw32dll xine-engine debug: diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am new file mode 100644 index 000000000..220f5991d --- /dev/null +++ b/src/audio_out/Makefile.am @@ -0,0 +1,45 @@ +CFLAGS = @GLOBAL_CFLAGS@ -DXINE_COMPILE + +EXTRA_DIST = audio_alsa_out.c audio_esd_out.c + +if HAVE_ALSA +alsa_module = xineplug_ao_out_alsa.la +endif + +if HAVE_ESD +esd_module = xineplug_ao_out_esd.la +endif + +## +# IMPORTANT: +# --------- +# All of xine audio out plugins should be named like the +# scheme "xineplug_ao_out_" +# +##lib_LTLIBRARIES = xineplug_ao_out_oss.la $(alsa_module) $(esd_module) +lib_LTLIBRARIES = + +##xineplug_ao_out_oss_la_SOURCES = audio_oss_out.c resample.c +##xineplug_ao_out_oss_la_LDFLAGS = -avoid-version -module + +##xineplug_ao_out_alsa_la_SOURCES = audio_alsa_out.c resample.c +##xineplug_ao_out_alsa_la_LDFLAGS = -avoid-version -module + +##xineplug_ao_out_esd_la_SOURCES = audio_esd_out.c resample.c +##xineplug_ao_out_esd_la_LDFLAGS = -avoid-version -module + +noinst_HEADERS = audio_oss_out.h audio_alsa_out.h audio_esd_out.h resample.h + + +debug: + $(MAKE) CFLAGS="$(DEBUG_CFLAGS)" + + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c new file mode 100644 index 000000000..95405511f --- /dev/null +++ b/src/audio_out/audio_alsa_out.c @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: audio_alsa_out.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <errno.h> + +#include <sys/asoundlib.h> +//#include <linux/asound.h> +//#include <linux/asequencer.h> +//#include <linux/asoundid.h> + +#include <inttypes.h> +#include "xine/xine.h" +#include "xine/monitor.h" +#include "xine/audio_out.h" +#include "xine/metronom.h" +#include "resample.h" +#include "xine/ac3.h" +#include "xine/utils.h" + +#define AUDIO_NUM_FRAGMENTS 15 +#define AUDIO_FRAGMENT_SIZE 8192 + +#define GAP_TOLERANCE 15000 +#define MAX_MASTER_CLOCK_DIV 5000 + +extern uint32_t xine_debug; + + +typedef struct _audio_alsa_globals { + + snd_pcm_t *front_handle; + + int32_t output_sample_rate, input_sample_rate; + uint32_t num_channels; + + uint32_t bytes_in_buffer; /* number of bytes written to audio hardware */ + uint32_t last_vpts; /* vpts at which last written package ends */ + + uint32_t sync_vpts; /* this syncpoint is used as a starting point */ + uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */ + + int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */ + int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */ + + int16_t *zero_space; + + int audio_started; + int pcm_default_card; + int pcm_default_device; + + int direction; + int mode; + int start_mode; + int stop_mode; + int format; + int rate; + int voices; + int interleave; + int frag_size; + int frag_count; + int pcm_len; + +} audio_alsa_globals_t; + +static audio_alsa_globals_t gAudioALSA; + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static void alsa_set_frag(int fragment_size, int fragment_count) { + snd_pcm_channel_params_t params; + snd_pcm_channel_setup_t setup; + snd_pcm_format_t format; + int err; + + memset(¶ms, 0, sizeof(params)); + + params.mode = gAudioALSA.mode; + params.channel = gAudioALSA.direction; + params.start_mode = gAudioALSA.start_mode; + params.stop_mode = gAudioALSA.stop_mode; + params.buf.block.frag_size = fragment_size; + params.buf.block.frags_max = fragment_count; + params.buf.block.frags_min = 1; + + memset(&format, 0, sizeof(format)); + format.format = gAudioALSA.format; + format.rate = gAudioALSA.rate; + format.voices = gAudioALSA.voices; + format.interleave = gAudioALSA.interleave; + memcpy(¶ms.format, &format, sizeof(format)); + + snd_pcm_playback_flush(gAudioALSA.front_handle); + + if((err = snd_pcm_channel_params(gAudioALSA.front_handle, ¶ms)) < 0) { + perr("snd_pcm_channel_params() failed: %s\n", snd_strerror(err)); + return; + } + if((err = snd_pcm_playback_prepare(gAudioALSA.front_handle)) < 0) { + perr("snd_pcm_channel_prepare() failed: %s\n", snd_strerror(err)); + return; + } + + memset(&setup, 0, sizeof(setup)); + setup.mode = gAudioALSA.mode; + setup.channel = gAudioALSA.direction; + if((err = snd_pcm_channel_setup(gAudioALSA.front_handle, &setup)) < 0) { + perr("snd_pcm_channel_setup() failed: %s\n", snd_strerror(err)); + return; + } + + gAudioALSA.frag_size = fragment_size; + gAudioALSA.frag_count = fragment_count; + + gAudioALSA.pcm_len = fragment_size * + (snd_pcm_format_width(gAudioALSA.format) / 8) * + gAudioALSA.voices; + + // perr("PCM len = %d\n", gAudioALSA.pcm_len); + if(gAudioALSA.zero_space) + free(gAudioALSA.zero_space); + + gAudioALSA.zero_space = (int16_t *) malloc(gAudioALSA.frag_size); + memset(gAudioALSA.zero_space, + (int16_t) snd_pcm_format_silence(gAudioALSA.format), + gAudioALSA.frag_size); +} +/* ------------------------------------------------------------------------- */ +/* + * open the audio device for writing to + */ +static int ao_open(uint32_t bits, uint32_t rate, int ao_mode) { + int channels; + int subdevice = 0; + int direction = SND_PCM_OPEN_PLAYBACK; + snd_pcm_format_t pcm_format; + snd_pcm_channel_setup_t pcm_chan_setup; + snd_pcm_channel_params_t pcm_chan_params; + snd_pcm_channel_info_t pcm_chan_info; + int err; + int mode; + + + switch (ao_mode) { + + case AO_MODE_STEREO: + case AO_MODE_AC3: + channels = 2; + break; + + case AO_MODE_MONO: + channels = 1; + break; + + default: + return 0; + break; + } + + xprintf (VERBOSE|AUDIO, "bits = %d, rate = %d, channels = %d\n", + bits, rate, channels); + +#warning "FIXME in libAC3" + if(!rate) + return 0; + + if(gAudioALSA.front_handle != NULL) { + + if(rate == gAudioALSA.input_sample_rate) + return 1; + + snd_pcm_close(gAudioALSA.front_handle); + } + + gAudioALSA.input_sample_rate = rate; + gAudioALSA.bytes_in_buffer = 0; + gAudioALSA.last_vpts = 0; + gAudioALSA.sync_vpts = 0; + gAudioALSA.sync_bytes_in_buffer = 0; + gAudioALSA.audio_started = 0; + gAudioALSA.direction = SND_PCM_CHANNEL_PLAYBACK; + + if (ao_mode == AO_MODE_AC3) { + gAudioALSA.pcm_default_device = 2; + mode = SND_PCM_MODE_BLOCK; + } + else { + mode = SND_PCM_MODE_BLOCK; + } + + gAudioALSA.mode = mode; + + if((err = snd_pcm_open_subdevice(&gAudioALSA.front_handle, + gAudioALSA.pcm_default_card, + gAudioALSA.pcm_default_device, + subdevice, direction +// | SND_PCM_OPEN_NONBLOCK)) < 0) { + )) < 0) { + perr("snd_pcm_open_subdevice() failed: %s\n", snd_strerror(err)); + return 0; + } + + memset(&pcm_chan_info, 0, sizeof(snd_pcm_channel_info_t)); + if((err = snd_pcm_channel_info(gAudioALSA.front_handle, + &pcm_chan_info)) < 0) { + perr("snd_pcm_channel_info() failed: %s\n", snd_strerror(err)); + return 0; + } + + memset(&pcm_chan_params, 0, sizeof(snd_pcm_channel_params_t)); + memset(&pcm_format, 0, sizeof(snd_pcm_format_t)); + /* set sample size */ + switch(bits) { + case 8: + pcm_format.format = SND_PCM_SFMT_S8; + break; + + case 16: + pcm_format.format = SND_PCM_SFMT_S16; + break; + + case 24: + pcm_format.format = SND_PCM_SFMT_S24; + break; + + case 32: + pcm_format.format = SND_PCM_SFMT_S32; + break; + + default: + perr("sample format %d unsupported\n", bits); + break; + } + gAudioALSA.format = pcm_format.format; + + xprintf (VERBOSE|AUDIO, "format name = '%s'\n", + snd_pcm_get_format_name(pcm_format.format)); + + + pcm_format.voices = gAudioALSA.voices = channels; + pcm_format.rate = gAudioALSA.rate = rate; + pcm_format.interleave = gAudioALSA.interleave = 1; + + gAudioALSA.num_channels = channels; + + xprintf (VERBOSE|AUDIO, "audio channels = %d ao_mode = %d\n", + gAudioALSA.num_channels,ao_mode); + + if(rate > pcm_chan_info.max_rate) + gAudioALSA.output_sample_rate = pcm_chan_info.max_rate; + else + gAudioALSA.output_sample_rate = gAudioALSA.input_sample_rate; + + gAudioALSA.audio_step = (uint32_t) 90000 + * (uint32_t) 32768 / gAudioALSA.input_sample_rate; + + gAudioALSA.bytes_per_kpts = gAudioALSA.output_sample_rate + * gAudioALSA.num_channels * 2 * 1024 / 90000; + + xprintf (VERBOSE|AUDIO, "%d input samples/sec %d output samples/sec\n", + rate, gAudioALSA.output_sample_rate); + xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n", + gAudioALSA.audio_step); + + metronom_set_audio_rate (gAudioALSA.audio_step); + + memcpy(&pcm_chan_params.format, &pcm_format, sizeof(snd_pcm_format_t)); + + pcm_chan_params.mode = mode; + pcm_chan_params.channel = gAudioALSA.direction; + + pcm_chan_params.start_mode = SND_PCM_START_FULL; + //pcm_chan_params.start_mode = SND_PCM_START_DATA; + //pcm_chan_params.stop_mode = SND_PCM_STOP_STOP; + pcm_chan_params.stop_mode = SND_PCM_STOP_ROLLOVER; + + gAudioALSA.start_mode = pcm_chan_params.start_mode; + gAudioALSA.stop_mode = pcm_chan_params.stop_mode; + + if (ao_mode == AO_MODE_AC3) { + pcm_chan_params.digital.dig_valid = 1; + pcm_chan_params.digital.dig_status[0] = SND_PCM_DIG0_NONAUDIO; + pcm_chan_params.digital.dig_status[0] |= SND_PCM_DIG0_PROFESSIONAL; + pcm_chan_params.digital.dig_status[0] |= SND_PCM_DIG0_PRO_FS_48000; + pcm_chan_params.digital.dig_status[3] = SND_PCM_DIG3_CON_FS_48000; + } + + snd_pcm_playback_flush(gAudioALSA.front_handle); + if((err = snd_pcm_channel_params(gAudioALSA.front_handle, + &pcm_chan_params)) < 0) { + perr("snd_pcm_channel_params() failed: %s\n", snd_strerror(err)); + return 0; + } + if((err = snd_pcm_playback_prepare(gAudioALSA.front_handle)) < 0) { + perr("snd_pcm_channel_prepare() failed: %s\n", snd_strerror(err)); + return 0; + } + + pcm_chan_setup.mode = mode; + pcm_chan_setup.channel = gAudioALSA.direction; + + if((err = snd_pcm_channel_setup(gAudioALSA.front_handle, + &pcm_chan_setup)) < 0) { + perr("snd_pcm_channel_setup() failed: %s\n", snd_strerror(err)); + return 0; + } + + printf ("actual rate: %d\n", pcm_chan_setup.format.rate); + + alsa_set_frag(1536, 6); + + gAudioALSA.bytes_in_buffer = 0; + + return 1; +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static void ao_fill_gap (uint32_t pts_len) { + int num_bytes = pts_len * gAudioALSA.bytes_per_kpts / 1024; + + num_bytes = (num_bytes / 4) * 4; + + gAudioALSA.bytes_in_buffer += num_bytes; + + printf ("audio_alsa_out: inserting %d 0-bytes to fill a gap of %d pts\n", + num_bytes, pts_len); + + while (num_bytes>0) { + if (num_bytes>gAudioALSA.frag_size) { + snd_pcm_write(gAudioALSA.front_handle, gAudioALSA.zero_space, + gAudioALSA.frag_size); + num_bytes -= gAudioALSA.frag_size; + } else { + int old_frag_size = gAudioALSA.frag_size; + + alsa_set_frag(num_bytes, 6); + + snd_pcm_write(gAudioALSA.front_handle, gAudioALSA.zero_space, num_bytes); + + alsa_set_frag(old_frag_size, 6); + + num_bytes = 0; + } + } + + gAudioALSA.last_vpts += pts_len ; +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static uint32_t ao_get_current_vpts (void) { + int pos; + snd_pcm_channel_status_t pcm_stat; + int err; + int32_t diff; + uint32_t vpts; + + + if (gAudioALSA.audio_started) { + memset(&pcm_stat, 0, sizeof(snd_pcm_channel_status_t)); + pcm_stat.channel = SND_PCM_CHANNEL_PLAYBACK; + if((err = snd_pcm_channel_status(gAudioALSA.front_handle, + &pcm_stat)) < 0) { + //Hide error report + perr("snd_pcm_channel_status() failed: %s\n", snd_strerror(err)); + return 0; + } + pos = pcm_stat.scount; + } + else { + pos = 0; + } + + diff = gAudioALSA.sync_bytes_in_buffer - pos; + + vpts = gAudioALSA.sync_vpts - diff * 1024 / gAudioALSA.bytes_per_kpts; + + xprintf (AUDIO|VERBOSE,"audio_alsa_out: get_current_vpts pos=%d diff=%d " + "vpts=%d sync_vpts=%d sync_bytes_in_buffer %d\n", pos, diff, + vpts, gAudioALSA.sync_vpts,gAudioALSA.sync_bytes_in_buffer); + + return vpts; +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static void ao_put_samples(int16_t* output_samples, + uint32_t num_samples, uint32_t pts_) { + uint32_t vpts; + uint32_t audio_vpts; + uint32_t master_vpts; + int32_t diff, gap; + int bDropPackage = 0; + snd_pcm_channel_status_t status_front; + int err; + uint16_t sample_buffer[gAudioALSA.frag_size]; + + + xprintf(VERBOSE|AUDIO, "Audio : play %d samples at pts %d pos %d \n", + num_samples, pts_, gAudioALSA.bytes_in_buffer); + + if (gAudioALSA.front_handle == NULL) + return; + + // if(gAudioALSA.frag_size != num_samples) { + // alsa_set_frag(num_samples, 6); + // } + + vpts = metronom_got_audio_samples (pts_, num_samples); + + /* + * check if these samples "fit" in the audio output buffer + * or do we have an audio "gap" here? + */ + + gap = vpts - gAudioALSA.last_vpts; + + xprintf (VERBOSE|AUDIO, "audio_alsa_out: got %d samples, vpts=%d, " + "last_vpts=%d\n", num_samples, vpts, gAudioALSA.last_vpts); + + if (gap > GAP_TOLERANCE) { + // ao_fill_gap (gap); + } + else if (gap < -GAP_TOLERANCE) { + bDropPackage = 1; + } + + /* + * sync on master clock + */ + + audio_vpts = ao_get_current_vpts () ; + master_vpts = metronom_get_current_time (); + diff = audio_vpts - master_vpts; + + xprintf (VERBOSE|AUDIO,"audio_alsa_out: syncing on master clock: " + "audio_vpts=%d master_vpts=%d\n", audio_vpts, master_vpts); + + /* + * method 1 : resampling + */ + + /* + */ + + /* + * method 2: adjust master clock + */ + + + if (abs(diff) > MAX_MASTER_CLOCK_DIV) { + printf ("master clock adjust time %d -> %d\n", master_vpts, audio_vpts); + metronom_adjust_clock (audio_vpts); + } + + /* + * resample and output samples + */ + if (!bDropPackage) { + int num_output_samples = + num_samples + * gAudioALSA.output_sample_rate + / gAudioALSA.input_sample_rate; + + if(num_output_samples != gAudioALSA.frag_size) + alsa_set_frag(num_output_samples, 6); + + if (num_output_samples != num_samples ) { + audio_out_resample_stereo (output_samples, num_samples, + sample_buffer, num_output_samples); + snd_pcm_write(gAudioALSA.front_handle, (void*)sample_buffer, + num_output_samples * 2 * gAudioALSA.num_channels); + } + else { + snd_pcm_write(gAudioALSA.front_handle, (void*)output_samples, + num_samples * 2 * gAudioALSA.num_channels); + } + + memset(&status_front, 0, sizeof(snd_pcm_channel_status_t)); + if((err = snd_pcm_channel_status(gAudioALSA.front_handle, + &status_front)) < 0) { + perr("snd_pcm_channel_status() failed: %s\n", snd_strerror(err)); + } + + /* Hummm, this seems made mistakes (flushing isnt good here). */ + /* + if(status_front.underrun) { + perr("underrun, resetting front channel\n"); + snd_pcm_channel_flush(gAudioALSA.front_handle, channel); + snd_pcm_playback_prepare(gAudioALSA.front_handle); + snd_pcm_write(gAudioALSA.front_handle, output_samples, num_samples<<1); + if((err = snd_pcm_channel_status(gAudioALSA.front_handle, + &status_front)) < 0) { + perr("snd_pcm_channel_status() failed: %s", snd_strerror(err)); + } + if(status_front.underrun) { + perr("front write error, giving up\n"); + } + } + */ + + /* + * remember vpts + */ + + gAudioALSA.sync_vpts = vpts; + gAudioALSA.sync_bytes_in_buffer = gAudioALSA.bytes_in_buffer; + + /* + * step values + */ + gAudioALSA.bytes_in_buffer += + num_output_samples * 2 * gAudioALSA.num_channels; + + gAudioALSA.audio_started = 1; + } + else { + printf ("audio_alsa_out: audio package (vpts = %d) dropped\n", vpts); + gAudioALSA.sync_vpts = vpts; + } + + gAudioALSA.last_vpts = + vpts + num_samples * 90000 / gAudioALSA.input_sample_rate ; + +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static void ao_close(void) { + int err; + + if(gAudioALSA.front_handle) { + if((err = snd_pcm_playback_flush(gAudioALSA.front_handle)) < 0) { + perr("snd_pcm_channel_flush() failed: %s\n", snd_strerror(err)); + } + + if((err = snd_pcm_close(gAudioALSA.front_handle)) < 0) { + perr("snd_pcm_close() failed: %s\n", snd_strerror(err)); + } + + gAudioALSA.front_handle = NULL; + } +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static int ao_is_mode_supported (int mode) { + + switch (mode) { + + case AO_MODE_STEREO: + case AO_MODE_AC3: + /*case AO_MODE_MONO: FIXME */ + return 1; + + } + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* + * + */ +static ao_functions_t audio_alsaout = { + ao_is_mode_supported, + ao_open, + ao_put_samples, + ao_close +}; +/* ------------------------------------------------------------------------- */ +/* + * + */ +static void sighandler(int signum) { +} +/* ------------------------------------------------------------------------- */ +/* + * + */ +ao_functions_t *audio_alsaout_init(void) { + int best_rate; + int devnum; + int err; + int direction = SND_PCM_OPEN_PLAYBACK; + int snd_default_card; + int snd_default_mixer_card; + int snd_default_mixer_device; + snd_pcm_info_t pcm_info; + snd_pcm_channel_info_t pcm_chan_info; + struct sigaction action; + + /* Check if, at least, one card is installed */ + if((devnum = snd_cards()) == 0) { + return NULL; + } + else { + snd_default_card = snd_defaults_card(); + if((err = snd_card_load(snd_default_card)) < 0) { + perr("snd_card_load() failed: %s\n", snd_strerror(err)); + } + xprintf (VERBOSE|AUDIO, "%d card(s) installed. Default = %d\n", + devnum, snd_default_card); + + if((snd_default_mixer_card = snd_defaults_mixer_card()) < 0) { + perr("snd_defaults_mixer_card() failed: %s\n", + snd_strerror(snd_default_mixer_card)); + } + xprintf (VERBOSE|AUDIO, "default mixer card = %d\n", + snd_default_mixer_card); + + if((snd_default_mixer_device = snd_defaults_mixer_device()) < 0) { + perr("snd_defaults_mixer_device() failed: %s\n", + snd_strerror(snd_default_mixer_device)); + } + xprintf (VERBOSE|AUDIO, "default mixer device = %d\n", + snd_default_mixer_device); + } + + xprintf (VERBOSE|AUDIO, "Opening audio device..."); + + if((gAudioALSA.pcm_default_card = snd_defaults_pcm_card()) < 0) { + perr("There is no default pcm card.\n"); + exit(1); + } + xprintf (VERBOSE|AUDIO, "snd_defaults_pcm_card() return %d\n", + gAudioALSA.pcm_default_card); + + if((gAudioALSA.pcm_default_device = snd_defaults_pcm_device()) < 0) { + perr("There is no default pcm device.\n"); + exit(1); + } + xprintf (VERBOSE|AUDIO, "snd_defaults_pcm_device() return %d\n", + gAudioALSA.pcm_default_device); + + action.sa_handler = sighandler; + sigemptyset(&(action.sa_mask)); + action.sa_flags = 0; + if(sigaction(SIGALRM, &action, NULL) != 0) { + perr("sigaction(SIGALRM) failed: %s\n", strerror(errno)); + } + alarm(2); + + if((err = snd_pcm_open(&gAudioALSA.front_handle, gAudioALSA.pcm_default_card, + gAudioALSA.pcm_default_device, direction)) < 0) { + perr("snd_pcm_open() failed: %s\n", snd_strerror(err)); + perr(">>> Check if another program don't already use PCM <<<\n"); + return NULL; + } + + memset(&pcm_info, 0, sizeof(snd_pcm_info_t)); + if((err = snd_pcm_info(gAudioALSA.front_handle, &pcm_info)) < 0) { + perr("snd_pcm_info() failed: %s\n", snd_strerror(err)); + exit(1); + } + + xprintf (VERBOSE|AUDIO, "snd_pcm_info():\n"); + xprintf (VERBOSE|AUDIO, "---------------\n"); + xprintf (VERBOSE|AUDIO, "type = 0x%x\n", pcm_info.type); + xprintf (VERBOSE|AUDIO, "flags = 0x%x\n", pcm_info.flags); + xprintf (VERBOSE|AUDIO, "id = '%s'\n", pcm_info.id); + xprintf (VERBOSE|AUDIO, "name = '%s'\n", pcm_info.name); + xprintf (VERBOSE|AUDIO, "playback = %d\n", pcm_info.playback); + xprintf (VERBOSE|AUDIO, "capture = %d\n", pcm_info.capture); + + memset(&pcm_chan_info, 0, sizeof(snd_pcm_channel_info_t)); + pcm_chan_info.channel = SND_PCM_CHANNEL_PLAYBACK; + if((err = snd_pcm_channel_info(gAudioALSA.front_handle, + &pcm_chan_info)) < 0) { + perr("snd_pcm_channel_info() failed: %s\n", snd_strerror(err)); + exit(1); + } + + best_rate = pcm_chan_info.rates; + + xprintf (VERBOSE|AUDIO, "best_rate = %d\n", best_rate); + xprintf (VERBOSE|AUDIO, "snd_pcm_channel_info(PLAYBACK):\n"); + xprintf (VERBOSE|AUDIO, "-------------------------------\n"); + xprintf (VERBOSE|AUDIO, "subdevice = %d\n", + pcm_chan_info.subdevice); + xprintf (VERBOSE|AUDIO, "subname = %s\n", + pcm_chan_info.subname); + xprintf (VERBOSE|AUDIO, "channel = %d\n", + pcm_chan_info.channel); + xprintf (VERBOSE|AUDIO, "mode = %d\n", + pcm_chan_info.mode); + xprintf (VERBOSE|AUDIO, "flags = 0x%x\n", + pcm_chan_info.flags); + xprintf (VERBOSE|AUDIO, "formats = %d\n", + pcm_chan_info.formats); + xprintf (VERBOSE|AUDIO, "rates = %d\n", + pcm_chan_info.rates); + xprintf (VERBOSE|AUDIO, "min_rate = %d\n", + pcm_chan_info.min_rate); + xprintf (VERBOSE|AUDIO, "max_rate = %d\n", + pcm_chan_info.max_rate); + xprintf (VERBOSE|AUDIO, "min_voices = %d\n", + pcm_chan_info.min_voices); + xprintf (VERBOSE|AUDIO, "max_voices = %d\n", + pcm_chan_info.max_voices); + xprintf (VERBOSE|AUDIO, "buffer_size = %d\n", + pcm_chan_info.buffer_size); + xprintf (VERBOSE|AUDIO, "min_fragment_size = %d\n", + pcm_chan_info.min_fragment_size); + xprintf (VERBOSE|AUDIO, "max_fragment_size = %d\n", + pcm_chan_info.max_fragment_size); + xprintf (VERBOSE|AUDIO, "fragment_align = %d\n", + pcm_chan_info.fragment_align); + xprintf (VERBOSE|AUDIO, "fifo_size = %d\n", + pcm_chan_info.fifo_size); + xprintf (VERBOSE|AUDIO, "transfer_block_size = %d\n", + pcm_chan_info.transfer_block_size); + xprintf (VERBOSE|AUDIO, "mmap_size = %ld\n", + pcm_chan_info.mmap_size); + xprintf (VERBOSE|AUDIO, "mixer_device = %d\n", + pcm_chan_info.mixer_device); + + snd_pcm_close (gAudioALSA.front_handle); + gAudioALSA.front_handle = NULL; + + return &audio_alsaout; +} diff --git a/src/audio_out/audio_alsa_out.h b/src/audio_out/audio_alsa_out.h new file mode 100644 index 000000000..123282262 --- /dev/null +++ b/src/audio_out/audio_alsa_out.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: audio_alsa_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + */ +#ifndef _AUDIO_ALSA_OUT_H_ +#define _AUDIO_ALSA_OUT_H_ 1 + +ao_functions_t *audio_alsaout_init(void); + +#endif + diff --git a/src/audio_out/audio_esd_out.c b/src/audio_out/audio_esd_out.c new file mode 100644 index 000000000..259364a55 --- /dev/null +++ b/src/audio_out/audio_esd_out.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <esd.h> +#include <sys/time.h> +#include <inttypes.h> + +#include "xine/xine.h" +#include "xine/monitor.h" +#include "xine/audio_out.h" +#include "resample.h" +#include "xine/metronom.h" +#include "xine/ac3.h" +#include "xine/utils.h" + +#define GAP_TOLERANCE 15000 +#define MAX_MASTER_CLOCK_DIV 5000 + +extern uint32_t xine_debug; + +typedef struct _audio_esd_globals { + + int audio_fd; + + int32_t output_sample_rate, input_sample_rate; + int32_t output_rate_correction; + double sample_rate_factor; + uint32_t num_channels; + + uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ + uint32_t last_vpts; /* vpts at which last written package ends */ + + uint32_t sync_vpts; /* this syncpoint is used as a starting point */ + uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */ + + int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */ + int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */ + + int16_t *zero_space; + + int audio_started; + +} audio_esd_globals_t; + +static audio_esd_globals_t gAudioESD; + +/* + * open the audio device for writing to + */ +static int ao_open(uint32_t bits, uint32_t rate, int mode) +{ + esd_format_t format; + + printf ("audio_esd_out: ao_open rate=%d, mode=%d\n", rate, mode); + + if ((mode != AO_MODE_STEREO) && (mode != AO_MODE_MONO)) { + printf ("ESD Driver only supports mono/stereo output modes at the moment\n"); + return -1; + } + + if (gAudioESD.audio_fd > -1) { + + if (rate == gAudioESD.input_sample_rate) + return 1; + + close (gAudioESD.audio_fd); + } + + gAudioESD.input_sample_rate = rate; + gAudioESD.bytes_in_buffer = 0; + gAudioESD.last_vpts = 0; + gAudioESD.output_rate_correction = 0; + gAudioESD.sync_vpts = 0; + gAudioESD.sync_bytes_in_buffer = 0; + gAudioESD.audio_started = 0; + + /* + * open stream to ESD server + */ + + format = ESD_STREAM | ESD_PLAY | ESD_BITS16; + if (mode == AO_MODE_STEREO) { + format |= ESD_STEREO; + gAudioESD.num_channels = 2; + } else { + format |= ESD_MONO; + gAudioESD.num_channels = 1; + } + gAudioESD.output_sample_rate = gAudioESD.input_sample_rate; + if (gAudioESD.output_sample_rate > 44100) + gAudioESD.output_sample_rate = 44100; + + gAudioESD.audio_fd=esd_play_stream(format, gAudioESD.output_sample_rate, NULL, NULL); + if(gAudioESD.audio_fd < 0) { + printf("audio_esd_out: Connecting to ESD server %s: %s\n", + getenv("ESPEAKER"), strerror(errno)); + return -1; + } + + xprintf (VERBOSE|AUDIO, "audio_esd_out: %d channels\n",gAudioESD.num_channels); + + gAudioESD.sample_rate_factor = (double) gAudioESD.output_sample_rate / (double) gAudioESD.input_sample_rate; + gAudioESD.audio_step = (uint32_t) 90000 * (uint32_t) 32768 + / gAudioESD.input_sample_rate; + gAudioESD.bytes_per_kpts = gAudioESD.output_sample_rate * gAudioESD.num_channels * 2 * 1024 / 90000; + + xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n", gAudioESD.audio_step); + + metronom_set_audio_rate (gAudioESD.audio_step); + + return 1; +} + +static uint32_t ao_get_current_vpts (void) { + + int32_t diff ; + uint32_t vpts ; + + if (gAudioESD.audio_started) + diff = 0; + else + diff = gAudioESD.sync_bytes_in_buffer; + + vpts = gAudioESD.sync_vpts - diff * 1024 / gAudioESD.bytes_per_kpts; + +// xprintf (AUDIO|VERBOSE,"audio_esd_out: get_current_vpts pos=%d diff=%d vpts=%d sync_vpts=%d\n", +// pos, diff, vpts, gAudioESD.sync_vpts); + + return vpts; +} + +static void ao_fill_gap (uint32_t pts_len) { + + int num_bytes = pts_len * gAudioESD.bytes_per_kpts / 1024; + + num_bytes = (num_bytes / 4) * 4; + + printf ("audio_esd_out: inserting %d 0-bytes to fill a gap of %d pts\n",num_bytes, pts_len); + + gAudioESD.bytes_in_buffer += num_bytes; + + while (num_bytes>0) { + if (num_bytes>8192) { + write(gAudioESD.audio_fd, gAudioESD.zero_space, 8192); + num_bytes -= 8192; + } else { + write(gAudioESD.audio_fd, gAudioESD.zero_space, num_bytes); + num_bytes = 0; + } + } + + gAudioESD.last_vpts += pts_len; +} + +static void ao_write_audio_data(int16_t* output_samples, uint32_t num_samples, + uint32_t pts_) +{ + + uint32_t vpts, + audio_vpts, + master_vpts; + int32_t diff, gap; + int bDropPackage; + uint16_t sample_buffer[8192]; + + + if (gAudioESD.audio_fd<0) + return; + + vpts = metronom_got_audio_samples (pts_, num_samples); + + xprintf (VERBOSE|AUDIO, "audio_esd_out: got %d samples, vpts=%d, last_vpts=%d\n", + num_samples, vpts, gAudioESD.last_vpts); + + /* + * check if these samples "fit" in the audio output buffer + * or do we have an audio "gap" here? + */ + + gap = vpts - gAudioESD.last_vpts ; + + /* + printf ("audio_esd_out: gap = %d - %d + %d = %d\n", + vpts, gAudioESD.last_vpts, diff, gap); + */ + + bDropPackage = 0; + + if (gap>GAP_TOLERANCE) { + ao_fill_gap (gap); + } else if (gap<-GAP_TOLERANCE) { + bDropPackage = 1; + } + + /* + * sync on master clock + */ + + audio_vpts = ao_get_current_vpts () ; + master_vpts = metronom_get_current_time (); + diff = audio_vpts - master_vpts; + + xprintf (AUDIO|VERBOSE, "audio_esd_out: syncing on master clock: audio_vpts=%d master_vpts=%d\n", + audio_vpts, master_vpts); + /* + printf ("audio_esd_out: audio_vpts=%d <=> master_vpts=%d (diff=%d)\n", + audio_vpts, master_vpts, diff); + */ + + /* + * adjust master clock + */ + + if (abs(diff)>MAX_MASTER_CLOCK_DIV) { + printf ("master clock adjust time %d -> %d (diff: %d)\n", master_vpts, audio_vpts, diff); + metronom_adjust_clock (audio_vpts); + } + + /* + * resample and output samples + */ + + if (!bDropPackage) { + int num_output_samples = num_samples * (gAudioESD.output_sample_rate + gAudioESD.output_rate_correction) / gAudioESD.input_sample_rate; + + + audio_out_resample_stereo (output_samples, num_samples, + sample_buffer, num_output_samples); + + write(gAudioESD.audio_fd, sample_buffer, num_output_samples * 2 * gAudioESD.num_channels); + + xprintf (AUDIO|VERBOSE, "audio_esd_out :audio package written\n"); + + /* + * remember vpts + */ + + gAudioESD.sync_vpts = vpts; + gAudioESD.sync_bytes_in_buffer = gAudioESD.bytes_in_buffer; + + /* + * step values + */ + + gAudioESD.bytes_in_buffer += num_output_samples * 2 * gAudioESD.num_channels; + gAudioESD.audio_started = 1; + } else { + printf ("audio_esd_out: audio package (vpts = %d) dropped\n", vpts); + gAudioESD.sync_vpts = vpts; + } + + gAudioESD.last_vpts = vpts + num_samples * 90000 / gAudioESD.input_sample_rate ; +} + + +static void ao_close(void) +{ + close(gAudioESD.audio_fd); + gAudioESD.audio_fd = -1; +} + +static int ao_is_mode_supported (int mode) { + return ((mode == AO_MODE_STEREO) || (mode == AO_MODE_MONO)); +} + +static ao_functions_t audio_esdout = { + ao_is_mode_supported, + ao_open, + ao_write_audio_data, + ao_close, +}; + + +ao_functions_t *audio_esdout_init (void) +{ + int audio_fd; + + /* + * open stream to ESD server + */ + + xprintf(VERBOSE|AUDIO, "Connecting to ESD server..."); + audio_fd = esd_open_sound(NULL); + + if(audio_fd < 0) + { + char *server = getenv("ESPEAKER"); + + // print a message so the user knows why ESD failed + printf("Can't connect to %s ESD server: %s\n", + server ? server : "local", strerror(errno)); + + return NULL; + } // else +// xprintf(VERBOSE|AUDIO, " %s\n", gAudioESD.audio_dev); + + close(audio_fd); + + gAudioESD.output_sample_rate = 0; + gAudioESD.zero_space = xmalloc (8192); + + return &audio_esdout; +} diff --git a/src/audio_out/audio_esd_out.h b/src/audio_out/audio_esd_out.h new file mode 100644 index 000000000..a98dcc34f --- /dev/null +++ b/src/audio_out/audio_esd_out.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _AUDIO_ESD_OUT_H_ +#define _AUDIO_ESD_OUT_H_ 1 + +ao_functions_t *audio_esdout_init(void); + +#endif diff --git a/src/audio_out/audio_oss_out.c b/src/audio_out/audio_oss_out.c new file mode 100644 index 000000000..18d11ff5d --- /dev/null +++ b/src/audio_out/audio_oss_out.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: audio_oss_out.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> +#if defined(__OpenBSD__) +#include <soundcard.h> +#elif defined(__FreeBSD__) +#include <machine/soundcard.h> +#else +#include <sys/soundcard.h> +#endif +#include <sys/ioctl.h> +#include <inttypes.h> + +#include "xine/xine.h" +#include "xine/monitor.h" +#include "xine/audio_out.h" +#include "resample.h" +#include "xine/metronom.h" +#include "xine/ac3.h" +#include "xine/utils.h" + +#define AUDIO_NUM_FRAGMENTS 15 +#define AUDIO_FRAGMENT_SIZE 8192 + +#define GAP_TOLERANCE 15000 +#define MAX_MASTER_CLOCK_DIV 5000 + +extern uint32_t xine_debug; + +#define DSP_TEMPLATE "/dev/dsp%d" + +typedef struct _audio_oss_globals { + + char audio_dev[20]; + int audio_fd; + + int32_t output_sample_rate, input_sample_rate; + int32_t output_rate_correction; + double sample_rate_factor; + uint32_t num_channels; + + uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ + uint32_t last_vpts; /* vpts at which last written package ends */ + + uint32_t sync_vpts; /* this syncpoint is used as a starting point */ + uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */ + + int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */ + int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */ + + int16_t *zero_space; + + int audio_started; + +} audio_oss_globals_t; + +static audio_oss_globals_t gAudioOSS; + +/* + * open the audio device for writing to + */ +static int ao_open(uint32_t bits, uint32_t rate, int mode) +{ + int tmp; + int fsize; + + printf ("audio_oss_out: ao_open rate=%d, mode=%d\n", rate, mode); + + if ((mode != AO_MODE_STEREO) && (mode != AO_MODE_MONO)) { + printf ("OSS Driver only supports mono/stereo output modes at the moment\n"); + return -1; + } + + if (gAudioOSS.audio_fd > -1) { + + if (rate == gAudioOSS.input_sample_rate) + return 1; + + close (gAudioOSS.audio_fd); + } + + gAudioOSS.input_sample_rate = rate; + gAudioOSS.bytes_in_buffer = 0; + gAudioOSS.last_vpts = 0; + gAudioOSS.output_rate_correction = 0; + gAudioOSS.sync_vpts = 0; + gAudioOSS.sync_bytes_in_buffer = 0; + gAudioOSS.audio_started = 0; + + /* + * open audio device + */ + + gAudioOSS.audio_fd=open(gAudioOSS.audio_dev,O_WRONLY|O_NDELAY); + if(gAudioOSS.audio_fd < 0) { + printf("audio_oss_out: Opening audio device %s: %s\n", + gAudioOSS.audio_dev, strerror(errno)); + return -1; + } + + /* We wanted non blocking open but now put it back to normal */ + fcntl(gAudioOSS.audio_fd, F_SETFL, fcntl(gAudioOSS.audio_fd, F_GETFL)&~FNDELAY); + + /* + * configure audio device + */ + + tmp = (mode == AO_MODE_STEREO) ? 1 : 0; + ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_STEREO,&tmp); + + gAudioOSS.num_channels = tmp+1; + xprintf (VERBOSE|AUDIO, "audio_oss_out: %d channels\n",gAudioOSS.num_channels); + + tmp = bits; + ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SAMPLESIZE,&tmp); + + tmp = gAudioOSS.input_sample_rate; + ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SPEED, &tmp); + gAudioOSS.output_sample_rate = tmp; + + xprintf (VERBOSE|AUDIO, "audio_oss_out: audio rate : %d requested, %d provided by device/sec\n", + gAudioOSS.input_sample_rate, gAudioOSS.output_sample_rate); + + gAudioOSS.sample_rate_factor = (double) gAudioOSS.output_sample_rate / (double) gAudioOSS.input_sample_rate; + gAudioOSS.audio_step = (uint32_t) 90000 * (uint32_t) 32768 + / gAudioOSS.input_sample_rate; + gAudioOSS.bytes_per_kpts = gAudioOSS.output_sample_rate * gAudioOSS.num_channels * 2 * 1024 / 90000; + + xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n", gAudioOSS.audio_step); + + metronom_set_audio_rate (gAudioOSS.audio_step); + + /* + * audio buffer size handling + */ + + tmp=0 ; + fsize = AUDIO_FRAGMENT_SIZE; + while (fsize>0) { + fsize /=2; + tmp++; + } + tmp--; + + tmp = (AUDIO_NUM_FRAGMENTS << 16) | tmp ; + + xprintf (VERBOSE|AUDIO, "Audio buffer fragment info : %x\n",tmp); + + ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SETFRAGMENT,&tmp); + + + return 1; +} + +static uint32_t ao_get_current_vpts (void) { + + int pos ; + int32_t diff ; + uint32_t vpts ; + + count_info info; + + if (gAudioOSS.audio_started) { + ioctl (gAudioOSS.audio_fd, SNDCTL_DSP_GETOPTR, &info); + + pos = info.bytes; + + } else + pos = 0; + + diff = gAudioOSS.sync_bytes_in_buffer - pos; + + vpts = gAudioOSS.sync_vpts - diff * 1024 / gAudioOSS.bytes_per_kpts; + + xprintf (AUDIO|VERBOSE,"audio_oss_out: get_current_vpts pos=%d diff=%d vpts=%d sync_vpts=%d\n", + pos, diff, vpts, gAudioOSS.sync_vpts); + + return vpts; +} + +static void ao_fill_gap (uint32_t pts_len) { + + int num_bytes = pts_len * gAudioOSS.bytes_per_kpts / 1024; + + num_bytes = (num_bytes / 4) * 4; + + printf ("audio_oss_out: inserting %d 0-bytes to fill a gap of %d pts\n",num_bytes, pts_len); + + gAudioOSS.bytes_in_buffer += num_bytes; + + while (num_bytes>0) { + if (num_bytes>8192) { + write(gAudioOSS.audio_fd, gAudioOSS.zero_space, 8192); + num_bytes -= 8192; + } else { + write(gAudioOSS.audio_fd, gAudioOSS.zero_space, num_bytes); + num_bytes = 0; + } + } + + gAudioOSS.last_vpts += pts_len; +} + +static void ao_write_audio_data(int16_t* output_samples, uint32_t num_samples, + uint32_t pts_) +{ + + uint32_t vpts, + audio_vpts, + master_vpts; + int32_t diff, gap; + int bDropPackage; + uint16_t sample_buffer[8192]; + + + if (gAudioOSS.audio_fd<0) + return; + + vpts = metronom_got_audio_samples (pts_, num_samples); + + xprintf (VERBOSE|AUDIO, "audio_oss_out: got %d samples, vpts=%d, last_vpts=%d\n", + num_samples, vpts, gAudioOSS.last_vpts); + + /* + * check if these samples "fit" in the audio output buffer + * or do we have an audio "gap" here? + */ + + gap = vpts - gAudioOSS.last_vpts ; + + /* + printf ("audio_oss_out: gap = %d - %d + %d = %d\n", + vpts, gAudioOSS.last_vpts, diff, gap); + */ + + bDropPackage = 0; + + if (gap>GAP_TOLERANCE) { + ao_fill_gap (gap); + } else if (gap<-GAP_TOLERANCE) { + bDropPackage = 1; + } + + /* + * sync on master clock + */ + + audio_vpts = ao_get_current_vpts () ; + master_vpts = metronom_get_current_time (); + diff = audio_vpts - master_vpts; + + xprintf (AUDIO|VERBOSE, "audio_oss_out: syncing on master clock: audio_vpts=%d master_vpts=%d\n", + audio_vpts, master_vpts); + /* + printf ("audio_oss_out: audio_vpts=%d <=> master_vpts=%d (diff=%d)\n", + audio_vpts, master_vpts, diff); + */ + /* + * method 1 : resampling + */ + + /* + if (abs(diff)>5000) { + + if (diff>5000) { + ao_fill_gap (diff); + } else if (diff<-5000) { + bDropPackage = 1; + } + + } else if (abs(diff)>1000) { + gAudioOSS.output_rate_correction = diff/10 ; + + printf ("audio_oss_out: diff = %d => rate correction : %d\n", diff, gAudioOSS.output_rate_correction); + + if ( gAudioOSS.output_rate_correction < -500) + gAudioOSS.output_rate_correction = -500; + else if ( gAudioOSS.output_rate_correction > 500) + gAudioOSS.output_rate_correction = 500; + } + */ + + /* + * method 2: adjust master clock + */ + + + if (abs(diff)>MAX_MASTER_CLOCK_DIV) { + printf ("master clock adjust time %d -> %d (diff: %d)\n", master_vpts, audio_vpts, diff); + metronom_adjust_clock (audio_vpts); + } + + + /* + * resample and output samples + */ + + if (!bDropPackage) { + int num_output_samples = num_samples * (gAudioOSS.output_sample_rate + gAudioOSS.output_rate_correction) / gAudioOSS.input_sample_rate; + + + audio_out_resample_stereo (output_samples, num_samples, + sample_buffer, num_output_samples); + + write(gAudioOSS.audio_fd, sample_buffer, num_output_samples * 2 * gAudioOSS.num_channels); + + xprintf (AUDIO|VERBOSE, "audio_oss_out :audio package written\n"); + + /* + * remember vpts + */ + + gAudioOSS.sync_vpts = vpts; + gAudioOSS.sync_bytes_in_buffer = gAudioOSS.bytes_in_buffer; + + /* + * step values + */ + + gAudioOSS.bytes_in_buffer += num_output_samples * 2 * gAudioOSS.num_channels; + gAudioOSS.audio_started = 1; + } else { + printf ("audio_oss_out: audio package (vpts = %d) dropped\n", vpts); + gAudioOSS.sync_vpts = vpts; + } + + gAudioOSS.last_vpts = vpts + num_samples * 90000 / gAudioOSS.input_sample_rate ; +} + + +static void ao_close(void) +{ + close(gAudioOSS.audio_fd); + gAudioOSS.audio_fd = -1; +} + +static int ao_is_mode_supported (int mode) { + return ((mode == AO_MODE_STEREO) || (mode == AO_MODE_MONO)); +} + +static ao_functions_t audio_ossout = { + ao_is_mode_supported, + ao_open, + ao_write_audio_data, + ao_close, +}; + + +ao_functions_t *audio_ossout_init (void) +{ + int caps; + char devname[] = "/dev/dsp\0\0\0"; + int best_rate; + int rate ; + int devnum; + int audio_fd; + + /* + * find best device driver/channel + */ + + xprintf (VERBOSE|AUDIO, "Opening audio device..."); + devnum = 0; + best_rate = 0; + while (devnum<16) { + audio_fd=open(devname,O_WRONLY|O_NDELAY); + + if (audio_fd>0) { + + /* test bitrate capability */ + + rate = 48000; + ioctl(audio_fd,SNDCTL_DSP_SPEED, &rate); + if (rate>best_rate) { + strncpy (gAudioOSS.audio_dev, devname, 19); + best_rate = rate; + } + + close (audio_fd); + } + + sprintf(devname, DSP_TEMPLATE, devnum); + devnum++; + } + + /* + * open that device + */ + + audio_fd=open(gAudioOSS.audio_dev, O_WRONLY|O_NDELAY); + + if(audio_fd < 0) + { + xprintf(VERBOSE|AUDIO, "%s: Opening audio device %s\n", + strerror(errno), gAudioOSS.audio_dev); + + return NULL; + + } else + xprintf (VERBOSE|AUDIO, " %s\n", gAudioOSS.audio_dev); + + ioctl (audio_fd, SNDCTL_DSP_GETCAPS, &caps); + + if ((caps & DSP_CAP_REALTIME) > 0) { + xprintf (VERBOSE|AUDIO, "audio_out : realtime check: passed :-)\n"); + } else { + xprintf (VERBOSE|AUDIO, "audio_out : realtime check: *FAILED* :-(((((\n\n"); + } + + if ((caps & DSP_CAP_TRIGGER) > 0) { + xprintf (VERBOSE|AUDIO, "audio_out : trigger check : passed :-)\n"); + } else { + xprintf (VERBOSE|AUDIO, "audio_out : trigger check : *FAILED* :-(((((\n"); + } + + close (audio_fd); + + gAudioOSS.output_sample_rate = 0; + + gAudioOSS.zero_space = xmalloc (8192); + + return &audio_ossout; +} diff --git a/src/audio_out/audio_oss_out.h b/src/audio_out/audio_oss_out.h new file mode 100644 index 000000000..2618acbf9 --- /dev/null +++ b/src/audio_out/audio_oss_out.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: audio_oss_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + */ +#ifndef _AUDIO_OSS_OUT_H_ +#define _AUDIO_OSS_OUT_H_ 1 + +ao_functions_t *audio_ossout_init(void); + +#endif diff --git a/src/audio_out/resample.c b/src/audio_out/resample.c new file mode 100644 index 000000000..9f464b276 --- /dev/null +++ b/src/audio_out/resample.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: resample.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +/* contributed by paul flinders */ + +void audio_out_resample_stereo(int16_t* input_samples, uint32_t in_samples, + int16_t* output_samples, uint32_t out_samples) +{ + int osample; + /* 16+16 fixed point math */ + uint32_t isample = 0; + uint32_t istep = (in_samples << 16)/out_samples; + +#ifdef VERBOSE + printf ("Audio : resample %d samples to %d\n", + in_samples, out_samples); +#endif + + for (osample = 0; osample < out_samples - 1; osample++) { + int s1; + int s2; + int16_t os; + uint32_t t = isample&0xffff; + + /* don't "optimize" the (isample >> 16)*2 to (isample >> 15) */ + s1 = input_samples[(isample >> 16)*2]; + s2 = input_samples[(isample >> 16)*2+2]; + + os = (s1 * (0x10000-t)+ s2 * t) >> 16; + output_samples[osample * 2] = os; + + s1 = input_samples[(isample >> 16)*2+1]; + s2 = input_samples[(isample >> 16)*2+3]; + + os = (s1 * (0x10000-t)+ s2 * t) >> 16; + output_samples[(osample * 2 )+1] = os; + isample += istep; + } + output_samples[out_samples*2-2] = input_samples[in_samples*2-2]; + output_samples[out_samples*2-1] = input_samples[in_samples*2-1]; +} + diff --git a/src/audio_out/resample.h b/src/audio_out/resample.h new file mode 100644 index 000000000..11c9e4a45 --- /dev/null +++ b/src/audio_out/resample.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: resample.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + * + * utilitiy functions for audio drivers + * + * FIXME: not all of them are implemented yet + */ + +#ifndef HAVE_RESAMPLE_H +#define HAVE_RESAMPLE_H + +void audio_out_resample_stereo(int16_t* input_samples, uint32_t in_samples, + int16_t* output_samples, uint32_t out_samples); + +void audio_out_resample_mono(int16_t* input_samples, uint32_t in_samples, + int16_t* output_samples, uint32_t out_samples); + +void audio_out_resample_4channel(int16_t* input_samples, uint32_t in_samples, + int16_t* output_samples, uint32_t out_samples); + +void audio_out_resample_5channel(int16_t* input_samples, uint32_t in_samples, + int16_t* output_samples, uint32_t out_samples); + +#endif diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am new file mode 100644 index 000000000..8d0c37c20 --- /dev/null +++ b/src/video_out/Makefile.am @@ -0,0 +1,41 @@ +CFLAGS = @GLOBAL_CFLAGS@ @X_CFLAGS@ -DXINE_COMPILE + +if HAVE_X11 +## xshm and syncfb should be fixed first ;-) +## xshm_module = xineplug_vo_out_xshm.la +## syncfb_module = xineplug_vo_out_syncfb.la +if HAVE_XV +xv_module = xineplug_vo_out_xv.la +endif +endif + +## +# IMPORTANT: +# --------- +# All of xine video out plugins should be named like the +# scheme "xineplug_vo_out_" +# +lib_LTLIBRARIES = $(xv_module) ## $(xshm_module) $(syncfb_module) + +xineplug_vo_out_xv_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c video_out_xv.c +xineplug_vo_out_xv_la_LDFLAGS = -avoid-version -module + +##xineplug_vo_out_xshm_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c video_out_xshm.c +##xineplug_vo_out_xshm_la_LDFLAGS = -avoid-version -module + +##xineplug_vo_out_syncfb_la_SOURCES = yuv2rgb.c yuv2rgb_mmx.c video_out_syncfb.c +##xineplug_vo_out_syncfb_la_LDFLAGS = -avoid-version -module + +noinst_HEADERS = yuv2rgb.h video_out_syncfb.h + + +debug: + $(MAKE) CFLAGS="$(DEBUG_CFLAGS)" + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/video_out/video_out_syncfb.c b/src/video_out/video_out_syncfb.c new file mode 100644 index 000000000..2d99f3216 --- /dev/null +++ b/src/video_out/video_out_syncfb.c @@ -0,0 +1,849 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: video_out_syncfb.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + * + * video_out_syncfb.c, Matrox G400 video extension interface for xine + * + * based on video_out_mga code from + * Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * xine-specific code by Joachim Koenig <joachim.koenig@gmx.net> + * Underlaying window by Matthias Dahl <matthew2k@web.de> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <sys/ioctl.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <X11/Xutil.h> + +#include "video_out.h" +#include "video_out_syncfb.h" + +#include "monitor.h" +#include "configfile.h" + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 +typedef struct _mwmhints { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t input_mode; + uint32_t status; +} MWMHints; + +Display *lDisplay; +extern Display *gDisplay; +extern Window gVideoWin; +extern Pixmap gXineLogo; +extern int gXineLogoWidth, gXineLogoHeight; + +extern uint32_t xine_debug; + + + +typedef struct _display { + int width; + int height; + int depth; + int default_screen; +} display; + +typedef struct _window { + char title[20]; + + Window clasped_window; + int visibility; +} window; + +typedef struct _mga_globals { + syncfb_config_t mga_vid_config; + syncfb_capability_t caps; + syncfb_buffer_info_t bufinfo; + syncfb_param_t param; + uint8_t *vid_data, *frame0, *frame1; + int next_frame; + int fd; + uint32_t bespitch; + + int bFullscreen; + int bIsFullscreen; + int image_width; + int image_height; + int image_xoff; + int image_yoff; + int orig_width; /* image size correct. by ratio */ + int orig_height; + int dest_width; + int dest_height; + int fourcc_format; + + uint32_t ratio; + int user_ratio, user_ratio_changed; + +// XvImage *cur_image; + + /* + * misc (read: fun ;)) + */ + int bLogoMode; + + int bright_min, bright_current, bright_max; + int cont_min, cont_current, cont_max; + + int overlay_state; + +} mga_globals; + +mga_globals _mga_priv; +display _display; +window _window; + +int nLocks; + + + +static int _mga_write_frame_g400 (uint8_t *src[]) +{ + uint8_t *dest; + int h; + uint8_t *y = src[0]; + uint8_t *cb = src[1]; + uint8_t *cr = src[2]; + + dest = _mga_priv.vid_data; + + if (_mga_priv.fourcc_format == IMGFMT_YUY2) { + for (h=0; h < _mga_priv.mga_vid_config.src_height; h++) { + memcpy(dest, y, _mga_priv.mga_vid_config.src_width*2); + y += _mga_priv.mga_vid_config.src_width*2; + dest += _mga_priv.bespitch*2; + } + return 0; + } + + + for (h=0; h < _mga_priv.mga_vid_config.src_height; h++) { + memcpy(dest, y, _mga_priv.mga_vid_config.src_width); + y += _mga_priv.mga_vid_config.src_width; + dest += _mga_priv.bespitch; + } + + + for (h=0; h < _mga_priv.mga_vid_config.src_height/2; h++) { + memcpy(dest, cb, _mga_priv.mga_vid_config.src_width/2); + cb += _mga_priv.mga_vid_config.src_width/2; + dest += _mga_priv.bespitch/2; + } + + dest = _mga_priv.vid_data + _mga_priv.bespitch * _mga_priv.mga_vid_config.src_height * 3 / 2; + + for (h=0; h < _mga_priv.mga_vid_config.src_height/2; h++) { + memcpy(dest, cr, _mga_priv.mga_vid_config.src_width/2); + cr += _mga_priv.mga_vid_config.src_width/2; + dest += _mga_priv.bespitch/2; + } + + return 0; +} + + + + + + +static int get_capabilities_mga () { + return VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CONTRAST | VO_CAP_BRIGHTNESS; +} + + +static void setup_window_mga () { + int ww=0, wh=0; + float aspect; + + XWindowAttributes wattr; + + Atom prop; + MWMHints mwmhints; + + XGetWindowAttributes(lDisplay, DefaultRootWindow(lDisplay), &wattr); + + _display.width = wattr.width; + _display.height = wattr.height; + _display.depth = wattr.depth; + + ww = _display.width ; + wh = _display.height; + +xprintf(VERBOSE|VIDEO,"setup_window_mga: unscaled size should be %d x %d \n",_mga_priv.orig_width,_mga_priv.orig_height); +printf("setup_window_mga: unscaled size should be %d x %d \n",_mga_priv.orig_width,_mga_priv.orig_height); + + if (_mga_priv.bFullscreen) { + + /* + * zoom to fullscreen + */ + + if (_mga_priv.orig_width != ww) { + aspect = (float) _mga_priv.orig_width / (float) _mga_priv.orig_height ; + + _mga_priv.dest_width = ww; + _mga_priv.dest_height = ww / aspect; + + if (_mga_priv.dest_height > wh) { + + _mga_priv.dest_width = wh * aspect; + _mga_priv.dest_height = wh; + } + + } else { + + _mga_priv.dest_width = _mga_priv.orig_width ; + _mga_priv.dest_height = _mga_priv.orig_height ; + + } + _mga_priv.image_xoff = ( ww - _mga_priv.dest_width) / 2; + _mga_priv.image_yoff = ( wh - _mga_priv.dest_height) / 2; + + + _mga_priv.bIsFullscreen = 1; + + + } else { + + /* + * zoom to mpeg1 to double size + */ + +// if (_mga_priv.orig_width < 600) { +// _mga_priv.dest_width = _mga_priv.orig_width *2; +// _mga_priv.dest_height = _mga_priv.orig_height *2; +// } else { + + if (_mga_priv.orig_width > ww) { + aspect = (float) _mga_priv.orig_width / (float) _mga_priv.orig_height ; + + _mga_priv.dest_width = ww; + _mga_priv.dest_height = ww / aspect; + + if (_mga_priv.dest_height > wh) { + _mga_priv.dest_width = wh * aspect; + _mga_priv.dest_height = wh; + } + + } else { + _mga_priv.dest_width = _mga_priv.orig_width ; + _mga_priv.dest_height = _mga_priv.orig_height ; + } +// } + + _mga_priv.bIsFullscreen = 0; + + _mga_priv.image_xoff = ( ww - _mga_priv.dest_width) / 2; + _mga_priv.image_yoff = ( wh - _mga_priv.dest_height) / 2; + + } + xprintf(VERBOSE|VIDEO,"Calculated size should be %d x %d xoff %d yoff %d Display Is %d x %d\n",_mga_priv.dest_width,_mga_priv.dest_height,_mga_priv.image_xoff,_mga_priv.image_yoff,ww,wh); + printf("Calculated size should be %d x %d xoff %d yoff %d Display Is %d x %d\n",_mga_priv.dest_width,_mga_priv.dest_height,_mga_priv.image_xoff,_mga_priv.image_yoff,ww,wh); + + if (ioctl(_mga_priv.fd,SYNCFB_GET_CAPS,&_mga_priv.caps)) perror("Error in config ioctl"); + xprintf(VERBOSE|VIDEO,"Syncfb device name is '%s'\n", _mga_priv.caps.name); + xprintf(VERBOSE|VIDEO,"Memory size is %ld \n", _mga_priv.caps.memory_size); + + if (ioctl(_mga_priv.fd,SYNCFB_GET_CONFIG,&_mga_priv.mga_vid_config)) + printf("Error in get_config ioctl\n"); + + _mga_priv.mga_vid_config.fb_screen_size = _display.width * _display.height * (_display.depth/8); // maybe wrong if depth = 15 (?) + _mga_priv.mga_vid_config.src_width = _mga_priv.image_width; + _mga_priv.mga_vid_config.src_height= _mga_priv.image_height; + _mga_priv.bespitch = (_mga_priv.image_width + 31) & ~31; + + switch (_mga_priv.fourcc_format) { + case IMGFMT_YV12: + _mga_priv.mga_vid_config.src_palette = VIDEO_PALETTE_YUV420P3; + break; + case IMGFMT_YUY2: + _mga_priv.mga_vid_config.src_palette = VIDEO_PALETTE_YUYV; + break; + default: + _mga_priv.mga_vid_config.src_palette = VIDEO_PALETTE_YUV420P3; + break; + } + _mga_priv.mga_vid_config.image_width = _mga_priv.dest_width; + _mga_priv.mga_vid_config.image_height= _mga_priv.dest_height; + _mga_priv.mga_vid_config.syncfb_mode = SYNCFB_FEATURE_BLOCK_REQUEST | SYNCFB_FEATURE_SCALE_H | SYNCFB_FEATURE_SCALE_V | SYNCFB_FEATURE_CROP ; /* | SYNCFB_FEATURE_DEINTERLACE; */ + _mga_priv.mga_vid_config.image_xorg= _mga_priv.image_xoff; + _mga_priv.mga_vid_config.image_yorg= _mga_priv.image_yoff; + + _mga_priv.mga_vid_config.src_crop_top = 0; + _mga_priv.mga_vid_config.src_crop_bot = 0; + +#ifdef CINEMODE + _mga_priv.mga_vid_config.default_repeat = 3; +#else + _mga_priv.mga_vid_config.default_repeat = 2; +#endif + + if (ioctl(_mga_priv.fd,SYNCFB_SET_CONFIG,&_mga_priv.mga_vid_config)) + xprintf(VERBOSE|VIDEO,"Error in set_config ioctl\n"); + if (_mga_priv.bLogoMode) { + if (ioctl(_mga_priv.fd,SYNCFB_OFF)) { + xprintf(VERBOSE|VIDEO,"Error in OFF ioctl\n"); + } + else + _mga_priv.overlay_state = 0; + } + +// if (ioctl(_mga_priv.fd,SYNCFB_ON)) +// xprintf(VERBOSE|VIDEO,"Error in ON ioctl\n"); + + // create a simple window without anything. Just make overlay clickable. :) + if (!_window.clasped_window) { + _window.clasped_window = XCreateSimpleWindow(lDisplay, RootWindow(lDisplay, _display.default_screen), 0, 0, _mga_priv.dest_width, _mga_priv.dest_height, 0, 0, 0); + gVideoWin = _window.clasped_window; + + // turn off all borders etc. (taken from the Xv plugin) + prop = XInternAtom(lDisplay, "_MOTIF_WM_HINTS", False); + mwmhints.flags = MWM_HINTS_DECORATIONS; + mwmhints.decorations = 0; + XChangeProperty(lDisplay, gVideoWin, prop, prop, 32, + PropModeReplace, (unsigned char *) &mwmhints, + PROP_MWM_HINTS_ELEMENTS); + XSetTransientForHint(lDisplay, gVideoWin, None); + + XSelectInput(gDisplay, _window.clasped_window, VisibilityChangeMask | KeyPressMask | ButtonPressMask | SubstructureNotifyMask | StructureNotifyMask); + XMapRaised(lDisplay, _window.clasped_window); + XSync(lDisplay,0); + } + + XSetStandardProperties(lDisplay, _window.clasped_window, _window.title, _window.title, None, NULL, 0, NULL); + XMoveResizeWindow(lDisplay, _window.clasped_window, (_mga_priv.bIsFullscreen) ? 0 : _mga_priv.image_xoff, (_mga_priv.bIsFullscreen) ? 0 : _mga_priv.image_yoff, (_mga_priv.bIsFullscreen) ? _display.width : _mga_priv.dest_width, (_mga_priv.bIsFullscreen) ? _display.height : _mga_priv.dest_height); + XMapRaised(lDisplay, _window.clasped_window); + XSync(lDisplay,0); +} + + + +/* setup internal variables and (re-)init window if necessary */ +static int set_image_format_mga (uint32_t width, uint32_t height, uint32_t ratio, int format) { + + + double res_h, res_v, display_ratio, aspect_ratio; + + if ( (_mga_priv.image_width == width) + && (_mga_priv.image_height == height) + && (_mga_priv.ratio == ratio) + && (_mga_priv.bFullscreen == _mga_priv.bIsFullscreen) + && (_mga_priv.fourcc_format == format) + && !_mga_priv.user_ratio_changed ) { + + + return 0; + } + + _mga_priv.image_width = width; + _mga_priv.image_height = height; + _mga_priv.ratio = ratio; + _mga_priv.fourcc_format = format; + _mga_priv.user_ratio_changed = 0; + + + /* + * Mpeg-2: + */ + + res_h = 1; // _display.width; + res_v = 1; // _display.height; + + display_ratio = res_h / res_v; + + xprintf (VERBOSE | VIDEO, "display_ratio : %f\n",display_ratio); + + if (_mga_priv.user_ratio == ASPECT_AUTO) { + switch (_mga_priv.ratio) { + case 0: /* forbidden */ + fprintf (stderr, "invalid ratio\n"); + exit (1); + break; + case 1: /* "square" => 4:3 */ + case 2: + aspect_ratio = 4.0 / 3.0; + break; + case 3: + aspect_ratio = 16.0 / 9.0; + break; + case 42: /* some stupid stream => don't touch aspect ratio */ + default: + xprintf (VIDEO, "unknown aspect ratio (%d) in stream. untouched.\n", _mga_priv.ratio); + aspect_ratio = (double)_mga_priv.image_width / (double)_mga_priv.image_height; + break; + } + } else if (_mga_priv.user_ratio == ASPECT_ANAMORPHIC) { + aspect_ratio = 16.0 / 9.0; + } else if (_mga_priv.user_ratio == ASPECT_DVB) { + aspect_ratio = 2.0 / 1.0; + } else { + aspect_ratio = 1.0; + } + aspect_ratio *= display_ratio; + if (_mga_priv.image_height * aspect_ratio >= _mga_priv.image_width) { + _mga_priv.orig_width = rint((double)((double)_mga_priv.image_height * aspect_ratio)); + _mga_priv.orig_height = _mga_priv.image_height; + } + else { + _mga_priv.orig_width = _mga_priv.image_width; + _mga_priv.orig_height = rint((double)((double)_mga_priv.image_width / aspect_ratio)); + } + + + xprintf (VERBOSE|VIDEO, "picture size : %d x %d (Ratio: %d)\n", + width, height, ratio); + + setup_window_mga () ; + + return 1; +} + +static void dispose_image_buffer_mga (vo_image_buffer_t *image) { + + free (image->mem[0]); + free (image); + +} + +static vo_image_buffer_t *alloc_image_buffer_mga () { + + vo_image_buffer_t *image; + + if (!(image = malloc (sizeof (vo_image_buffer_t)))) + return NULL; + + // we only know how to do 4:2:0 planar yuv right now. + // we prepare for YUY2 sizes + if (!(image->mem[0] = malloc (_mga_priv.image_width * _mga_priv.image_height * 2))) { + free(image); + return NULL; + } + + image->mem[2] = image->mem[0] + _mga_priv.image_width * _mga_priv.image_height; + image->mem[1] = image->mem[0] + _mga_priv.image_width * _mga_priv.image_height * 5 / 4; + + pthread_mutex_init (&image->mutex, NULL); + + return image; +} + +static void process_macroblock_mga (vo_image_buffer_t *img, + uint8_t *py, uint8_t *pu, uint8_t *pv, + int slice_offset, + int offset){ +} + +int is_fullscreen_mga () { + return _mga_priv.bFullscreen; +} + +void set_fullscreen_mga (int bFullscreen) { + _mga_priv.bFullscreen = bFullscreen; + + + set_image_format_mga (_mga_priv.image_width, _mga_priv.image_height, _mga_priv.ratio, + _mga_priv.fourcc_format); + +} + + +static void display_frame_mga(vo_image_buffer_t *vo_img) { + + // only write frame if overlay is active (otherwise syncfb hangs) + if (_mga_priv.overlay_state == 1) { + ioctl(_mga_priv.fd,SYNCFB_REQUEST_BUFFER,&_mga_priv.bufinfo); + //printf("get buffer %d\n",_mga_priv.bufinfo.id); + if ( _mga_priv.bufinfo.id == -1 ) { + printf( "Got buffer #%d\n", _mga_priv.bufinfo.id ); + return; + } + + _mga_priv.vid_data = (uint_8 *)(_mga_priv.frame0 + _mga_priv.bufinfo.offset); + + _mga_write_frame_g400(vo_img->mem); + + ioctl(_mga_priv.fd,SYNCFB_COMMIT_BUFFER,&_mga_priv.bufinfo); + } + /* Image is copied so release buffer */ + vo_image_drawn ( (vo_image_buffer_t *) vo_img); +} + +#if 0 +void draw_logo_xv () { + XClearWindow (gDisplay, gXv.window); + + XCopyArea (gDisplay, gXineLogo, gXv.window, gXv.gc, 0, 0, + gXineLogoWidth, gXineLogoHeight, + (gXv.dest_width - gXineLogoWidth)/2, + (gXv.dest_height - gXineLogoHeight)/2); + + XFlush(gDisplay); +} +#endif + +void handle_event_mga (XEvent *event) { + switch (event->type) { + case VisibilityNotify: + if (event->xany.window == _window.clasped_window) { + if (event->xvisibility.state == VisibilityFullyObscured) + { + _window.visibility = 0; + + if (_mga_priv.overlay_state == 1) { + if (ioctl(_mga_priv.fd,SYNCFB_OFF)) { + xprintf(VERBOSE|VIDEO,"Error in OFF ioctl\n"); + } + else + _mga_priv.overlay_state = 0; + } + } + else + { + _window.visibility = 1; + + if (_mga_priv.overlay_state == 0 && !_mga_priv.bLogoMode) { + if (ioctl(_mga_priv.fd,SYNCFB_GET_CONFIG,&_mga_priv.mga_vid_config)) + printf("Error in get_config ioctl\n"); + if (ioctl(_mga_priv.fd,SYNCFB_SET_CONFIG,&_mga_priv.mga_vid_config)) + printf("Error in get_config ioctl\n"); + if (ioctl(_mga_priv.fd,SYNCFB_ON)) { + xprintf(VERBOSE|VIDEO,"Error in ON ioctl\n"); + } + else + _mga_priv.overlay_state = 1; + } + } + } + break; + } +} + +void set_logo_mode_mga (int bLogoMode) { + if (_mga_priv.bLogoMode == bLogoMode) + return; + + _mga_priv.bLogoMode = bLogoMode; + + if (bLogoMode) { + if (ioctl(_mga_priv.fd,SYNCFB_OFF)) { + xprintf(VERBOSE|VIDEO,"Error in OFF ioctl\n"); + } + else + _mga_priv.overlay_state = 0; + } + else { + if (_window.visibility == 1) { + if (ioctl(_mga_priv.fd,SYNCFB_GET_CONFIG,&_mga_priv.mga_vid_config)) + printf("Error in get_config ioctl\n"); + if (ioctl(_mga_priv.fd,SYNCFB_SET_CONFIG,&_mga_priv.mga_vid_config)) + printf("Error in get_config ioctl\n"); + if (ioctl(_mga_priv.fd,SYNCFB_ON)) { + xprintf(VERBOSE|VIDEO,"Error in ON ioctl\n"); + } + else { + _mga_priv.overlay_state = 1; + } + } + } + +#if 0 + XLOCK + if (bLogoMode) + draw_logo_xv (); + else { /* FIXME : Bad hack to reinstall Xv overlay */ + XUnmapWindow(gDisplay, gXv.window); + XMapRaised(gDisplay, gXv.window); + + /* Spark + * move the window back to 0,0 in case the window manager moved it + * do this only is the window is fullscreen or + * we lose the top and left windowborders + */ + if (gXv.bIsFullscreen) { + XMoveWindow(gDisplay, gXv.window, 0, 0); + } + + } + XUNLOCK +#endif +} + +void reset_mga () { +} + +void display_cursor_mga () { +} + +void set_aspect_mga (int ratio) { + + ratio %= 4; + + if (ratio != _mga_priv.user_ratio) { + _mga_priv.user_ratio = ratio; + _mga_priv.user_ratio_changed = 1; + + set_image_format_mga (_mga_priv.image_width, _mga_priv.image_height, _mga_priv.ratio, _mga_priv.fourcc_format); + } +} + +int get_aspect_mga () { + return _mga_priv.user_ratio; +} + +void exit_mga () { +printf("exit mga\n"); + if (ioctl(_mga_priv.fd,SYNCFB_ON)) { + xprintf(VERBOSE|VIDEO,"Error in ON ioctl\n"); + } + if (ioctl(_mga_priv.fd,SYNCFB_OFF)) { + xprintf(VERBOSE|VIDEO,"Error in OFF ioctl\n"); + } + close(_mga_priv.fd); + _mga_priv.fd = -1; +} + +static int get_noop(void) { + return 0; +} + +static int set_noop(int v) { + return v; +} + +/* + * Contrast settings + */ +static int get_contrast_min(void) { + return _mga_priv.cont_min; +} +static int get_current_contrast(void) { + return _mga_priv.cont_current; +} +static int set_contrast(int value) { + + _mga_priv.param.contrast = value; + _mga_priv.param.brightness = _mga_priv.bright_current; + + if (ioctl(_mga_priv.fd,SYNCFB_SET_PARAMS,&_mga_priv.param) == 0) { + _mga_priv.cont_current = _mga_priv.param.contrast; + return _mga_priv.cont_current; + } + return value; +} + +static int get_contrast_max(void) { + return _mga_priv.cont_max; +} + +/* + * Brightness settings + */ +static int get_brightness_min(void) { + return _mga_priv.bright_min; +} +static int get_current_brightness(void) { + return _mga_priv.bright_current; +} +static int set_brightness(int value) { + + _mga_priv.param.brightness = value; + _mga_priv.param.contrast = _mga_priv.cont_current; + + if (ioctl(_mga_priv.fd,SYNCFB_SET_PARAMS,&_mga_priv.param) == 0) { + _mga_priv.bright_current = _mga_priv.param.brightness; + return _mga_priv.bright_current; + } + return value; +} + +static int get_brightness_max(void) { + return _mga_priv.bright_max; +} + +static void reset_settings(void) { + set_contrast(config_file_lookup_int ("contrast", _mga_priv.cont_current)); + set_brightness(config_file_lookup_int ("brightness", _mga_priv.bright_current)); +} + +static void save_settings(void) { + config_file_set_int ("brightness", _mga_priv.bright_current); + config_file_set_int ("contrast", _mga_priv.cont_current); +} + + +static vo_driver_t vo_mga = { + get_capabilities_mga, + set_image_format_mga, + alloc_image_buffer_mga, + dispose_image_buffer_mga, + process_macroblock_mga, + display_frame_mga, + set_fullscreen_mga, + is_fullscreen_mga, + handle_event_mga, + set_logo_mode_mga, + reset_mga, + display_cursor_mga, + set_aspect_mga, + get_aspect_mga, + exit_mga, + /* HUE min, current, set , max */ + get_noop, + get_noop, + set_noop, + get_noop, + + /* SATURATION min, current, set , max */ + get_noop, + get_noop, + set_noop, + get_noop, + + /* Brightness min, current, set , max */ + get_brightness_min, + get_current_brightness, + set_brightness, + get_brightness_max, + + /* Contrast min, current, set , max */ + get_contrast_min, + get_current_contrast, + set_contrast, + get_contrast_max, + + /* Colorkey min , current set */ + get_noop, + get_noop, + set_noop, + reset_settings, + save_settings +}; + + +/* + * connect to server, create and map window, + * allocate colors and (shared) memory + */ + +vo_driver_t *init_video_out_mga () { + + char name[]= "/dev/syncfb"; + + _mga_priv.image_width=720; + _mga_priv.image_height=576; + + + if ((_mga_priv.fd = open ((char *) name, O_RDWR)) < 0) { + xprintf(VERBOSE|VIDEO, "Can't open %s\n", (char *) name); + return 0; + } + + if (ioctl(_mga_priv.fd,SYNCFB_GET_CAPS,&_mga_priv.caps)) { + xprintf(VERBOSE|VIDEO,"Error in config ioctl"); + close(_mga_priv.fd); + return 0; + } + + _mga_priv.vid_data = (char*)mmap(0,_mga_priv.caps.memory_size,PROT_WRITE,MAP_SHARED,_mga_priv.fd,0); + + //clear the buffer +// memset(_mga_priv.vid_data,0,1024*768*2); + + + _mga_priv.frame0 = _mga_priv.vid_data; + + + + /* + * init global variables + */ + + strcpy(_window.title, "Xine syncfb overlay\0"); + _window.visibility = 1; + + lDisplay = XOpenDisplay(":0.0"); /* Xine may run on another Display but syncfb always goes to :0.0 */ +// lDisplay = gDisplay; + _mga_priv.bFullscreen = 0; + _mga_priv.bIsFullscreen = 0; + _mga_priv.image_width = 0; + _mga_priv.image_height = 0; + _mga_priv.ratio = 0; + _mga_priv.bLogoMode = 0; +// _mga_priv.cur_image = NULL; + _mga_priv.user_ratio = ASPECT_AUTO; + _mga_priv.user_ratio_changed = 0 ; + _mga_priv.fourcc_format = 0; + + _window.clasped_window = 0; + _display.default_screen = DefaultScreen(lDisplay); + _mga_priv.cont_min = 0; + _mga_priv.cont_max = 255; + _mga_priv.bright_max = 127; + _mga_priv.bright_min = -128; + + _mga_priv.overlay_state = 0; // 0 = off, 1 = on + + if (ioctl(_mga_priv.fd,SYNCFB_GET_PARAMS,&_mga_priv.param) == 0) { + _mga_priv.cont_current = _mga_priv.param.contrast; + _mga_priv.bright_current = _mga_priv.param.brightness; + } + else { + _mga_priv.cont_current = 0x80; + _mga_priv.bright_current = 0; + xprintf(VERBOSE|VIDEO,"syncfb:Brightness and Contrast control not available, please update your syncfb module"); + printf("syncfb:Brightness and Contrast control not available, please update your syncfb module\n"); + + } + + set_logo_mode_mga(1); + + return &vo_mga; +} + +/* #else *//* no MGA */ + +/* vo_functions_t *init_video_out_xv () { */ +/* fprintf (stderr, "Xvideo support not compiled in\n"); */ +/* return NULL; */ +/* } */ + +/* #endif */ /* HAVE_XV */ diff --git a/src/video_out/video_out_syncfb.h b/src/video_out/video_out_syncfb.h new file mode 100644 index 000000000..96503ca89 --- /dev/null +++ b/src/video_out/video_out_syncfb.h @@ -0,0 +1,240 @@ +#ifndef __LINUX_SYNCFB_H +#define __LINUX_SYNCFB_H + +#ifdef __KERNEL__ +#include <linux/config.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/videodev.h> + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> + +#define TRUE 1 +#define FALSE 0 + +#define SFB_STATUS_FREE 0 +#define SFB_STATUS_OFFS 1 +#define SFB_STATUS_WAIT 2 +#define SFB_STATUS_LIVE 3 + +#endif /* KERNEL */ + + +#ifndef AARONS_TYPES +typedef unsigned long uint_32; +typedef unsigned char uint_8; +#endif + +#define SYNCFB_MAJOR 178 + +#define SYNCFB_ERROR_NO_ERROR 0; +#define SYNCFB_ERROR_NO_BUFFER_AVAILABLE 1; +#define SYNCFB_ERROR_PALETTE_NOT_SUPPORTED 2; +#define SYNCFB_ERROR_NOT_ENOUGH_MEMORY 3; + + + +#ifndef __LINUX_VIDEODEV_H +#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ +#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ +#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ +#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ +#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ +#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ +#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ +#define VIDEO_PALETTE_YUYV 8 +#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ +#define VIDEO_PALETTE_YUV420 10 +#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ +#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ +#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ +#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ +#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ +#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ +#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ +#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ +#endif + + +#define VIDEO_PALETTE_YUV422P3 13 /* YUV 4:2:2 Planar (3 Plane, same as YUV422P) */ +#define VIDEO_PALETTE_YUV422P2 17 /* YUV 4:2:2 Planar (2 Plane) */ + +#define VIDEO_PALETTE_YUV411P3 14 /* YUV 4:1:1 Planar (3 Plane, same as YUV411P) */ +#define VIDEO_PALETTE_YUV411P2 18 /* YUV 4:1:1 Planar (2 Plane) */ + +#define VIDEO_PALETTE_YUV420P3 15 /* YUV 4:2:0 Planar (3 Plane, same as YUV420P) */ +#define VIDEO_PALETTE_YUV420P2 19 /* YUV 4:2:0 Planar (2 Plane) */ + +#define VIDEO_PALETTE_YUV410P3 16 /* YUV 4:1:0 Planar (3 Plane, same as YUV410P) */ +#define VIDEO_PALETTE_YUV410P2 20 /* YUV 4:1:0 Planar (2 Plane) */ + + + +#define SYNCFB_FEATURE_SCALE_H 1 +#define SYNCFB_FEATURE_SCALE_V 2 +#define SYNCFB_FEATURE_SCALE 3 +#define SYNCFB_FEATURE_CROP 4 +#define SYNCFB_FEATURE_OFFSET 8 +#define SYNCFB_FEATURE_DEINTERLACE 16 +#define SYNCFB_FEATURE_PROCAMP 32 +#define SYNCFB_FEATURE_TRANSITIONS 64 +#define SYNCFB_FEATURE_COLKEY 128 +#define SYNCFB_FEATURE_MIRROR_H 256 +#define SYNCFB_FEATURE_MIRROR_V 512 +#define SYNCFB_FEATURE_BLOCK_REQUEST 1024 +#define SYNCFB_FEATURE_FREQDIV2 2048 + + +typedef struct syncfb_config_s +{ + uint_32 syncfb_mode; /* bitfield: turn on/off the available features */ + uint_32 error_code; /* RO: returns 0 on successful config calls, error code otherwise */ + + uint_32 fb_screen_size; /* WO, size in bytes of video memory reserved for fbdev */ + uint_32 fb_screen_width; /* WO, visible screen width in pixel */ + uint_32 fb_screen_height; /* WO, visible screen height in pixel */ + + uint_32 buffers; /* RO, number of available buffers */ + uint_32 buffer_size; /* RO, filled in by syncfb */ + + uint_32 default_repeat; /* default repeat time for a single frame, can be overridden in syncfb_buffer_info_t */ + + uint_32 src_width; /* source image width in pixel */ + uint_32 src_height; /* source image height in pixel */ + uint_32 src_palette; /* set palette mode, see videodev.h for palettes */ + uint_32 src_pitch; /* RO: filled in by ioctl: actual line length in pixel */ + + uint_32 image_xorg; /* x position of the image on the screen */ + uint_32 image_yorg; /* y position of the image on the screen */ + + /* if syncfb has FEATURE_SCALE */ + uint_32 scale_filters; /* 0: no filtering, 255: all filters on */ + uint_32 image_width; /* onscreen image width */ + uint_32 image_height; /* onscreen image height */ + + /* if syncfb has FEATURE_CROP */ + uint_32 src_crop_left; /* */ + uint_32 src_crop_right; /* */ + uint_32 src_crop_top; /* */ + uint_32 src_crop_bot; /* */ + + /* if syncfb has FEATURE_OFFSET */ + uint_32 image_offset_left; /* */ + uint_32 image_offset_right; /* */ + uint_32 image_offset_top; /* */ + uint_32 image_offset_bot; /* */ + + /* if syncfb has FEATURE_COLKEY */ + uint_8 colkey_red; + uint_8 colkey_green; + uint_8 colkey_blue; + +} syncfb_config_t; + + +/* + picture parameters, +*/ +typedef struct syncfb_param_s +{ + /* the idea is to enable smooth transitions between eg. image sizes (not yet implemented) */ + /* if syncfb has FEATURE_TRANSITIONS */ + uint_32 transition_time; + + /* if syncfb has FEATURE_PROCAMP */ + uint_32 contrast; /* 0: least contrast, 1000: normal contrast, */ + uint_32 brightness; + uint_32 color; /* for syncfb_matrox: color=0: b/w else: full color */ + + /* if syncfb has FEATURE_SCALE , currently only supported in CONFIG call */ + uint_8 scale_filters; /* 0: no filtering, 255: all filters on */ + uint_32 image_xorg; /* x position of the image on the screen */ + uint_32 image_yorg; /* y position of the image on the screen */ + uint_32 image_width; /* onscreen image width */ + uint_32 image_height; /* onscreen image height */ + +} syncfb_param_t; + + + +typedef struct syncfb_status_info_s +{ + uint_32 field_cnt; /* basically all vbi's since the start of syncfb */ + uint_32 frame_cnt; /* number of frames comitted & output */ + + uint_32 hold_field_cnt; /* number of repeated fields becaus no new data was available */ + uint_32 skip_field_cnt; /* skipped fields when fifo was about to fill up */ + + uint_32 request_frames; /* number of request_buffer calls */ + uint_32 commit_frames; /* number of commit_buffer calls */ + + uint_32 failed_requests; /* number of calls to request_buffer that failed */ + + uint_32 buffers_waiting; + uint_32 buffers_free; + +} syncfb_status_info_t; + + + + +typedef struct syncfb_capability_s +{ + char name[64]; /* A name for the syncfb ... */ + uint_32 palettes; /* supported palettes - see videodev.h for palettes, test the corresponding bit here */ + uint_32 features; /* supported features - see SYNCFB_FEATURE_* */ + uint_32 memory_size; /* total size of mappable video memory */ + +} syncfb_capability_t; + + + +typedef struct syncfb_buffer_info_s +{ + int id; /* buffer id: a return value of -1 means no buffer available */ + uint_32 repeat; /* the buffer will be shown <repeat> times */ + uint_32 offset; /* buffer offset from start of video memory */ + uint_32 offset_p2; /* yuv plane 2 buffer offset from start of video memory */ + uint_32 offset_p3; /* yuv plane 3 buffer offset from start of video memory */ + +} syncfb_buffer_info_t; + + + + + + + +/* get syncfb capabilities */ +#define SYNCFB_GET_CAPS _IOR('J', 1, syncfb_config_t) + +#define SYNCFB_GET_CONFIG _IOR('J', 2, syncfb_config_t) +#define SYNCFB_SET_CONFIG _IOR('J', 3, syncfb_config_t) +#define SYNCFB_ON _IO ('J', 4) +#define SYNCFB_OFF _IO ('J', 5) +#define SYNCFB_REQUEST_BUFFER _IOR ('J', 6, syncfb_buffer_info_t) +#define SYNCFB_COMMIT_BUFFER _IOR ('J', 7, syncfb_buffer_info_t) +#define SYNCFB_STATUS _IOR ('J', 8, syncfb_status_info_t) +#define SYNCFB_VBI _IO ('J', 9) /* simulate interrupt - debugging only */ +#define SYNCFB_SET_PARAMS _IOR('J', 10, syncfb_param_t) +#define SYNCFB_GET_PARAMS _IOR('J', 11, syncfb_param_t) + + + + +#endif /* __LINUX_SYNCFB_H */ + diff --git a/src/video_out/video_out_xshm.c b/src/video_out/video_out_xshm.c new file mode 100644 index 000000000..61531225f --- /dev/null +++ b/src/video_out/video_out_xshm.c @@ -0,0 +1,968 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: video_out_xshm.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + * + * video_out_xshm.c, X11 shared memory extension interface for xine + * + * based on mpeg2dec code from + * Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * xine-specific code by Guenter Bartsch <bartscgr@studbox.uni-stuttgart.de> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "video_out.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <errno.h> + +#include <X11/extensions/XShm.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <Imlib.h> +#include <pthread.h> + +#include "xine_internal.h" +#include "monitor.h" +#include "libmpeg2/mpeg2.h" +#include "yuv2rgb.h" +#warning "FIXME" +#include "../gui/gui_dnd.h" +#include "../gui/gui_main.h" + +extern uint32_t xine_debug; + +extern int XShmGetEventBase(Display *); + +extern Display *gDisplay; +extern pthread_mutex_t gXLock; +extern Window gVideoWin; +extern ImlibImage *gXineLogoImg; +extern ImlibData *gImlib_data; +extern Pixmap gXineLogo; +extern int gXineLogoWidth, gXineLogoHeight; + +typedef struct xshm_image_info_s { + vo_image_buffer_t mImageBuffer; + XImage *mImage; + XShmSegmentInfo mShminfo; +} xshm_image_buffer_t; + +typedef struct _xshm_globals { + int screen; + Window window; + XVisualInfo vinfo; + int depth,bpp; + int bFullscreen; + int bIsFullscreen; + GC gc; + uint32_t image_width; + uint32_t image_height; + uint32_t image_xoff; + uint32_t image_yoff; + uint32_t dest_width; + uint32_t dest_height; + uint32_t ratio; + int user_ratio, user_ratio_changed; + int bYuvInitialized; + int bytes_per_pixel; + int hstride_rgb, hstride_y, hstride_uv; + int anamorphic; + + + xshm_image_buffer_t *cur_image; + + /* + * misc (read: fun ;)) + */ + int bLogoMode; + +#define HIDE_CURSOR 0 +#define SHOW_CURSOR 1 + Cursor mcursor[2]; + int current_cursor; + + int bFail; + int bUseShm; + DND_struct_t *xdnd; +} xshm_globals; + + +xshm_globals gXshm ; + +int HandleXError (Display *gDisplay, XErrorEvent *xevent) { + + xprintf (VERBOSE|VIDEO, "\n\n*** X ERROR *** \n\n\n"); + + gXshm.bFail = 1; + return 0; + +} + +static void x11_InstallXErrorHandler () +{ + XSetErrorHandler (HandleXError); + XFlush (gDisplay); +} + +static void x11_DeInstallXErrorHandler () +{ + XSetErrorHandler (NULL); + XFlush (gDisplay); +} + +static int get_capabilities_xshm () { + return VO_CAP_COPIES_IMAGE | VO_CAP_YV12; +} + +static unsigned char bm_no_data[] = { 0,0,0,0, 0,0,0,0 }; + +static void create_cursor_xshm (Colormap colormap) { + Pixmap bm_no; + XColor black, dummy; + + /* + * create empty cursor + */ + + bm_no = XCreateBitmapFromData(gDisplay, gXshm.window, + bm_no_data, 8, 8); + + XAllocNamedColor(gDisplay,colormap,"black",&black,&dummy); + gXshm.mcursor[0] = XCreatePixmapCursor(gDisplay, bm_no, bm_no, + &black, &black, + 0, 0); + gXshm.mcursor[1]= XCreateFontCursor(gDisplay, XC_left_ptr); + +} + +static void display_cursor_xshm(int state) { + + XDefineCursor(gDisplay, gXshm.window, gXshm.mcursor[state]); + gXshm.current_cursor = state; +} + +static void setup_window_xshm () { + char *hello = "xine video output"; + Colormap theCmap = 0; + XSizeHints hint; + XWMHints *wm_hint; + XEvent xev; + XGCValues xgcv; + XColor background, ignored; + XSetWindowAttributes attr; + int ww, wh; + float aspect; + Atom WM_DELETE_WINDOW; + XClassHint *xshm_xclasshint; + + XLOCK (); + + + if((xshm_xclasshint = XAllocClassHint()) != NULL) { + xshm_xclasshint->res_name = "Xine Xshm Video"; + xshm_xclasshint->res_class = "Xine"; + } + + if (gXshm.bFullscreen) { + + ww = DisplayWidth (gDisplay, gXshm.screen); + wh = DisplayHeight (gDisplay, gXshm.screen); + + /* + * zoom to fullscreen + */ + + if (gXshm.dest_width < ww) { + aspect = (float) gXshm.dest_width / (float) gXshm.dest_height ; + + gXshm.dest_width = ww; + gXshm.dest_height = ww / aspect; + } + + gXshm.image_xoff = ( ww - gXshm.dest_width) / 2; + gXshm.image_yoff = ( wh - gXshm.dest_height) / 2; + + + if (gXshm.window) { + + if (gXshm.bIsFullscreen) { + XUNLOCK (); + return; + } + + XDestroyWindow(gDisplay, gXshm.window); + gXshm.window = 0; + + } + + gXshm.bIsFullscreen = 1; + + /* + * open fullscreen window + */ + + if (XAllocNamedColor (gDisplay, DefaultColormap (gDisplay, gXshm.screen), + "black", + &background, &ignored) == 0) { + fprintf (stderr, "Cannot allocate color black\n"); + exit(1); + } + + attr.background_pixel = background.pixel; + attr.override_redirect = True; + + gXshm.window = + XCreateWindow (gDisplay, RootWindow (gDisplay, gXshm.screen), 0, 0, + ww, wh, 0, gXshm.depth, + CopyFromParent, DefaultVisual (gDisplay, gXshm.screen), + CWBackPixel|CWOverrideRedirect, &attr); + + if(xshm_xclasshint != NULL) + XSetClassHint(gDisplay, gXshm.window, xshm_xclasshint); + + gVideoWin = gXshm.window; + + } else { + + if (gXshm.window) { + + if (gXshm.bIsFullscreen) { + XDestroyWindow(gDisplay, gXshm.window); + gXshm.window = 0; + } else { + + XResizeWindow (gDisplay, gXshm.window, gXshm.dest_width, + gXshm.dest_height); + + XFlush(gDisplay); + XSync(gDisplay, False); + XUNLOCK (); + return; + + } + } + + gXshm.bIsFullscreen = 0; + + + hint.x = 0; + hint.y = 0; + hint.width = gXshm.dest_width; + hint.height = gXshm.dest_height; + hint.flags = PPosition | PSize; + + theCmap = XCreateColormap(gDisplay, RootWindow(gDisplay,gXshm.screen), + gXshm.vinfo.visual, AllocNone); + + attr.background_pixel = 0; + attr.border_pixel = 1; + attr.colormap = theCmap; + + gXshm.window = XCreateWindow(gDisplay, RootWindow(gDisplay, gXshm.screen), + hint.x, hint.y, hint.width, hint.height, 4, + gXshm.depth,CopyFromParent,gXshm.vinfo.visual, + CWBackPixel | CWBorderPixel |CWColormap,&attr); + + if(xshm_xclasshint != NULL) + XSetClassHint(gDisplay, gXshm.window, xshm_xclasshint); + + gVideoWin = gXshm.window; + + /* Tell other applications about this window */ + + XSetStandardProperties(gDisplay, gXshm.window, hello, hello, + None, NULL, 0, &hint); + + gXshm.image_xoff = 0; + gXshm.image_yoff = 0; + } + + XSelectInput(gDisplay, gXshm.window, StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask); + + wm_hint = XAllocWMHints(); + if (wm_hint != NULL) { + wm_hint->input = True; + wm_hint->initial_state = NormalState; + wm_hint->flags = InputHint | StateHint; + XSetWMHints(gDisplay, gXshm.window, wm_hint); + XFree(wm_hint); + } + + WM_DELETE_WINDOW = XInternAtom(gDisplay, "WM_DELETE_WINDOW", False); + XSetWMProtocols(gDisplay, gXshm.window, &WM_DELETE_WINDOW, 1); + + /* Map window. */ + + XMapWindow(gDisplay, gXshm.window); + + /* Wait for map. */ + do { + XMaskEvent(gDisplay, + StructureNotifyMask, + &xev) ; + } while (xev.type != MapNotify || xev.xmap.event != gXshm.window); + + XFlush(gDisplay); + XSync(gDisplay, False); + + gXshm.gc = XCreateGC(gDisplay, gXshm.window, 0L, &xgcv); + + if (gXshm.bFullscreen) + XSetInputFocus (gDisplay, gXshm.window, RevertToNone, CurrentTime); + + if (gXshm.bFullscreen) { + if (!gXshm.mcursor[0]) { + create_cursor_xshm (theCmap); + } + + //display_cursor_xshm(HIDE_CURSOR); + } + /* drag and drop */ + + XUNLOCK (); + + if(!gXshm.xdnd) + gXshm.xdnd = (DND_struct_t *) malloc(sizeof(DND_struct_t)); + + gui_init_dnd(gXshm.xdnd); + gui_dnd_set_callback (gXshm.xdnd, gui_dndcallback); + gui_make_window_dnd_aware (gXshm.xdnd, gXshm.window); + +} + +/* allocates ximages if necessary */ +static int set_image_format_xshm (uint32_t width, uint32_t height, uint32_t ratio, int format) { + + /* FIXME: handle format argument */ + + if ( (gXshm.image_width == width) + && (gXshm.image_height == height) + && (gXshm.ratio == ratio) + && (gXshm.bFullscreen == gXshm.bIsFullscreen) + && !gXshm.user_ratio_changed ) + return 0; + + gXshm.image_width = width; + gXshm.image_height = height; + gXshm.hstride_rgb = gXshm.dest_width * gXshm.bytes_per_pixel; + gXshm.hstride_y = gXshm.image_width; + gXshm.hstride_uv = gXshm.image_width / 2; + gXshm.ratio = ratio; + gXshm.user_ratio_changed = 0; + + /* + * calculate dest size from ratio + */ + + /* + * Mpeg-2: + */ + + if (gXshm.user_ratio == ASPECT_AUTO) { + switch (gXshm.ratio) { + case 0: /* forbidden */ + fprintf (stderr, "invalid ratio\n"); + exit (1); + break; + case 1: /* square */ + gXshm.dest_width = width; + gXshm.dest_height = height; + gXshm.anamorphic = 0; + break; + case 2: /* 4:3 */ + gXshm.dest_width = width ; + gXshm.dest_height = height; + gXshm.anamorphic = 0; + break; + case 3: /* 16:9 */ + gXshm.dest_width = width * 5/4; + gXshm.dest_height = height; + gXshm.anamorphic = 1; + break; + default: + gXshm.dest_width = width; + gXshm.dest_height = height; + xprintf (VERBOSE|VIDEO, "invalid ratio\n"); + /*exit (1); */ + break; + } + } else if (gXshm.user_ratio == ASPECT_ANAMORPHIC) { + gXshm.dest_width = width *5/4 ; + gXshm.dest_height = height; + gXshm.anamorphic = 1; + } else { + gXshm.dest_width = width; + gXshm.dest_height = height; + gXshm.anamorphic = 0; + } + + + xprintf (VERBOSE|VIDEO, "picture size : %d x %d (Ratio: %d)\n", + width, height, ratio); + + setup_window_xshm () ; + + return 1; +} + +static void dispose_image_buffer_xshm (vo_image_buffer_t *vo_img) { + + xshm_image_buffer_t *img = (xshm_image_buffer_t *) vo_img; + + XLOCK (); + + if (gXshm.bUseShm) { + + XShmDetach(gDisplay, &img->mShminfo); + XDestroyImage(img->mImage); + shmdt(img->mShminfo.shmaddr); + shmctl (img->mShminfo.shmid, IPC_RMID,NULL); + + } else { + + XDestroyImage(img->mImage); + + } + + free (img); + + XUNLOCK (); +} + + +static vo_image_buffer_t *alloc_image_buffer_xshm () { + + uint32_t image_size; + xshm_image_buffer_t *img ; + + XLOCK (); + + img = (xshm_image_buffer_t *) malloc (sizeof (xshm_image_buffer_t)); + + if (img==NULL) { + printf ("out of memory while allocating image buffer\n"); + exit (1); + } + + if (gXshm.bUseShm) { + img->mImage = XShmCreateImage(gDisplay, + gXshm.vinfo.visual, + gXshm.vinfo.depth, + ZPixmap, NULL, + &img->mShminfo, + gXshm.dest_width, + gXshm.dest_height); + + if (img->mImage == NULL ) { + fprintf(stderr, "Shared memory error when allocating image => exit (Ximage error)\n"); + exit (1); + } + + gXshm.bpp = img->mImage->bits_per_pixel; + gXshm.bytes_per_pixel = gXshm.bpp / 8; + gXshm.hstride_rgb = gXshm.dest_width * gXshm.bytes_per_pixel; + img->mShminfo.shmid=shmget(IPC_PRIVATE, + img->mImage->bytes_per_line * img->mImage->height, + IPC_CREAT | 0777); + + if (img->mShminfo.shmid < 0 ) { + printf("%s: allocating image \n",strerror(errno)); + exit (1); + } + + img->mShminfo.shmaddr = (char *) shmat(img->mShminfo.shmid, 0, 0); + + if (img->mShminfo.shmaddr == ((char *) -1)) { + fprintf(stderr, "Shared memory error (address error) when allocating image \n"); + exit (1); + } + + img->mShminfo.readOnly = False; + img->mImage->data = img->mShminfo.shmaddr; + + XShmAttach(gDisplay, &img->mShminfo); + + XSync(gDisplay, False); + /* shmctl(img->mShminfo.shmid, IPC_RMID, 0);*/ + } else { /* no shm */ + + /* + XResizeWindow (gDisplay, gXshm.window, gXshm.dest_width, + gXshm.dest_height); + + XSync(gDisplay, False); + */ + img->mImage = XGetImage (gDisplay, gXshm.window, 0, 0, + gXshm.dest_width, gXshm.dest_height, + AllPlanes, ZPixmap); + XSync(gDisplay, False); + } + + + image_size = gXshm.image_width * gXshm.image_height; + img->mImageBuffer.mem[0] = malloc(image_size); + img->mImageBuffer.mem[1] = malloc(image_size/4); + img->mImageBuffer.mem[2] = malloc(image_size/4); + + if (!gXshm.bYuvInitialized) { + int mode = ((img->mImage->blue_mask & 0x01)) ? MODE_RGB : MODE_BGR; + yuv2rgb_init((gXshm.depth == 24) ? gXshm.bpp : gXshm.depth, mode); + gXshm.bYuvInitialized = 1; + } + + XUNLOCK (); + + pthread_mutex_init (&img->mImageBuffer.mutex, NULL); + + return (vo_image_buffer_t *) img; +} + +static void process_macroblock_xshm (vo_image_buffer_t *img, + uint8_t *py, uint8_t *pu, uint8_t *pv, + int slice_num, + int subslice_num) { + + uint8_t *dst; + int subslice_offset; + xshm_image_buffer_t *xshm_img = (xshm_image_buffer_t *) img; + int slice_offset; + + slice_offset = slice_num * 16 * gXshm.dest_width ; + if (gXshm.anamorphic) + subslice_offset = subslice_num *20 ; + else + subslice_offset = subslice_num *16 ; + + dst = xshm_img->mImage->data + (slice_offset + subslice_offset) * gXshm.bytes_per_pixel ; + + if (gXshm.anamorphic) + yuv2rgb_anamorphic(dst, + py, pu, pv, + gXshm.hstride_rgb, + gXshm.hstride_y, + gXshm.hstride_uv); + else + yuv2rgb(dst, + py, pu, pv, + gXshm.hstride_rgb, + gXshm.hstride_y, + gXshm.hstride_uv); +} + +static int is_fullscreen_xshm () { + return gXshm.bFullscreen; +} + +static void set_fullscreen_xshm (int bFullscreen) { + + xprintf(VERBOSE|VIDEO, "(!)Fullscreen not implemented for xshm.\n"); + + return ; /* FIXME - not implemented for xshm */ + + gXshm.bFullscreen = bFullscreen; + + set_image_format_xshm (gXshm.image_width, gXshm.image_height, gXshm.ratio, IMGFMT_YV12); +} + +static void display_frame_xshm (vo_image_buffer_t *vo_img) { + + xshm_image_buffer_t *img = (xshm_image_buffer_t *) vo_img; + + XLOCK (); + + if (gXshm.bUseShm) { + + XShmPutImage(gDisplay, gXshm.window, gXshm.gc, img->mImage, + 0, 0, 0, 0, + img->mImage->width, img->mImage->height, True); + } else { + + xprintf (VERBOSE|VIDEO, "display frame\n"); + + XPutImage(gDisplay, gXshm.window, gXshm.gc, img->mImage, + 0, 0, 0, 0, + img->mImage->width, img->mImage->height); + } + + XFlush(gDisplay); + + if (gXshm.cur_image) { + vo_image_drawn ( (vo_image_buffer_t *) gXshm.cur_image); + } + + gXshm.cur_image = img; + + XUNLOCK (); +} + +static void draw_logo_xshm () { + ImlibImage *resized_image; + int xwin, ywin, tmp; + unsigned int wwin, hwin, bwin, dwin; + double ratio = 1; + Window rootwin; + + XLOCK ();; + + XClearWindow (gDisplay, gXshm.window); + + if(XGetGeometry(gDisplay, gXshm.window, &rootwin, + &xwin, &ywin, &wwin, &hwin, &bwin, &dwin) != BadDrawable) { + + tmp = (wwin / 100) * 86; + ratio = (tmp < gXineLogoWidth && tmp >= 1) ? + (ratio = (double)tmp / (double)gXineLogoWidth) : 1; + } + + resized_image = Imlib_clone_image(gImlib_data, gXineLogoImg); + Imlib_render (gImlib_data, resized_image, + (int)gXineLogoWidth * ratio, + (int)gXineLogoHeight * ratio); + + XCopyArea (gDisplay, resized_image->pixmap, gXshm.window, gXshm.gc, 0, 0, + resized_image->width, resized_image->height, + (wwin - resized_image->width) / 2, + (hwin - resized_image->height) / 2); + + XFlush(gDisplay); + + Imlib_destroy_image(gImlib_data, resized_image); + + XUNLOCK ();; +} + +static void handle_event_xshm (XEvent *event) { + + switch (event->type) { + case Expose: + if (event->xexpose.window == gXshm.window) { + if (gXshm.bLogoMode) { + draw_logo_xshm (); + } else { + XLOCK (); + XClearWindow (gDisplay, gXshm.window); + if (gXshm.cur_image) + XShmPutImage(gDisplay, gXshm.window, gXshm.gc, + gXshm.cur_image->mImage, + 0, 0, 0, 0, + gXshm.cur_image->mImage->width, + gXshm.cur_image->mImage->height, True); + XUNLOCK (); + } + } + break; + + case ClientMessage: + if(event->xany.window == gXshm.window) + gui_dnd_process_client_message (gXshm.xdnd, event); + break; + } +} + +static void set_logo_mode_xshm (int bLogoMode) { + gXshm.bLogoMode = bLogoMode; + + if (bLogoMode) + draw_logo_xshm (); +} + +static void reset_xshm () { + + if (gXshm.cur_image) { + vo_image_drawn ( (vo_image_buffer_t *) gXshm.cur_image); + gXshm.cur_image = NULL; + } + +} + +void set_aspect_xshm (int ratio) { + + ratio %= 3; /* DVB unsupported */ + + if (ratio != gXshm.user_ratio) { + gXshm.user_ratio = ratio; + gXshm.user_ratio_changed = 1; + + set_image_format_xshm (gXshm.image_width, gXshm.image_height, gXshm.ratio, IMGFMT_YV12); + } + + printf ("set_aspect done\n"); +} + +int get_aspect_xshm () { + return gXshm.user_ratio; +} + +void exit_xshm () { +} + +static int get_noop(void) { + return 0; +} + +static int set_noop(int v) { + return v; +} + +static vo_driver_t vo_xshm = { + get_capabilities_xshm, + set_image_format_xshm, + alloc_image_buffer_xshm, + dispose_image_buffer_xshm, + process_macroblock_xshm, + display_frame_xshm, + set_fullscreen_xshm, + is_fullscreen_xshm, + handle_event_xshm, + set_logo_mode_xshm, + reset_xshm, + display_cursor_xshm, + set_aspect_xshm, + get_aspect_xshm, + exit_xshm, + + get_noop, + get_noop, + set_noop, + get_noop, + + get_noop, + get_noop, + set_noop, + get_noop, + + get_noop, + get_noop, + set_noop, + get_noop, + + get_noop, + get_noop, + set_noop, + get_noop, + + get_noop, + get_noop, + set_noop, + + NULL, + NULL +}; + +/* + * connect to server, create and map window, + * allocate colors and (shared) memory + */ + +vo_driver_t *init_video_out_xshm () { + + int completionType = -1; + XWindowAttributes attribs; + int minor, major; + Bool bPixmaps; + XImage *myimage; + XShmSegmentInfo myshminfo; + + /* XLOCK (); */ + + gXshm.image_width=720; + gXshm.image_height=576; + + /* + * ok, X11, prepare for some video! + */ + + gXshm.screen = DefaultScreen(gDisplay); + + XGetWindowAttributes(gDisplay, DefaultRootWindow(gDisplay), &attribs); + + /* + * + * depth in X11 terminology land is the number of bits used to + * actually represent the colour. + * + * bpp in X11 land means how many bits in the frame buffer per + * pixel. + * + * ex. 15 bit color is 15 bit depth and 16 bpp. Also 24 bit + * color is 24 bit depth, but can be 24 bpp or 32 bpp. + */ + + gXshm.depth = attribs.depth; + + if (gXshm.depth != 15 && gXshm.depth != 16 && gXshm.depth != 24 && gXshm.depth != 32) { + /* The root window may be 8bit but there might still be + * visuals with other bit depths. For example this is the + * case on Sun/Solaris machines. + */ + gXshm.depth = 24; + } + + XMatchVisualInfo(gDisplay, gXshm.screen, gXshm.depth, TrueColor, &gXshm.vinfo); + + gXshm.bUseShm = 1; + + /* + * check for X shared memory support + */ + + if (!XShmQueryExtension(gDisplay)) { + xprintf(VERBOSE|VIDEO, "Shared memory not supported\n\n"); + gXshm.bUseShm = 0; + goto finishShmTesting; + } + + if (!XShmQueryVersion (gDisplay, &major, &minor, &bPixmaps)){ + xprintf(VERBOSE|VIDEO, "Shared memory not supported\n\n"); + gXshm.bUseShm = 0; + goto finishShmTesting; + } + + xprintf (VERBOSE|VIDEO, "pixmaps : %d ?= %d\n", bPixmaps, True); + + if (!bPixmaps) { + xprintf(VERBOSE|VIDEO, "creation of shared pixmaps not possible\n\n"); + gXshm.bUseShm = 0; + goto finishShmTesting; + } + + + completionType = XShmGetEventBase(gDisplay) + ShmCompletion; + + /* try to create shared image */ + gXshm.bFail = 0; + x11_InstallXErrorHandler (); + + myimage = XShmCreateImage(gDisplay, + gXshm.vinfo.visual, + gXshm.vinfo.depth, + ZPixmap, NULL, + &myshminfo, + 100, + 100); + + if (myimage == NULL ) { + xprintf(VERBOSE|VIDEO, "Shared memory error when allocating image => exit (Ximage error)\n"); + gXshm.bUseShm = 0; + x11_DeInstallXErrorHandler(); + goto finishShmTesting; + } + + gXshm.bpp = myimage->bits_per_pixel; + gXshm.bytes_per_pixel = gXshm.bpp / 8; + + myshminfo.shmid=shmget(IPC_PRIVATE, + myimage->bytes_per_line * myimage->height, + IPC_CREAT | 0777); + + if (myshminfo.shmid < 0 ) { + xprintf(VERBOSE|VIDEO, "%s: allocating image \n",strerror(errno)); + gXshm.bUseShm = 0; + x11_DeInstallXErrorHandler(); + goto finishShmTesting; + } + + myshminfo.shmaddr = (char *) shmat(myshminfo.shmid, 0, 0); + + if (myshminfo.shmaddr == ((char *) -1)) { + xprintf(VERBOSE|VIDEO, "Shared memory error (address error) when allocating image \n"); + gXshm.bUseShm = 0; + x11_DeInstallXErrorHandler(); + goto finishShmTesting; + } + + myshminfo.readOnly = False; + myimage->data = myshminfo.shmaddr; + + XShmAttach(gDisplay, &myshminfo); + + XSync(gDisplay, False); + + if (gXshm.bFail) { + xprintf (VERBOSE|VIDEO, "Couldn't create shared memory image\n"); + gXshm.bUseShm = 0; + x11_DeInstallXErrorHandler(); + goto finishShmTesting; + } + + XShmDetach (gDisplay, &myshminfo); + XDestroyImage (myimage); + shmdt (myshminfo.shmaddr); + shmctl (myshminfo.shmid, IPC_RMID, 0); + + x11_DeInstallXErrorHandler(); + + finishShmTesting: + + if (!gXshm.bUseShm) { + printf ("\n\n!!! failed to initialize X shared memory extension. Fall back to plain X11 functions. This is very slow and mostly untested. Are you sure you're using a local graphics device for output? Remote playing via network is not supported by xine!!!\n\n"); + } + + + /* + * init global variables + */ + + gXshm.bFullscreen = 0; + gXshm.bIsFullscreen = 0; + gXshm.ratio = 0; + gXshm.user_ratio = ASPECT_AUTO; + gXshm.user_ratio_changed = 0 ; + gXshm.bLogoMode = 1; + gXshm.bYuvInitialized = 0; + gXshm.cur_image = NULL; + + set_image_format_xshm (720,576,1,IMGFMT_YV12); + + create_cursor_xshm(DefaultColormap(gDisplay, 0)); + gXshm.current_cursor = SHOW_CURSOR; + + /* + * If gVo.depth is 24 then it may either be a 3 or 4 byte per pixel + * format. We can't use bpp because then we would lose the + * distinction between 15/16bit gVo.depth (2 byte formate assumed). + * + * FIXME - change yuv2rgb_init to take both gVo.depth and bpp + * parameters + */ + + if (gXshm.depth>16) + printf ("\n\nWARNING: current display depth is %d. For better performance\na depth of 16 bpp is recommended!\n\n", + gXshm.depth); + + /* yuv2rgb_init((gXshm.depth == 24) ? gXshm.bpp : gXshm.depth,MODE_RGB); */ + + /* XUNLOCK (); */ + + return &vo_xshm; +} diff --git a/src/video_out/video_out_xv.c b/src/video_out/video_out_xv.c new file mode 100644 index 000000000..00cc8c18d --- /dev/null +++ b/src/video_out/video_out_xv.c @@ -0,0 +1,903 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: video_out_xv.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ + * + * video_out_xv.c, X11 video extension interface for xine + * + * based on mpeg2dec code from + * Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * Xv image support by Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + * xine-specific code by Guenter Bartsch <bartscgr@studbox.uni-stuttgart.de> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_XV + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <X11/extensions/XShm.h> +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> +#ifdef HAVE_XINERAMA +#include <X11/extensions/Xinerama.h> +#endif +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> +#include <stdio.h> +#include <stdlib.h> + +#include "video_out.h" + +/* override xprintf definition */ +#define xprintf(LVL, FMT, ARGS...) { printf(FMT, ##ARGS); } + +typedef struct xv_property_s { + int value; + int min; + int max; + Atom atom; + char *key; +} xv_property_t; + +typedef struct xv_frame_s { + vo_frame_t vo_frame; + + int width, height, ratio_code, format; + + XvImage *image; + XShmSegmentInfo shminfo; + +} xv_frame_t; + +typedef struct xv_driver_s { + + vo_driver_t vo_driver; + + config_values_t *config; + + /* X11 / Xv related stuff */ + Display *display; + int screen; + unsigned int xv_format_rgb, xv_format_yv12, xv_format_yuy2; + int depth; + XColor black; + XVisualInfo vinfo; + Window window; + XClassHint *xclasshint; + GC gc; + int CompletionType; + unsigned int xv_port; + + xv_property_t props[VO_NUM_PROPERTIES]; + uint32_t capabilities; + + xv_frame_t *cur_frame; + + /* size / aspect ratio calculations */ + int delivered_width; /* everything is set up for these frame dimensions */ + int delivered_height; /* the dimension as they come from the decoder */ + int delivered_ratio_code; + double ratio_factor; /* output frame must fullfill: height = width * ratio_factor */ + int output_width; /* frames will appear in this size (pixels) on screen */ + int output_height; + int output_xoffset; + int output_yoffset; + + /* display anatomy */ + double display_ratio; /* calced from display resolution */ + int fullscreen_width; /* this is basically how big the screen is */ + int fullscreen_height; + int in_fullscreen; /* is the window in fullscreen mode? */ +} xv_driver_t; + + +static uint32_t xv_get_capabilities (vo_driver_t *this_gen) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + + return this->capabilities; +} + +static vo_frame_t *xv_alloc_frame (vo_driver_t *this_gen) { + + xv_frame_t *frame ; + + frame = (xv_frame_t *) malloc (sizeof (xv_frame_t)); + memset (frame, 0, sizeof(xv_frame_t)); + + if (frame==NULL) { + printf ("xv_alloc_frame: out of memory\n"); + } + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + return (vo_frame_t *) frame; +} + +static void xv_update_frame_format (vo_driver_t *this_gen, vo_frame_t *frame_gen, + uint32_t width, uint32_t height, int ratio_code, + int format) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + xv_frame_t *frame = (xv_frame_t *) frame_gen; + unsigned int xv_format; + + if ((frame->width != width) || (frame->height != height) || (frame->format != format)) { + + XLockDisplay (this->display); + + /* + * (re-) allocate xvimage + */ + + if (frame->image) { + XShmDetach (this->display, &frame->shminfo); + + XFree (frame->image); + shmdt (frame->shminfo.shmaddr); + shmctl (frame->shminfo.shmid, IPC_RMID,NULL); + + frame->image = NULL; + } + + switch (format) { + case IMGFMT_YV12: + xv_format = this->xv_format_yv12; + break; + case IMGFMT_RGB: + xv_format = this->xv_format_rgb; + break; + case IMGFMT_YUY2: + xv_format = this->xv_format_yuy2; + break; + default: + fprintf (stderr, "xv_update_frame_format: unknown format %08x\n",format); + exit (1); + } + + frame->image = XvShmCreateImage(this->display, this->xv_port, xv_format, 0, + width, height, &frame->shminfo); + + if (frame->image == NULL ) { + fprintf(stderr, "xv_image_format: XvShmCreateImage failed.\n"); + exit (1); + } + + frame->shminfo.shmid=shmget(IPC_PRIVATE, + frame->image->data_size, + IPC_CREAT | 0777); + + if (frame->image->data_size==0) { + fprintf(stderr, "xv_update_frame_format: XvShmCreateImage returned a zero size\n"); + exit (1); + } + + if (frame->shminfo.shmid < 0 ) { + perror("xv_update_frame_format: shared memory error in shmget: "); + exit (1); + } + + frame->shminfo.shmaddr = (char *) shmat(frame->shminfo.shmid, 0, 0); + + if (frame->shminfo.shmaddr == NULL) { + fprintf(stderr, "xv_update_frame_format: shared memory error (address error NULL)\n"); + exit (1); + } + + if (frame->shminfo.shmaddr == ((char *) -1)) { + fprintf(stderr, "xv_update_frame_format: shared memory error (address error)\n"); + exit (1); + } + + frame->shminfo.readOnly = False; + frame->image->data = frame->shminfo.shmaddr; + + XShmAttach(this->display, &frame->shminfo); + + XSync(this->display, False); + shmctl(frame->shminfo.shmid, IPC_RMID, 0); + + frame->vo_frame.base[0] = frame->image->data; + frame->vo_frame.base[1] = frame->image->data + width * height * 5 / 4; + frame->vo_frame.base[2] = frame->image->data + width * height; + + frame->width = width; + frame->height = height; + frame->format = format; + + XUnlockDisplay (this->display); + } + + frame->ratio_code = ratio_code; +} + +static void xv_calc_format (xv_driver_t *this, int width, int height, int ratio_code) { + + double image_ratio, desired_ratio; + + this->delivered_width = width; + this->delivered_height = height; + this->delivered_ratio_code = ratio_code; + + /* + * aspect ratio calculation + */ + + image_ratio = (double) this->delivered_width / (double) this->delivered_height; + + xprintf (VERBOSE | VIDEO, "display_ratio : %f\n",this->display_ratio); + xprintf (VERBOSE | VIDEO, "stream aspect ratio : %f , code : %d\n", image_ratio, ratio_code); + + switch (this->props[VO_PROP_ASPECT_RATIO].value) { + case ASPECT_AUTO: + switch (ratio_code) { + case 3: /* anamorphic */ + desired_ratio = 16.0 /9.0; + break; + case 42: /* probably non-mpeg stream => don't touch aspect ratio */ + desired_ratio = image_ratio; + break; + case 0: /* forbidden */ + fprintf (stderr, "invalid ratio, using 4:3\n"); + case 1: /* "square" => 4:3 */ + case 2: /* 4:3 */ + default: + xprintf (VIDEO, "unknown aspect ratio (%d) in stream => using 4:3\n", ratio_code); + desired_ratio = 4.0 / 3.0; + break; + } + break; + case ASPECT_ANAMORPHIC: + desired_ratio = 16.0 / 9.0; + break; + case ASPECT_DVB: + desired_ratio = 2.0 / 1.0; + break; + default: + desired_ratio = 4.0 / 3.0; + } + + /* this->ratio_factor = display_ratio * desired_ratio / image_ratio ; */ + this->ratio_factor = this->display_ratio * desired_ratio; + + + /* + * calc output frame size + */ + + if (this->props[VO_PROP_FULLSCREEN].value) { + + if ( ((double) this->fullscreen_width / this->ratio_factor) < this->fullscreen_height ) { + + this->output_width = this->fullscreen_width ; + this->output_height = (double) this->fullscreen_width / this->ratio_factor ; + this->output_xoffset = 0; + this->output_yoffset = (this->fullscreen_height - this->output_height) / 2; + + } else { + + this->output_width = (double) this->fullscreen_height * this->ratio_factor ; + this->output_height = this->fullscreen_height; + this->output_xoffset = (this->fullscreen_width - this->output_width) / 2; + this->output_yoffset = 0; + } + + } else { + + double corr_factor = this->ratio_factor / image_ratio ; + + + if (corr_factor >= 1.0) { + this->output_width = this->delivered_width * corr_factor; + this->output_height = this->delivered_height ; + } + else { + this->output_width = this->delivered_width; + this->output_height = this->delivered_height / corr_factor; + } + + /* little hack to zoom mpeg1 / other small streams by default*/ + if (this->output_width<600) { + this->output_width *=2; + this->output_height *=2; + } + + this->output_xoffset = 0; + this->output_yoffset = 0; + } +} + +typedef struct +{ + int flags; + int functions; + int decorations; + int input_mode; + int status; +} MWMHints; + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 + +static void xv_setup_window (xv_driver_t *this) { + + static char *window_title = "xine Xv video output"; + XSizeHints hint; + XWMHints *wm_hint; + XSetWindowAttributes attr; + Atom prop; + Atom wm_delete_window; + MWMHints mwmhints; + XEvent xev; + XGCValues xgcv; + + XLockDisplay (this->display); + + if (this->props[VO_PROP_FULLSCREEN].value) { + + if (this->window) { + + if (this->in_fullscreen) { + XUnlockDisplay (this->display); + return; + } + + XDestroyWindow(this->display, this->window); + this->window = 0; + + } + + this->in_fullscreen = 1; + + /* + * open fullscreen window + */ + + attr.background_pixel = this->black.pixel; + + this->window = XCreateWindow (this->display, RootWindow (this->display, this->screen), + 0, 0, this->fullscreen_width, this->fullscreen_height, + 0, this->depth, CopyFromParent, this->vinfo.visual, + CWBackPixel, &attr); + + if (this->xclasshint != NULL) + XSetClassHint(this->display, this->window, this->xclasshint); + + /* + * wm, no borders please + */ + + prop = XInternAtom(this->display, "_MOTIF_WM_HINTS", False); + mwmhints.flags = MWM_HINTS_DECORATIONS; + mwmhints.decorations = 0; + XChangeProperty(this->display, this->window, prop, prop, 32, + PropModeReplace, (unsigned char *) &mwmhints, + PROP_MWM_HINTS_ELEMENTS); + XSetTransientForHint(this->display, this->window, None); + XRaiseWindow(this->display, this->window); + + } else { + + if (this->window) { + + if (this->in_fullscreen) { + XDestroyWindow(this->display, this->window); + this->window = 0; + } else { + + XResizeWindow (this->display, this->window, + this->output_width, this->output_height); + + XUnlockDisplay (this->display); + + return; + + } + } + + this->in_fullscreen = 0; + + hint.x = 0; + hint.y = 0; + hint.width = this->output_width; + hint.height = this->output_height; + hint.flags = PPosition | PSize; + + /* + theCmap = XCreateColormap(gDisplay, RootWindow(gDisplay,gXv.screen), + gXv.vinfo.visual, AllocNone); */ + + attr.background_pixel = this->black.pixel; + attr.border_pixel = 1; + /* attr.colormap = theCmap; */ + + + this->window = XCreateWindow(this->display, RootWindow(this->display, this->screen), + hint.x, hint.y, hint.width, hint.height, 4, + this->depth, CopyFromParent, this->vinfo.visual, + CWBackPixel | CWBorderPixel , &attr); + + if (this->xclasshint != NULL) + XSetClassHint(this->display, this->window, this->xclasshint); + + + /* Tell other applications about this window */ + + XSetStandardProperties(this->display, this->window, window_title, window_title, + None, NULL, 0, &hint); + + } + + XSelectInput(this->display, this->window, StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask); + + wm_hint = XAllocWMHints(); + if (wm_hint != NULL) { + wm_hint->input = True; + wm_hint->initial_state = NormalState; + wm_hint->flags = InputHint | StateHint; + XSetWMHints(this->display, this->window, wm_hint); + XFree(wm_hint); + } + + wm_delete_window = XInternAtom(this->display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(this->display, this->window, &wm_delete_window, 1); + + /* Map window. */ + + XMapRaised(this->display, this->window); + + /* Wait for map. */ + + do { + XMaskEvent(this->display, + StructureNotifyMask, + &xev) ; + } while (xev.type != MapNotify || xev.xmap.event != this->window); + + XFlush(this->display); + XSync(this->display, False); + + this->gc = XCreateGC(this->display, this->window, 0L, &xgcv); + + if (this->in_fullscreen) { + XSetInputFocus (this->display, this->window, RevertToNone, CurrentTime); + XMoveWindow (this->display, this->window, 0, 0); + } + + XUnlockDisplay (this->display); + + /* drag and drop FIXME: move this to the GUI */ + + /* + if(!gXv.xdnd) + gXv.xdnd = (DND_struct_t *) malloc(sizeof(DND_struct_t)); + + gui_init_dnd(gXv.xdnd); + gui_dnd_set_callback (gXv.xdnd, gui_dndcallback); + gui_make_window_dnd_aware (gXv.xdnd, gXv.window); + */ + + /* + * make cursor disappear + */ + + /* FIXME: implement in a clean way + + Cursor not already created. + if(gXv.current_cursor == -1) { + create_cursor_xv(theCmap); + gXv.current_cursor = SHOW_CURSOR; + }; + */ +} + +static void xv_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + xv_frame_t *frame = (xv_frame_t *) frame_gen; + + if ( (frame->width != this->delivered_width) || (frame->height != this->delivered_height) + || (frame->ratio_code != this->delivered_ratio_code) ) { + + xv_calc_format (this, frame->width, frame->height, frame->ratio_code); + xv_setup_window (this); + } + + XLockDisplay (this->display); + + XvShmPutImage(this->display, this->xv_port, this->window, this->gc, frame->image, + 0, 0, frame->width, frame->height, + this->output_xoffset, this->output_yoffset, + this->output_width, this->output_height, False); + + + XFlush(this->display); + + XUnlockDisplay (this->display); + + /* FIXME: this should be done using the completion event */ + if (this->cur_frame) { + this->cur_frame->vo_frame.displayed (&this->cur_frame->vo_frame); + } + + this->cur_frame = frame; +} + +static int xv_get_property (vo_driver_t *this_gen, int property) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + + return this->props[property].value; + +} + +static int xv_set_property (vo_driver_t *this_gen, + int property, int value) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + + if (this->props[property].atom) { + XvSetPortAttribute (this->display, this->xv_port, + this->props[property].atom, value); + XvGetPortAttribute (this->display, this->xv_port, + this->props[property].atom, + &this->props[property].value); + + this->config->set_int (this->config, this->props[property].key, + this->props[property].value); + + return this->props[property].value; + } else { + /* FIXME: implement these props + switch (property) { + case VO_PROP_WINDOW_VISIBLE: + break; + case VO_PROP_CURSOR_VISIBLE: + break; + case VO_PROP_FULLSCREEN: + break; + case VO_PROP_INTERLACED: + break; + case VO_PROP_ASPECT_RATIO: + break; + } + */ + } + + return value; +} + +static void xv_get_property_min_max (vo_driver_t *this_gen, + int property, int *min, int *max) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + + *min = this->props[property].min; + *max = this->props[property].max; +} + +static void xv_handle_event (vo_driver_t *this_gen, void *event_gen) { + + /* FIXME: implement */ + +} + +static void* xv_get_window (vo_driver_t *this_gen) { + xv_driver_t *this = (xv_driver_t *) this_gen; + + return &this->window; +} + +static void xv_set_logo_mode (vo_driver_t *this_gen, int show_logo) { + + /* FIXME: implement */ +} + +static void xv_exit (vo_driver_t *this_gen) { + + xv_driver_t *this = (xv_driver_t *) this_gen; + + if(XvUngrabPort (this->display, this->xv_port, CurrentTime) != Success) { + fprintf(stderr, "xv_exit: XvUngrabPort() failed.\n"); + } +} + +static int xv_check_yv12 (Display *display, XvPortID port) +{ + XvImageFormatValues * formatValues; + int formats; + int i; + + formatValues = XvListImageFormats (display, port, &formats); + for (i = 0; i < formats; i++) + if ((formatValues[i].id == IMGFMT_YV12) && + (! (strcmp (formatValues[i].guid, "YV12")))) { + XFree (formatValues); + return 0; + } + XFree (formatValues); + return 1; +} + +static void xv_check_capability (xv_driver_t *this, uint32_t capability, int property, + XvAttribute attr, int base_id, char *str_prop) { + + int nDefault; + + this->capabilities |= capability; + this->props[property].min = attr.min_value; + this->props[property].max = attr.max_value; + this->props[property].atom = XInternAtom (this->display, str_prop, False); + this->props[property].key = str_prop; + + XvGetPortAttribute (this->display, this->xv_port, this->props[property].atom, &nDefault); + + xv_set_property (&this->vo_driver, property, this->config->lookup_int (this->config, str_prop, nDefault) ); +} + +vo_driver_t *init_video_out_xv (Display *display, config_values_t *config) { + + xv_driver_t *this; + unsigned int adaptor_num, adaptors, i, j, formats; + unsigned int ver,rel,req,ev,err; + unsigned int xv_port; + XvAttribute *attr; + XvAdaptorInfo *adaptor_info; + XvImageFormatValues *fo; + int nattr; + double res_h, res_v; + XColor ignored; + XWindowAttributes attribs; + int dummy_a, dummy_b; +#ifdef HAVE_XINERAMA + int screens; + XineramaScreenInfo *screeninfo = NULL; +#endif + + /* + * check for Xvideo support + */ + + if (Success != XvQueryExtension(display,&ver,&rel,&req,&ev,&err)) { + printf ("video_out_xv: Xv extension not present.\n"); + return NULL; + } + + /* + * check adaptors, search for one that supports (at least) yuv12 + */ + + if (Success != XvQueryAdaptors(display,DefaultRootWindow(display), + &adaptors,&adaptor_info)) { + printf("video_out_xv: XvQueryAdaptors failed.\n"); + return NULL; + } + + xv_port = 0; + adaptor_num = 0; + + while ( (adaptor_num < adaptors) && !xv_port) { + if (adaptor_info[adaptor_num].type & XvImageMask) + for (j = 0; j < adaptor_info[adaptor_num].num_ports; j++) + if (( !(xv_check_yv12 (display, adaptor_info[adaptor_num].base_id + j))) + && (XvGrabPort (display, adaptor_info[adaptor_num].base_id + j, 0) == Success)) { + xv_port = adaptor_info[adaptor_num].base_id + j; + break; + } + + adaptor_num++; + } + + if (!xv_port) { + printf ("video_out_xv: Xv extension is present but I couldn't find a usable yuv12 port.\n"); + printf (" Looks like your graphics hardware driver doesn't support Xv?!\n"); + XvFreeAdaptorInfo (adaptor_info); + return NULL; + } else + printf ("video_out_xv: using Xv port %d for hardware colorspace conversion and scaling.\n", xv_port); + + + /* + * from this point on, nothing should go wrong anymore; so let's start initializing this driver + */ + + this = malloc (sizeof (xv_driver_t)); + memset (this, 0, sizeof(xv_driver_t)); + + this->display = display; + this->screen = DefaultScreen(display); + this->xv_port = xv_port; + this->capabilities = 0; + this->config = config; + + this->vo_driver.get_capabilities = xv_get_capabilities; + this->vo_driver.alloc_frame = xv_alloc_frame; + this->vo_driver.update_frame_format = xv_update_frame_format; + this->vo_driver.display_frame = xv_display_frame; + this->vo_driver.get_property = xv_get_property; + this->vo_driver.set_property = xv_set_property; + this->vo_driver.get_property_min_max = xv_get_property_min_max; + this->vo_driver.handle_event = xv_handle_event; + this->vo_driver.get_window = xv_get_window; + this->vo_driver.set_logo_mode = xv_set_logo_mode; + this->vo_driver.exit = xv_exit; + + if (XAllocNamedColor (display, DefaultColormap (display, this->screen), + "black", &this->black, &ignored) == 0) { + fprintf (stderr, "video_out_xv: cannot allocate color black\n"); + exit(1); + } + + XGetWindowAttributes(display, DefaultRootWindow(display), &attribs); + + this->depth = attribs.depth; + + if (this->depth != 15 && this->depth != 16 && this->depth != 24 && this->depth != 32) { + /* The root window may be 8bit but there might still be + * visuals with other bit depths. For example this is the + * case on Sun/Solaris machines. + */ + this->depth = 24; + } + + XMatchVisualInfo(display, this->screen, this->depth, TrueColor, &this->vinfo); + + /* + * init properties + */ + + for (i=0; i<VO_NUM_PROPERTIES; i++) { + this->props[i].value = 0; + this->props[i].min = 0; + this->props[i].max = 0; + this->props[i].atom = 0; + } + + this->props[VO_PROP_WINDOW_VISIBLE].value = 1; + this->props[VO_PROP_CURSOR_VISIBLE].value = 1; + this->props[VO_PROP_FULLSCREEN].value = 0; + this->props[VO_PROP_INTERLACED].value = 0; + this->props[VO_PROP_ASPECT_RATIO].value = ASPECT_AUTO; + + /* + * check this adaptor's capabilities + */ + + attr = XvQueryPortAttributes(display, adaptor_info[i].base_id, &nattr); + if(attr && nattr) { + int k; + + for(k = 0; k < nattr; k++) { + + if(attr[k].flags & XvSettable) { + if(!strcmp(attr[k].name, "XV_HUE")) + xv_check_capability (this, VO_CAP_HUE, VO_PROP_HUE, attr[k], + adaptor_info[i].base_id, "XV_HUE"); + else if(!strcmp(attr[k].name, "XV_SATURATION")) + xv_check_capability (this, VO_CAP_SATURATION, VO_PROP_SATURATION, attr[k], + adaptor_info[i].base_id, "XV_SATURATION"); + else if(!strcmp(attr[k].name, "XV_BRIGHTNESS")) + xv_check_capability (this, VO_CAP_BRIGHTNESS, VO_PROP_BRIGHTNESS, attr[k], + adaptor_info[i].base_id, "XV_BRIGHTNESS"); + else if(!strcmp(attr[k].name, "XV_CONTRAST")) + xv_check_capability (this, VO_CAP_CONTRAST, VO_PROP_CONTRAST, attr[k], + adaptor_info[i].base_id, "XV_CONTRAST"); + else if(!strcmp(attr[k].name, "XV_COLORKEY")) + xv_check_capability (this, VO_CAP_COLORKEY, VO_PROP_COLORKEY, attr[k], + adaptor_info[i].base_id, "XV_COLORKEY"); + } + + XFree(attr); + } + } else { + printf("video_out_xv: no port attributes defined.\n"); + } + + XvFreeAdaptorInfo (adaptor_info); + + /* + * check supported image formats + */ + + fo = XvListImageFormats(display, this->xv_port, (int*)&formats); + + this->xv_format_yv12 = 0; + this->xv_format_yuy2 = 0; + this->xv_format_rgb = 0; + + for(i = 0; i < formats; i++) { + xprintf(VERBOSE|VIDEO, "video_out_xv: Xv image format: 0x%x (%4.4s) %s\n", + fo[i].id, (char*)&fo[i].id, + (fo[i].format == XvPacked) ? "packed" : "planar"); + if (fo[i].id == IMGFMT_YV12) { + this->xv_format_yv12 = fo[i].id; + this->capabilities |= VO_CAP_YV12; + printf ("video_out_xv: this adaptor supports the yv12 format.\n"); + } else if (fo[i].id == IMGFMT_YUY2) { + this->xv_format_yuy2 = fo[i].id; + this->capabilities |= VO_CAP_YUY2; + printf ("video_out_xv: this adaptor supports the yuy2 format.\n"); + } else if (fo[i].id == IMGFMT_RGB) { + this->xv_format_rgb = fo[i].id; + this->capabilities |= VO_CAP_RGB; + printf ("video_out_xv: this adaptor supports the rgb format.\n"); + } + } + + /* + * find out screen dimensions + */ + +#ifdef HAVE_XINERAMA + /* Spark + * some Xinerama stuff + * I want to figure out what fullscreen means for this setup + */ + + if ((XineramaQueryExtension (display, &dummy_a, &dummy_b)) + && (screeninfo = XineramaQueryScreens(display, &screens))) { + /* Xinerama Detected */ + xprintf (VERBOSE|VIDEO, + "Display is using Xinerama with %d screens\n", screens); + xprintf (VERBOSE|VIDEO, + " going to assume we are using the first screen.\n"); + xprintf (VERBOSE|VIDEO, " size of the first screen is %dx%d.\n", + screeninfo[0].width, screeninfo[0].height); + + if (XineramaIsActive(display)) { + this->fullscreen_width = screeninfo[0].width; + this->fullscreen_height = screeninfo[0].height; + } else { + this->fullscreen_width = DisplayWidth (display, this->screen); + this->fullscreen_height = DisplayHeight (display, this->screen); + } + + } else { + /* no Xinerama */ + xprintf (VERBOSE|VIDEO, "Display is not using Xinerama.\n"); + } +#else + this->fullscreen_width = DisplayWidth (display, this->screen); + this->fullscreen_height = DisplayHeight (display, this->screen); +#endif + + res_h = (this->fullscreen_width*1000 / DisplayWidthMM (display, this->screen)); + res_v = (this->fullscreen_height*1000 / DisplayHeightMM (display, this->screen)); + this->display_ratio = res_h / res_v; + + /* + * init window + */ + + xv_calc_format (this, 720, 576, 2); + xv_setup_window (this); + + return &this->vo_driver; +} + +#endif diff --git a/src/video_out/yuv2rgb.c b/src/video_out/yuv2rgb.c new file mode 100644 index 000000000..05af7a88b --- /dev/null +++ b/src/video_out/yuv2rgb.c @@ -0,0 +1,465 @@ +/* + * yuv2rgb.c + * Copyright (C) 1999-2001 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * + * mpeg2dec 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. + * + * mpeg2dec is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "yuv2rgb.h" +#include "attributes.h" +#include "cpu_accel.h" +#include "utils.h" + + +const int32_t Inverse_Table_6_9[8][4] = { + {117504, 138453, 13954, 34903}, /* no sequence_display_extension */ + {117504, 138453, 13954, 34903}, /* ITU-R Rec. 709 (1990) */ + {104597, 132201, 25675, 53279}, /* unspecified */ + {104597, 132201, 25675, 53279}, /* reserved */ + {104448, 132798, 24759, 53109}, /* FCC */ + {104597, 132201, 25675, 53279}, /* ITU-R Rec. 624-4 System B, G */ + {104597, 132201, 25675, 53279}, /* SMPTE 170M */ + {117579, 136230, 16907, 35559} /* SMPTE 240M (1987) */ +}; + +static void yuv2rgb_c (yuv2rgb_t *this, uint8_t *image, + uint8_t *py, uint8_t *pu, uint8_t *pv) { + + /* int dy = this->step_dy; */ + int height = this->source_height >>= 1; + + do { + this->yuv2rgb_c_internal (this, py, py + this->y_stride, pu, pv, + image, ((uint8_t *)image) + this->rgb_stride, + this->source_width); + + py += 2 * this->y_stride; + pu += this->uv_stride; + pv += this->uv_stride; + image = ((uint8_t *) image) + 2 * this->rgb_stride; + } while (--height); +} + +int yuv2rgb_setup (yuv2rgb_t *this, + int source_width, int source_height, + int y_stride, int uv_stride, + int dest_width, int dest_height, + int rgb_stride) { + + + this->source_width = source_width; + this->source_height = source_height; + this->y_stride = y_stride; + this->uv_stride = uv_stride; + this->dest_width = dest_width; + this->dest_height = dest_height; + this->rgb_stride = rgb_stride; + + if ((source_width == dest_width) && (source_height == dest_height)) + this->do_scale = 0; + else { + this->do_scale = 1; + + this->step_dx = source_width * 32768 / dest_width; + this->step_dy = source_height * 32768 / dest_height; + + if (this->y_buffer) free (this->y_buffer); + if (this->u_buffer) free (this->u_buffer); + if (this->v_buffer) free (this->v_buffer); + + this->y_buffer = xmalloc_aligned (16, dest_width); + if (!this->y_buffer) + return 0; + this->u_buffer = xmalloc_aligned (16, dest_width); + if (!this->u_buffer) + return 0; + this->v_buffer = xmalloc_aligned (16, dest_width); + if (!this->v_buffer) + return 0; + } + return 1; +} + + +#define RGB(i) \ + U = pu[i]; \ + V = pv[i]; \ + r = this->table_rV[V]; \ + g = (void *) (((uint8_t *)this->table_gU[U]) + this->table_gV[V]); \ + b = this->table_bU[U]; + +#define DST1(i) \ + Y = py_1[2*i]; \ + dst_1[2*i] = r[Y] + g[Y] + b[Y]; \ + Y = py_1[2*i+1]; \ + dst_1[2*i+1] = r[Y] + g[Y] + b[Y]; + +#define DST2(i) \ + Y = py_2[2*i]; \ + dst_2[2*i] = r[Y] + g[Y] + b[Y]; \ + Y = py_2[2*i+1]; \ + dst_2[2*i+1] = r[Y] + g[Y] + b[Y]; + +#define DST1RGB(i) \ + Y = py_1[2*i]; \ + dst_1[6*i] = r[Y]; dst_1[6*i+1] = g[Y]; dst_1[6*i+2] = b[Y]; \ + Y = py_1[2*i+1]; \ + dst_1[6*i+3] = r[Y]; dst_1[6*i+4] = g[Y]; dst_1[6*i+5] = b[Y]; + +#define DST2RGB(i) \ + Y = py_2[2*i]; \ + dst_2[6*i] = r[Y]; dst_2[6*i+1] = g[Y]; dst_2[6*i+2] = b[Y]; \ + Y = py_2[2*i+1]; \ + dst_2[6*i+3] = r[Y]; dst_2[6*i+4] = g[Y]; dst_2[6*i+5] = b[Y]; + +#define DST1BGR(i) \ + Y = py_1[2*i]; \ + dst_1[6*i] = b[Y]; dst_1[6*i+1] = g[Y]; dst_1[6*i+2] = r[Y]; \ + Y = py_1[2*i+1]; \ + dst_1[6*i+3] = b[Y]; dst_1[6*i+4] = g[Y]; dst_1[6*i+5] = r[Y]; + +#define DST2BGR(i) \ + Y = py_2[2*i]; \ + dst_2[6*i] = b[Y]; dst_2[6*i+1] = g[Y]; dst_2[6*i+2] = r[Y]; \ + Y = py_2[2*i+1]; \ + dst_2[6*i+3] = b[Y]; dst_2[6*i+4] = g[Y]; dst_2[6*i+5] = r[Y]; + +static void yuv2rgb_c_32 (yuv2rgb_t *this, + uint8_t * py_1, uint8_t * py_2, + uint8_t * pu, uint8_t * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + uint32_t * r, * g, * b; + uint32_t * dst_1, * dst_2; + + width >>= 3; + dst_1 = _dst_1; + dst_2 = _dst_2; + + do { + RGB(0); + DST1(0); + DST2(0); + + RGB(1); + DST2(1); + DST1(1); + + RGB(2); + DST1(2); + DST2(2); + + RGB(3); + DST2(3); + DST1(3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 8; + dst_2 += 8; + } while (--width); +} + +/* This is very near from the yuv2rgb_c_32 code */ +static void yuv2rgb_c_24_rgb (yuv2rgb_t *this, + uint8_t * py_1, uint8_t * py_2, + uint8_t * pu, uint8_t * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + uint8_t * r, * g, * b; + uint8_t * dst_1, * dst_2; + + width >>= 3; + dst_1 = _dst_1; + dst_2 = _dst_2; + + do { + RGB(0); + DST1RGB(0); + DST2RGB(0); + + RGB(1); + DST2RGB(1); + DST1RGB(1); + + RGB(2); + DST1RGB(2); + DST2RGB(2); + + RGB(3); + DST2RGB(3); + DST1RGB(3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 24; + dst_2 += 24; + } while (--width); +} + +/* only trivial mods from yuv2rgb_c_24_rgb */ +static void yuv2rgb_c_24_bgr (yuv2rgb_t *this, + uint8_t * py_1, uint8_t * py_2, + uint8_t * pu, uint8_t * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + uint8_t * r, * g, * b; + uint8_t * dst_1, * dst_2; + + width >>= 3; + dst_1 = _dst_1; + dst_2 = _dst_2; + + do { + RGB(0); + DST1BGR(0); + DST2BGR(0); + + RGB(1); + DST2BGR(1); + DST1BGR(1); + + RGB(2); + DST1BGR(2); + DST2BGR(2); + + RGB(3); + DST2BGR(3); + DST1BGR(3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 24; + dst_2 += 24; + } while (--width); +} + +/* This is exactly the same code as yuv2rgb_c_32 except for the types of */ +/* r, g, b, dst_1, dst_2 */ +static void yuv2rgb_c_16 (yuv2rgb_t *this, + uint8_t * py_1, uint8_t * py_2, + uint8_t * pu, uint8_t * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + uint16_t * r, * g, * b; + uint16_t * dst_1, * dst_2; + + width >>= 3; + dst_1 = _dst_1; + dst_2 = _dst_2; + + do { + RGB(0); + DST1(0); + DST2(0); + + RGB(1); + DST2(1); + DST1(1); + + RGB(2); + DST1(2); + DST2(2); + + RGB(3); + DST2(3); + DST1(3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 8; + dst_2 += 8; + } while (--width); +} + +static int div_round (int dividend, int divisor) +{ + if (dividend > 0) + return (dividend + (divisor>>1)) / divisor; + else + return -((-dividend + (divisor>>1)) / divisor); +} + +static void yuv2rgb_c_init (yuv2rgb_t *this, int mode) +{ + int i; + uint8_t table_Y[1024]; + uint32_t * table_32 = 0; + uint16_t * table_16 = 0; + uint8_t * table_8 = 0; + int entry_size = 0; + void *table_r = 0, *table_g = 0, *table_b = 0; + + int crv = Inverse_Table_6_9[this->matrix_coefficients][0]; + int cbu = Inverse_Table_6_9[this->matrix_coefficients][1]; + int cgu = -Inverse_Table_6_9[this->matrix_coefficients][2]; + int cgv = -Inverse_Table_6_9[this->matrix_coefficients][3]; + + this->yuv2rgb_fun = yuv2rgb_c; + + for (i = 0; i < 1024; i++) { + int j; + + j = (76309 * (i - 384 - 16) + 32768) >> 16; + j = (j < 0) ? 0 : ((j > 255) ? 255 : j); + table_Y[i] = j; + } + + switch (mode) { + case MODE_32_RGB: + case MODE_32_BGR: + this->yuv2rgb_c_internal = yuv2rgb_c_32; + + table_32 = malloc ((197 + 2*682 + 256 + 132) * sizeof (uint32_t)); + + entry_size = sizeof (uint32_t); + table_r = table_32 + 197; + table_b = table_32 + 197 + 685; + table_g = table_32 + 197 + 2*682; + + for (i = -197; i < 256+197; i++) + ((uint32_t *) table_r)[i] = + table_Y[i+384] << ((mode==MODE_32_RGB) ? 16 : 0); + for (i = -132; i < 256+132; i++) + ((uint32_t *) table_g)[i] = table_Y[i+384] << 8; + for (i = -232; i < 256+232; i++) + ((uint32_t *) table_b)[i] = + table_Y[i+384] << ((mode==MODE_32_RGB) ? 0 : 16); + break; + + case MODE_24_RGB: + case MODE_24_BGR: + this->yuv2rgb_c_internal = (mode==MODE_24_RGB) ? yuv2rgb_c_24_rgb : yuv2rgb_c_24_bgr; + + table_8 = malloc ((256 + 2*232) * sizeof (uint8_t)); + + entry_size = sizeof (uint8_t); + table_r = table_g = table_b = table_8 + 232; + + for (i = -232; i < 256+232; i++) + ((uint8_t * )table_b)[i] = table_Y[i+384]; + break; + + case MODE_15_BGR: + case MODE_16_BGR: + case MODE_15_RGB: + case MODE_16_RGB: + this->yuv2rgb_c_internal = yuv2rgb_c_16; + + table_16 = malloc ((197 + 2*682 + 256 + 132) * sizeof (uint16_t)); + + entry_size = sizeof (uint16_t); + table_r = table_16 + 197; + table_b = table_16 + 197 + 685; + table_g = table_16 + 197 + 2*682; + + for (i = -197; i < 256+197; i++) { + int j = table_Y[i+384] >> 3; + + if (mode == MODE_16_RGB) + j <<= 11; + else if (mode == MODE_15_RGB) + j <<= 10; + + ((uint16_t *)table_r)[i] = j; + } + for (i = -132; i < 256+132; i++) { + int j = table_Y[i+384] >> (((mode==MODE_16_RGB) || (mode==MODE_16_BGR)) ? 2 : 3); + + ((uint16_t *)table_g)[i] = j << 5; + } + for (i = -232; i < 256+232; i++) { + int j = table_Y[i+384] >> 3; + + if (mode == MODE_16_BGR) + j <<= 11; + if (mode == MODE_15_BGR) + j <<= 10; + + ((uint16_t *)table_b)[i] = j; + } + break; + + default: + fprintf (stderr, "mode %d not supported by yuv2rgb\n", mode); + exit (1); + } + + for (i = 0; i < 256; i++) { + this->table_rV[i] = (((uint8_t *) table_r) + + entry_size * div_round (crv * (i-128), 76309)); + this->table_gU[i] = (((uint8_t *) table_g) + + entry_size * div_round (cgu * (i-128), 76309)); + this->table_gV[i] = entry_size * div_round (cgv * (i-128), 76309); + this->table_bU[i] = (((uint8_t *)table_b) + + entry_size * div_round (cbu * (i-128), 76309)); + } +} + +yuv2rgb_t *yuv2rgb_init (int mode) { + + uint32_t mm = mm_accel(); + yuv2rgb_t *this = xmalloc (sizeof (yuv2rgb_t)); + + + this->matrix_coefficients = 6; + + this->y_buffer = NULL; + this->u_buffer = NULL; + this->v_buffer = NULL; + + /* + * auto-probe for the best yuv2rgb function + */ + + this->yuv2rgb_fun = NULL; +#ifdef ARCH_X86 + if ((this->yuv2rgb_fun == NULL) && (mm & MM_ACCEL_X86_MMXEXT)) { + yuv2rgb_init_mmxext (this, mode); + if (this->yuv2rgb_fun != NULL) + fprintf (stderr, "Using MMXEXT for colorspace transform\n"); + } + if ((this->yuv2rgb_fun == NULL) && (mm & MM_ACCEL_X86_MMX)) { + yuv2rgb_init_mmx (this, mode); + if (this->yuv2rgb_fun != NULL) + fprintf (stderr, "Using MMX for colorspace transform\n"); + } +#endif + if (this->yuv2rgb_fun == NULL) { + fprintf (stderr, "No accelerated colorspace conversion found\n"); + yuv2rgb_c_init (this, mode); + } + return this; +} diff --git a/src/video_out/yuv2rgb.h b/src/video_out/yuv2rgb.h new file mode 100644 index 000000000..51de2ae01 --- /dev/null +++ b/src/video_out/yuv2rgb.h @@ -0,0 +1,81 @@ + +#ifndef HAVE_YUV2RGB_H +#define HAVE_YUV2RGB_h + +#include <inttypes.h> + +/* + * modes supported - feel free to implement yours + */ + +#define MODE_15_RGB 1 +#define MODE_15_BGR 2 +#define MODE_16_RGB 3 +#define MODE_16_BGR 4 +#define MODE_24_RGB 5 +#define MODE_24_BGR 6 +#define MODE_32_RGB 7 +#define MODE_32_BGR 8 + +typedef struct yuv2rgb_s yuv2rgb_t; + +struct yuv2rgb_s { + + /* + * this is the function to call for the yuv2rgb and scaling process + */ + void (*yuv2rgb_fun) (yuv2rgb_t *this, uint8_t * image, uint8_t * py, + uint8_t * pu, uint8_t * pv) ; + + /* private stuff below */ + + uint32_t matrix_coefficients; + int source_width, source_height; + int y_stride, uv_stride; + int dest_width, dest_height; + int rgb_stride; + int step_dx, step_dy; + int do_scale; + uint8_t *y_buffer; + uint8_t *u_buffer; + uint8_t *v_buffer; + + void *table_rV[256]; + void *table_gU[256]; + int table_gV[256]; + void *table_bU[256]; + + void (* yuv2rgb_c_internal) (yuv2rgb_t *this, + uint8_t *, uint8_t *, + uint8_t *, uint8_t *, + void *, void *, int); +} ; + + +/* call once on startup */ +yuv2rgb_t *yuv2rgb_init (int mode); + +/* + * set up yuv2rgb function, determine scaling parameters if necessary + * returns 0 on failure, 1 otherwise + */ +int yuv2rgb_setup (yuv2rgb_t *this, + int source_width, int source_height, + int y_stride, int uv_stride, + int dest_width, int dest_height, + int rgb_stride); + +/* + * internal stuff below this line + */ + +void yuv2rgb_init_mmxext (yuv2rgb_t *this, int mode); +void yuv2rgb_init_mmx (yuv2rgb_t *this, int mode); + +/* +void Color565DitherYV12MMX1X(unsigned char *lum, unsigned char *cr, + unsigned char *cb, unsigned char *out, + int rows, int cols, int mod ); +*/ + +#endif diff --git a/src/video_out/yuv2rgb_mmx.c b/src/video_out/yuv2rgb_mmx.c new file mode 100644 index 000000000..8ab3b050d --- /dev/null +++ b/src/video_out/yuv2rgb_mmx.c @@ -0,0 +1,537 @@ +/* + * yuv2rgb_mmx.c + * Copyright (C) 2000-2001 Silicon Integrated System Corp. + * All Rights Reserved. + * + * Author: Olie Lho <ollie@sis.com.tw> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * + * mpeg2dec 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. + * + * mpeg2dec is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef ARCH_X86 + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "attributes.h" +#include "cpu_accel.h" +#include "yuv2rgb.h" + +#define CPU_MMXEXT 0 +#define CPU_MMX 1 + +/* CPU_MMXEXT/CPU_MMX adaptation layer */ + +#define movntq(src,dest) \ +do { \ + if (cpu == CPU_MMXEXT) \ + movntq_r2m (src, dest); \ + else \ + movq_r2m (src, dest); \ +} while (0) + +static inline void mmx_yuv2rgb (uint8_t * py, uint8_t * pu, uint8_t * pv) +{ + static mmx_t mmx_80w = {0x0080008000800080}; + static mmx_t mmx_U_green = {0xf37df37df37df37d}; + static mmx_t mmx_U_blue = {0x4093409340934093}; + static mmx_t mmx_V_red = {0x3312331233123312}; + static mmx_t mmx_V_green = {0xe5fce5fce5fce5fc}; + static mmx_t mmx_10w = {0x1010101010101010}; + static mmx_t mmx_00ffw = {0x00ff00ff00ff00ff}; + static mmx_t mmx_Y_coeff = {0x253f253f253f253f}; + + movq_m2r (*py, mm6); // mm6 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + pxor_r2r (mm4, mm4); // mm4 = 0 + + psubusb_m2r (mmx_10w, mm6); // Y -= 16 + + + movd_m2r (*pu, mm0); // mm0 = 00 00 00 00 u3 u2 u1 u0 + movq_r2r (mm6, mm7); // mm7 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + + pand_m2r (mmx_00ffw, mm6); // mm6 = Y6 Y4 Y2 Y0 + psrlw_i2r (8, mm7); // mm7 = Y7 Y5 Y3 Y1 + + movd_m2r (*pv, mm1); // mm1 = 00 00 00 00 v3 v2 v1 v0 + psllw_i2r (3, mm6); // promote precision + + pmulhw_m2r (mmx_Y_coeff, mm6); // mm6 = luma_rgb even + psllw_i2r (3, mm7); // promote precision + + punpcklbw_r2r (mm4, mm0); // mm0 = u3 u2 u1 u0 + + psubsw_m2r (mmx_80w, mm0); // u -= 128 + punpcklbw_r2r (mm4, mm1); // mm1 = v3 v2 v1 v0 + + pmulhw_m2r (mmx_Y_coeff, mm7); // mm7 = luma_rgb odd + psllw_i2r (3, mm0); // promote precision + + psubsw_m2r (mmx_80w, mm1); // v -= 128 + movq_r2r (mm0, mm2); // mm2 = u3 u2 u1 u0 + + psllw_i2r (3, mm1); // promote precision + + movq_r2r (mm1, mm4); // mm4 = v3 v2 v1 v0 + + pmulhw_m2r (mmx_U_blue, mm0); // mm0 = chroma_b + + + // slot + + + // slot + + + pmulhw_m2r (mmx_V_red, mm1); // mm1 = chroma_r + movq_r2r (mm0, mm3); // mm3 = chroma_b + + paddsw_r2r (mm6, mm0); // mm0 = B6 B4 B2 B0 + paddsw_r2r (mm7, mm3); // mm3 = B7 B5 B3 B1 + + packuswb_r2r (mm0, mm0); // saturate to 0-255 + + + pmulhw_m2r (mmx_U_green, mm2); // mm2 = u * u_green + + + packuswb_r2r (mm3, mm3); // saturate to 0-255 + + + punpcklbw_r2r (mm3, mm0); // mm0 = B7 B6 B5 B4 B3 B2 B1 B0 + + + pmulhw_m2r (mmx_V_green, mm4); // mm4 = v * v_green + + + // slot + + + // slot + + + paddsw_r2r (mm4, mm2); // mm2 = chroma_g + movq_r2r (mm2, mm5); // mm5 = chroma_g + + + movq_r2r (mm1, mm4); // mm4 = chroma_r + paddsw_r2r (mm6, mm2); // mm2 = G6 G4 G2 G0 + + + packuswb_r2r (mm2, mm2); // saturate to 0-255 + paddsw_r2r (mm6, mm1); // mm1 = R6 R4 R2 R0 + + packuswb_r2r (mm1, mm1); // saturate to 0-255 + paddsw_r2r (mm7, mm4); // mm4 = R7 R5 R3 R1 + + packuswb_r2r (mm4, mm4); // saturate to 0-255 + paddsw_r2r (mm7, mm5); // mm5 = G7 G5 G3 G1 + + + packuswb_r2r (mm5, mm5); // saturate to 0-255 + + + punpcklbw_r2r (mm4, mm1); // mm1 = R7 R6 R5 R4 R3 R2 R1 R0 + + + punpcklbw_r2r (mm5, mm2); // mm2 = G7 G6 G5 G4 G3 G2 G1 G0 +} + +// basic opt +static inline void mmx_unpack_16rgb (uint8_t * image, int cpu) +{ + static mmx_t mmx_bluemask = {0xf8f8f8f8f8f8f8f8}; + static mmx_t mmx_greenmask = {0xfcfcfcfcfcfcfcfc}; + static mmx_t mmx_redmask = {0xf8f8f8f8f8f8f8f8}; + + /* + * convert RGB plane to RGB 16 bits + * mm0 -> B, mm1 -> R, mm2 -> G + * mm4 -> GB, mm5 -> AR pixel 4-7 + * mm6 -> GB, mm7 -> AR pixel 0-3 + */ + + pand_m2r (mmx_bluemask, mm0); // mm0 = b7b6b5b4b3______ + pxor_r2r (mm4, mm4); // mm4 = 0 + + pand_m2r (mmx_greenmask, mm2); // mm2 = g7g6g5g4g3g2____ + psrlq_i2r (3, mm0); // mm0 = ______b7b6b5b4b3 + + movq_r2r (mm2, mm7); // mm7 = g7g6g5g4g3g2____ + movq_r2r (mm0, mm5); // mm5 = ______b7b6b5b4b3 + + pand_m2r (mmx_redmask, mm1); // mm1 = r7r6r5r4r3______ + punpcklbw_r2r (mm4, mm2); + + punpcklbw_r2r (mm1, mm0); + + psllq_i2r (3, mm2); + + punpckhbw_r2r (mm4, mm7); + por_r2r (mm2, mm0); + + psllq_i2r (3, mm7); + + movntq (mm0, *image); + punpckhbw_r2r (mm1, mm5); + + por_r2r (mm7, mm5); + + // U + // V + + movntq (mm5, *(image+8)); +} + +static inline void mmx_unpack_32rgb (uint8_t * image, int cpu) +{ + /* + * convert RGB plane to RGB packed format, + * mm0 -> B, mm1 -> R, mm2 -> G, mm3 -> 0, + * mm4 -> GB, mm5 -> AR pixel 4-7, + * mm6 -> GB, mm7 -> AR pixel 0-3 + */ + + pxor_r2r (mm3, mm3); + movq_r2r (mm0, mm6); + + punpcklbw_r2r (mm2, mm6); + movq_r2r (mm1, mm7); + + punpcklbw_r2r (mm3, mm7); + movq_r2r (mm0, mm4); + + punpcklwd_r2r (mm7, mm6); + movq_r2r (mm1, mm5); + + /* scheduling: this is hopeless */ + movntq (mm6, *image); + movq_r2r (mm0, mm6); + punpcklbw_r2r (mm2, mm6); + punpckhwd_r2r (mm7, mm6); + movntq (mm6, *(image+8)); + punpckhbw_r2r (mm2, mm4); + punpckhbw_r2r (mm3, mm5); + punpcklwd_r2r (mm5, mm4); + movntq (mm4, *(image+16)); + movq_r2r (mm0, mm4); + punpckhbw_r2r (mm2, mm4); + punpckhwd_r2r (mm5, mm4); + movntq (mm4, *(image+24)); +} + +static inline void mmx_unpack_24rgb (uint8_t * image, int cpu) +{ + /* + * convert RGB plane to RGB packed format, + * mm0 -> B, mm1 -> R, mm2 -> G, mm3 -> 0, + * mm4 -> GB, mm5 -> AR pixel 4-7, + * mm6 -> GB, mm7 -> AR pixel 0-3 + */ + + pxor_r2r (mm3, mm3); + movq_r2r (mm0, mm6); + + punpcklbw_r2r (mm2, mm6); + movq_r2r (mm1, mm7); + + punpcklbw_r2r (mm3, mm7); + movq_r2r (mm0, mm4); + + punpcklwd_r2r (mm7, mm6); + movq_r2r (mm1, mm5); + + /* scheduling: this is hopeless */ + movntq (mm6, *image); + movq_r2r (mm0, mm6); + punpcklbw_r2r (mm2, mm6); + punpckhwd_r2r (mm7, mm6); + movntq (mm6, *(image+8)); + punpckhbw_r2r (mm2, mm4); + punpckhbw_r2r (mm3, mm5); + punpcklwd_r2r (mm5, mm4); + movntq (mm4, *(image+16)); +} + +static void scale_line (uint8_t *source, uint8_t *dest, + int width, int step) { + + int p1; + int p2; + int dx; + + p1 = *source++; + p2 = *source++; + dx = 0; + + while (width) { + + /* + printf ("scale_line, width = %d\n", width); + printf ("scale_line, dx = %d, p1 = %d, p2 = %d\n", dx, p1, p2); + */ + + *dest = (p1 * (32768 - dx) + p2 * dx) / 32768; + + dx += step; + while (dx > 32768) { + dx -= 32768; + p1 = p2; + p2 = *source++; + } + + dest ++; + width --; + } + +} + + +static inline void yuv420_rgb16 (yuv2rgb_t *this, + uint8_t * image, + uint8_t * py, uint8_t * pu, uint8_t * pv, + int cpu) +{ + int i; + int rgb_stride = this->rgb_stride; + int y_stride = this->y_stride; + int uv_stride = this->uv_stride; + int width = this->source_width; + int height = this->source_height; + + rgb_stride -= 2 * this->dest_width; + width >>= 3; + + if (!this->do_scale) { + y_stride -= width; + uv_stride -= width >> 1; + + do { + + i = width; + do { + mmx_yuv2rgb (py, pu, pv); + mmx_unpack_16rgb (image, cpu); + py += 8; + pu += 4; + pv += 4; + image += 16; + } while (--i); + + py += y_stride; + image += rgb_stride; + if (height & 1) { + pu += uv_stride; + pv += uv_stride; + } else { + pu -= 4 * width; + pv -= 4 * width; + } + } while (--height); + + } else { + + uint8_t *y_buf, *u_buf, *v_buf; + + scale_line (pu, this->u_buffer, + this->dest_width >> 1, this->step_dx); + scale_line (pv, this->v_buffer, + this->dest_width >> 1, this->step_dx); + + do { + + y_buf = this->y_buffer; + u_buf = this->u_buffer; + v_buf = this->v_buffer; + + scale_line (py, y_buf, + this->dest_width, this->step_dx); + + + i = this->dest_width >> 3; + do { + /* printf ("i : %d\n",i); */ + + mmx_yuv2rgb (y_buf, u_buf, v_buf); + mmx_unpack_16rgb (image, cpu); + y_buf += 8; + u_buf += 4; + v_buf += 4; + image += 16; + } while (--i); + + py += y_stride; + image += rgb_stride; + + if (height & 1) { + pu += uv_stride; + pv += uv_stride; + + scale_line (pu, this->u_buffer, + this->dest_width >> 1, this->step_dx); + scale_line (pv, this->v_buffer, + this->dest_width >> 1, this->step_dx); + + } + + } while (--height); + } +} + +static inline void yuv420_argb32 (yuv2rgb_t *this, + uint8_t * image, uint8_t * py, + uint8_t * pu, uint8_t * pv, int cpu) +{ + int i; + int rgb_stride = this->rgb_stride; + int y_stride = this->y_stride; + int uv_stride = this->uv_stride; + int width = this->source_width; + int height = this->source_height; + + rgb_stride -= 4 * this->dest_width; + width >>= 3; + + if (!this->do_scale) { + y_stride -= width; + uv_stride -= width >> 1; + + do { + i = width; + do { + mmx_yuv2rgb (py, pu, pv); + mmx_unpack_32rgb (image, cpu); + py += 8; + pu += 4; + pv += 4; + image += 32; + } while (--i); + + py += y_stride; + image += rgb_stride; + if (height & 1) { + pu += uv_stride; + pv += uv_stride; + } else { + pu -= 4 * width; + pv -= 4 * width; + } + } while (--height); + } else { + uint8_t *y_buf, *u_buf, *v_buf; + + scale_line (pu, this->u_buffer, + this->dest_width >> 1, this->step_dx); + scale_line (pv, this->v_buffer, + this->dest_width >> 1, this->step_dx); + + do { + + y_buf = this->y_buffer; + u_buf = this->u_buffer; + v_buf = this->v_buffer; + + scale_line (py, y_buf, + this->dest_width, this->step_dx); + + + i = this->dest_width >> 3; + do { + /* printf ("i : %d\n",i); */ + + mmx_yuv2rgb (y_buf, u_buf, v_buf); + mmx_unpack_32rgb (image, cpu); + y_buf += 8; + u_buf += 4; + v_buf += 4; + image += 32; + } while (--i); + + py += y_stride; + image += rgb_stride; + + if (height & 1) { + pu += uv_stride; + pv += uv_stride; + + scale_line (pu, this->u_buffer, + this->dest_width >> 1, this->step_dx); + scale_line (pv, this->v_buffer, + this->dest_width >> 1, this->step_dx); + + } + + } while (--height); + + } +} + +static void mmxext_rgb16 (yuv2rgb_t *this, uint8_t * image, + uint8_t * py, uint8_t * pu, uint8_t * pv) +{ + yuv420_rgb16 (this, image, py, pu, pv, CPU_MMXEXT); +} + +static void mmxext_argb32 (yuv2rgb_t *this, uint8_t * image, + uint8_t * py, uint8_t * pu, uint8_t * pv) +{ + yuv420_argb32 (this, image, py, pu, pv, CPU_MMXEXT); +} + +static void mmx_rgb16 (yuv2rgb_t *this, uint8_t * image, + uint8_t * py, uint8_t * pu, uint8_t * pv) +{ + yuv420_rgb16 (this, image, py, pu, pv, CPU_MMX); +} + +static void mmx_argb32 (yuv2rgb_t *this, uint8_t * image, + uint8_t * py, uint8_t * pu, uint8_t * pv) +{ + yuv420_argb32 (this, image, py, pu, pv, CPU_MMX); +} + +void yuv2rgb_init_mmxext (yuv2rgb_t *this, int mode) +{ + switch (mode) { + case MODE_16_RGB: + this->yuv2rgb_fun = mmxext_rgb16; + break; + case MODE_32_RGB: + this->yuv2rgb_fun = mmxext_argb32; + break; + } +} + +void yuv2rgb_init_mmx (yuv2rgb_t *this, int mode) +{ + switch (mode) { + case MODE_16_RGB: + this->yuv2rgb_fun = mmx_rgb16; + break; + case MODE_32_RGB: + this->yuv2rgb_fun = mmx_argb32; + break; + } +} + + +#endif + + diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am index a58623dce..ef533c055 100644 --- a/src/xine-engine/Makefile.am +++ b/src/xine-engine/Makefile.am @@ -7,9 +7,11 @@ CFLAGS = @GLOBAL_CFLAGS@ lib_LTLIBRARIES = libxine.la ##libxine_la_SOURCES = xine.c buffer.c metronom.c configfile.c \ -## monitor.c utils.c audio_decoder.c video_decoder.c load_plugins.c -libxine_la_SOURCES = metronom.c configfile.c buffer.c monitor.c utils.c cpu_accel.c \ - load_plugins.c video_decoder.c audio_decoder.c +## monitor.c utils.c audio_decoder.c video_decoder.c load_plugins.c \ +## audio_out.c +libxine_la_SOURCES = metronom.c configfile.c buffer.c monitor.c utils.c \ + cpu_accel.c load_plugins.c video_decoder.c audio_decoder.c \ + video_out.c libxine_la_DEPENDENCIES = libsdeps libxine_la_LIBADD = \ ## $(top_srcdir)/src/libmpeg2/libmpeg2.la \ @@ -25,6 +27,7 @@ libxine_la_LDFLAGS = -version-info 5:0:5 # monitor.h cpu_accel.h attributes.h utils.h audio_decoder.h noinst_HEADERS = xine_internal.h buffer.h metronom.h configfile.h \ monitor.h cpu_accel.h attributes.h utils.h +include_HEADERS = audio_out.h video_out.h ### # Hardcoded rule: diff --git a/include/audio_out.h b/src/xine-engine/audio_out.h index 3e0118325..5688645b2 100644 --- a/include/audio_out.h +++ b/src/xine-engine/audio_out.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.h,v 1.1 2001/04/18 22:36:42 f1rmb Exp $ + * $Id: audio_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ */ #ifndef HAVE_AUDIO_OUT_H #define HAVE_AUDIO_OUT_H diff --git a/include/video_out.h b/src/xine-engine/video_out.h index 8e733bde2..a6ed08231 100644 --- a/include/video_out.h +++ b/src/xine-engine/video_out.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: video_out.h,v 1.6 2001/04/23 22:43:59 f1rmb Exp $ + * $Id: video_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $ * * * xine version of video_out.h |