diff --git a/.gitignore b/.gitignore index a3d6d2480062b..7d5682a654dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -561,6 +561,7 @@ lib/cmyth/Makefile /xbmc/cores/dvdplayer/Makefile /xbmc/cores/amlplayer/Makefile +/xbmc/cores/omxplayer/Makefile # /lib/ffmpeg/ /lib/ffmpeg/config.h diff --git a/Makefile.in b/Makefile.in index 1cea93f3c4c01..cef82b880a23d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -151,6 +151,10 @@ ifeq (@USE_AMLPLAYER@,1) DIRECTORY_ARCHIVES += xbmc/cores/amlplayer/amlplayer.a endif +ifeq (@USE_OMXPLAYER@,1) +DIRECTORY_ARCHIVES += xbmc/cores/omxplayer/omxplayer.a +endif + PAPCODECS_DIRS= \ lib/xbadpcm \ lib/nosefart \ diff --git a/Makefile.include.in b/Makefile.include.in index a517dd269ee1c..704a4bc7f2c84 100644 --- a/Makefile.include.in +++ b/Makefile.include.in @@ -40,6 +40,10 @@ ifneq (@USE_EXTERNAL_FFMPEG@,1) endif INCLUDES+=-I@abs_top_srcdir@/xbmc/linux INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer +ifeq (@USE_OMXPLAYER@,1) +INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/AudioEngine +INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/AudioEngine/Utils +endif DEFINES+= \ @ARCH_DEFINES@ \ -D_FILE_DEFINED \ diff --git a/configure.in b/configure.in index 4c4b1252d1eee..41d3633ee8c88 100644 --- a/configure.in +++ b/configure.in @@ -154,7 +154,8 @@ libusb_disabled="== libusb disabled. Plug and play USB device support will not b libusb_disabled_udev_found="== libusb disabled. ==" libcec_enabled="== libcec enabled. ==" libcec_disabled="== libcec disabled. CEC adapter support will not be available. ==" -libcec_disabled_missing_libs="== libcec disabled because both libudev and libusb are not available. CEC adapter support will not be available. ==" +libcec_disabled_missing_libs="== libcec disabled because it either needs libudev, or libusb a compatible version of the RPi API. CEC adapter support will not be available. ==" +cec_rpi_api_missing="== no compatible RPi API found ==" # External library message strings external_libraries_enabled="== Use of all supported external libraries enabled. ==" @@ -184,6 +185,12 @@ AC_ARG_WITH([arch], [use_arch=$withval], [use_arch=no]) +AC_ARG_WITH([platform], + [AS_HELP_STRING([--with-platform], + [use a pre-configured config for common arm boards])], + [use_platform=$withval], + [use_platform=none]) + AC_ARG_WITH([cpu], [AS_HELP_STRING([--with-cpu], [build with given cpu passing to ffmpeg (default is no)])], @@ -475,7 +482,7 @@ AC_ARG_ENABLE([libcap], AC_ARG_ENABLE([player], [AS_HELP_STRING([--enable-player], - [enable additional players from a list of comma separated names, (default is none, choices are amlplayer)])], + [enable additional players from a list of comma separated names, (default is none, choices are amlplayer, omxplayer)])], [add_players=$enableval], [add_players=no]) @@ -639,11 +646,37 @@ if test "$target_platform" = "target_android" ; then AC_DEFINE(HAS_EGLGLES, [1], [Define if supporting EGL based GLES Framebuffer]) fi +case $use_platform in + raspberry-pi) + target_platform=target_raspberry_pi + use_neon=no + use_texturepacker=yes + use_texturepacker_native=yes + use_arch="arm" + use_cpu=arm1176jzf-s + use_armv5te="yes" + use_armv6t2="no" + use_armv6="yes" + use_armvfp="yes" + use_hardcoded_tables="yes" + use_alsa="no" + USE_TEXTUREPACKER_NATIVE_ROOT="$TEXTUREPACKER_NATIVE_ROOT" + ARCH="arm" + AC_SUBST(ARCH_DEFINES, "-DTARGET_POSIX -DTARGET_LINUX -D_LINUX -D_ARMEL -DTARGET_RASPBERRY_PI") + AC_DEFINE(HAS_EGLGLES, [1], [Define if supporting EGL based GLES Framebuffer]) + USE_OMXLIB=1; AC_DEFINE([HAVE_OMXLIB],[1],["Define to 1 if OMX libs is enabled"]) + CFLAGS="$CFLAGS" + CXXFLAGS="$CXXFLAGS" + ffmpeg_target_os=linux + ;; +esac + if test "$build_shared_lib" = "yes"; then final_message="$final_message\n Shared lib\tYes" AC_SUBST(USE_LIBXBMC,1) fi + # platform debug flags if test "$use_debug" = "yes"; then final_message="$final_message\n Debugging:\tYes" @@ -670,8 +703,13 @@ CXXFLAGS="$CXXFLAGS $DEBUG_FLAGS" if test "$use_optimizations" = "yes"; then final_message="$final_message\n Optimization:\tYes" - CXXFLAGS="$CXXFLAGS -O2" - CFLAGS="$CFLAGS -O2" + if test "$target_platform" = "target_raspberry_pi"; then + CXXFLAGS="$CXXFLAGS" + CFLAGS="$CFLAGS" + else + CXXFLAGS="$CXXFLAGS -O2" + CFLAGS="$CFLAGS -O2" + fi else final_message="$final_message\n Optimization:\tNo" fi @@ -703,6 +741,9 @@ if test "$host_vendor" = "apple" ; then LIBS="$LIBS -framework QuartzCore" fi USE_EXTERNAL_FFMPEG=1 +elif test "$target_platform" = "target_raspberry_pi"; then + ARCH="arm" + use_arch="arm" elif test "$use_arch" = "arm"; then CFLAGS="$CFLAGS -mno-apcs-stack-check" CXXFLAGS="$CXXFLAGS -mno-apcs-stack-check" @@ -792,8 +833,15 @@ if test "$use_gles" = "yes"; then AC_DEFINE([HAVE_LIBGLESV2],[1],["Define to 1 if you have the `GLESv2' library (-lGLESv2)."]) AC_MSG_RESULT(== WARNING: OpenGLES support is assumed.) else - AC_CHECK_LIB([EGL], [main],, AC_MSG_ERROR($missing_library)) - AC_CHECK_LIB([GLESv2],[main],, AC_MSG_ERROR($missing_library)) + if test "$target_platform" = "target_raspberry_pi"; then + AC_DEFINE([HAVE_LIBEGL],[1],["Define to 1 if you have the `EGL' library (-lEGL)."]) + AC_DEFINE([HAVE_LIBGLESV2],[1],["Define to 1 if you have the `GLESv2' library (-lGLESv2)."]) + AC_MSG_RESULT(== WARNING: OpenGLES support is assumed.) + LIBS="$LIBS -lEGL -lGLESv2 -lbcm_host -lvcos -lvchiq_arm" + else + AC_CHECK_LIB([EGL], [main],, AC_MSG_ERROR($missing_library)) + AC_CHECK_LIB([GLESv2],[main],, AC_MSG_ERROR($missing_library)) + fi fi else if test "$use_gl" = "yes"; then @@ -1353,9 +1401,22 @@ fi # libcec USE_LIBCEC=0 +USE_CEC_RPI_API=0 +use_rpi_cec_api="auto" if test "x$use_libcec" != "xno"; then - # libcec needs libudev or libusb under linux, or the device will never be detected. - if test "$host_vendor" != "apple" && test "$use_libusb" = "no" && test "$use_libudev" = "no"; then + case "${host_cpu}" in + arm*) + echo "will check for RPi support" + AC_CHECK_HEADER(interface/vmcs_host/vc_cec.h,,use_rpi_cec_api="no") + ;; + *) + echo "will not check for RPi support (unsupported cpu: ${host_cpu})" + use_rpi_cec_api="no" + ;; + esac + + # libcec needs libudev, libusb or the RPi API under linux, or the device will never be detected. + if test "$host_vendor" != "apple" && test "$use_libusb" = "no" && test "$use_libudev" = "no" && test "$use_rpi_cec_api" = "no"; then if test "x$use_libcec" != "xauto"; then AC_MSG_ERROR($libcec_disabled_missing_libs) else @@ -1366,11 +1427,20 @@ if test "x$use_libcec" != "xno"; then # libcec is dyloaded, so we need to check for its headers and link any depends. if test "x$use_libcec" != "xno"; then - PKG_CHECK_MODULES([CEC],[libcec >= 1.7.0],,[use_libcec="no";AC_MSG_RESULT($libcec_disabled)]) + if test "x$use_libcec" != "xauto"; then + PKG_CHECK_MODULES([CEC],[libcec >= 1.8.0],,[use_libcec="no";AC_MSG_ERROR($libcec_disabled)]) + else + PKG_CHECK_MODULES([CEC],[libcec >= 1.8.0],,[use_libcec="no";AC_MSG_RESULT($libcec_disabled)]) + fi if test "x$use_libcec" != "xno"; then INCLUDES="$INCLUDES $CEC_CFLAGS" USE_LIBCEC=1;AC_DEFINE([HAVE_LIBCEC],[1],["Define to 1 if libcec is installed"]) + if test "x$use_rpi_cec_api" != "xno"; then + LIBS+=" -lvcos -lvchiq_arm" + AC_DEFINE([HAVE_CEC_RPI_API],[1],["Define to 1 if the CEC RPi API is installed"]) + USE_CEC_RPI_API=1 + fi XB_FIND_SONAME([LIBCEC],[cec],[use_libcec]) AC_MSG_NOTICE($libcec_enabled) else @@ -1629,6 +1699,10 @@ if test "$host_vendor" = "apple" ; then use_openmax="no" USE_OPENMAX=0 AC_MSG_NOTICE($openmax_disabled) +elif test "$target_platform" = "target_raspberry_pi"; then + use_openmax="no" + USE_OPENMAX=0 + AC_MSG_NOTICE($openmax_disabled) else if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then PKG_CHECK_MODULES([OPENMAX], [libomxil-bellagio], @@ -1656,6 +1730,9 @@ case $add_players in AC_CHECK_HEADER([amlplayer/amports/amstream.h],, AC_MSG_ERROR($missing_headers)) XB_ADD_PLAYER([AMLPLAYER], [amlplayer]) ;; + *omxplayer*) + XB_ADD_PLAYER([OMXPLAYER], [omxplayer]) + ;; esac # platform specific bin utilities @@ -2055,6 +2132,11 @@ fi if test "x$use_libcec" != "xno"; then final_message="$final_message\n libcec support:\tYes" + if test "x$use_rpi_cec_api" != "xno"; then + final_message="$final_message\n libcec RPi support:\tYes" + else + final_message="$final_message\n libcec RPi support:\tNo" + fi else final_message="$final_message\n libcec support:\tNo" fi @@ -2125,6 +2207,7 @@ OUTPUT_FILES="Makefile \ xbmc/cores/AudioEngine/Makefile \ xbmc/cores/paplayer/Makefile \ xbmc/cores/amlplayer/Makefile \ + xbmc/cores/omxplayer/Makefile \ lib/timidity/Makefile \ lib/xbadpcm/Makefile \ lib/asap/Makefile \ @@ -2233,6 +2316,7 @@ AC_SUBST(USE_AIRTUNES) AC_SUBST(USE_LIBUDEV) AC_SUBST(USE_LIBUSB) AC_SUBST(USE_LIBCEC) +AC_SUBST(USE_CEC_RPI_API) AC_SUBST(USE_MYSQL) AC_SUBST(USE_WEB_SERVER) AC_SUBST(USE_ANDROID) @@ -2373,6 +2457,10 @@ XB_CONFIG_MODULE([lib/ffmpeg], [ `if test "$use_arch" != "no"; then echo --arch=$use_arch; fi`\ `if test "$use_cpu" != "no"; then echo --cpu=$use_cpu; fi`\ `if test "$use_neon" = "yes"; then echo --enable-neon; else echo --disable-neon; fi`\ + `if test "$use_armv5te" = "yes"; then echo --enable-armv5te; else echo --disable-armv5te; fi`\ + `if test "$use_armv6t2" = "yes"; then echo --enable-armv6t2; else echo --disable-armv6t2; fi`\ + `if test "$use_armv6" = "yes"; then echo --enable-armv6; else echo --disable-armv6; fi`\ + `if test "$use_armvfp" = "yes"; then echo --enable-armvfp; else echo --disable-armvfp; fi`\ --target-os=$ffmpeg_target_os \ --disable-muxers \ --enable-muxer=spdif \ @@ -2398,6 +2486,7 @@ XB_CONFIG_MODULE([lib/ffmpeg], [ --enable-protocol=http \ --enable-pthreads \ --enable-runtime-cpudetect \ + `if test "$use_hardcoded_tables" = "yes"; then echo --enable-hardcoded-tables; else echo --disable-hardcoded-tables; fi`\ `if test "$target_platform" = "target_android" && test "$host_cpu" = "i686"; then echo --disable-mmx; fi #workaround for gcc 4.6 bug` \ `if test "$target_platform" = "target_android"; then echo "--custom-libname-with-major=\\$(SLIBPREF)\\$(FULLNAME)-\\$(LIBMAJOR)-${ARCH}\\$(SLIBSUF)"; \ else echo "--custom-libname-with-major=\\$(FULLNAME)-\\$(LIBMAJOR)-${ARCH}\\$(SLIBSUF)"; fi` \ diff --git a/docs/README.raspberrypi b/docs/README.raspberrypi new file mode 100644 index 0000000000000..57b67f80fb0e3 --- /dev/null +++ b/docs/README.raspberrypi @@ -0,0 +1,35 @@ +TOC +1. Introduction +2. Installing and setting up the buildroot environment + +----------------------------------------------------------------------------- +1. Introduction +----------------------------------------------------------------------------- + +For developemnt you can use a customized buildroot to produce a small +raspberrypi system image which is used to boot the raspberrypi and as +crosscompile environment. + +----------------------------------------------------------------------------- +2. Installing and setting up the buildroot environment +----------------------------------------------------------------------------- + +Create a top level directory where you checkout xbmc and buildroot. + +For example : + +mkdir /opt/xbmc-raspberrypi +cd /opt/xbmc-raspberrypi + +Checkout xbmc : + +git clone https://github.com/xbmc/xbmc.git + +Checkout buildroot : + +git clone https://github.com/huceke/buildroot-rbp.git + +cd /opt/xbmc-raspberrypi/buildroot-rbp + +Follow the instroctions in README.rbp how to build the system and xbmc. + diff --git a/lib/libcec/Makefile b/lib/libcec/Makefile index c1eed1e6ea7f1..b521c14551c34 100644 --- a/lib/libcec/Makefile +++ b/lib/libcec/Makefile @@ -7,7 +7,7 @@ # lib name, version LIBNAME=libcec -VERSION=1.7.1 +VERSION=1.8.1 SOURCE=$(LIBNAME)-$(VERSION) # download location and format diff --git a/lib/libsquish/Makefile.in b/lib/libsquish/Makefile.in index f7b3cc1621f9c..34f93bdeeadb9 100644 --- a/lib/libsquish/Makefile.in +++ b/lib/libsquish/Makefile.in @@ -12,11 +12,6 @@ SRCS= \ squish.cpp CXXFLAGS+=-I. -ifeq ($(findstring powerpc,$(ARCH)),powerpc) - CXXFLAGS+=-DSQUISH_USE_ALTIVEC=1 -maltivec -else ifeq ($(findstring x86,$(ARCH)), x86) - CXXFLAGS+=-DSQUISH_USE_SSE=2 -msse2 -endif LIB=libsquish.a @@ -32,9 +27,9 @@ all: $(LIB) $(NATIVE_LIB) # TexturePacker links to libsquish and needs to run on build system, so make a native flavor. $(NATIVE_LIB): $(SRCS) ifeq ($(findstring Darwin,$(shell uname -s)),Darwin) - g++ $(NATIVE_ARCH) -DSQUISH_USE_SSE=2 -msse2 -I. $(SRCS) -dynamiclib -install_name `pwd`/libsquish-native.so -o $@ + g++ $(NATIVE_ARCH) -I. $(SRCS) -dynamiclib -install_name `pwd`/libsquish-native.so -o $@ else - g++ -DSQUISH_USE_SSE=2 -msse2 -I. $(SRCS) -shared -fPIC -Wl,-soname,`pwd`/libsquish-native.so -o $@ + g++ -I. $(SRCS) -shared -fPIC -Wl,-soname,`pwd`/libsquish-native.so -o $@ endif endif diff --git a/lib/libsquish/config.h b/lib/libsquish/config.h index 2fad5576a9011..00a2d068bfbe6 100644 --- a/lib/libsquish/config.h +++ b/lib/libsquish/config.h @@ -28,13 +28,23 @@ // Set to 1 when building squish to use Altivec instructions. #ifndef SQUISH_USE_ALTIVEC +#if defined(__ALTIVEC__) +#define SQUISH_USE_ALTIVEC 1 +#else #define SQUISH_USE_ALTIVEC 0 #endif +#endif // Set to 1 or 2 when building squish to use SSE or SSE2 instructions. #ifndef SQUISH_USE_SSE +#if defined(__SSE2__) +#define SQUISH_USE_SSE 2 +#elif defined(__SSE__) +#define SQUISH_USE_SSE 1 +#else #define SQUISH_USE_SSE 0 #endif +#endif // Internally set SQUISH_USE_SIMD when either Altivec or SSE is available. #if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE diff --git a/project/BuildDependencies/scripts/libcec_d.txt b/project/BuildDependencies/scripts/libcec_d.txt index 5bead42f9a565..1e827d6b5b315 100644 --- a/project/BuildDependencies/scripts/libcec_d.txt +++ b/project/BuildDependencies/scripts/libcec_d.txt @@ -1,3 +1,3 @@ ; filename source of the file -libcec-1.7.1.zip http://mirrors.xbmc.org/build-deps/win32/ +libcec-1.8.1.zip http://mirrors.xbmc.org/build-deps/win32/ diff --git a/system/peripherals.xml b/system/peripherals.xml index cf0c6c442f9ba..5d2eef31529f9 100644 --- a/system/peripherals.xml +++ b/system/peripherals.xml @@ -9,6 +9,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml index 0267a5c612792..76027b6315ed1 100644 --- a/system/playercorefactory.xml +++ b/system/playercorefactory.xml @@ -11,7 +11,7 @@ - + @@ -19,13 +19,12 @@ - + - diff --git a/tools/android/depends/xbmc/Makefile b/tools/android/depends/xbmc/Makefile index 38ca25689982d..f5de74080ccac 100644 --- a/tools/android/depends/xbmc/Makefile +++ b/tools/android/depends/xbmc/Makefile @@ -17,7 +17,7 @@ CONFIGURE=cp -f $(CONFIG_SUB) $(CONFIG_GUESS) build-aux/ ;\ --enable-neon --enable-gles --enable-debug \ --disable-sdl --disable-x11 --disable-xrandr \ --disable-optical-drive --disable-joystick --disable-optimizations \ - --enable-shared-lib --disable-alsa + --enable-shared-lib --enable-alsa all: $(SOURCE)/libxbmc.so diff --git a/tools/darwin/depends/libcec/Makefile b/tools/darwin/depends/libcec/Makefile index 367386783e107..c516346a239d5 100644 --- a/tools/darwin/depends/libcec/Makefile +++ b/tools/darwin/depends/libcec/Makefile @@ -3,7 +3,7 @@ include ../config.site.mk # lib name, version LIBNAME=libcec -VERSION=1.7.1 +VERSION=1.8.1 SOURCE=$(LIBNAME)-$(VERSION) ARCHIVE=$(SOURCE).tar.gz diff --git a/tools/rbp/depends/Makefile b/tools/rbp/depends/Makefile new file mode 100644 index 0000000000000..0bb195ef7d049 --- /dev/null +++ b/tools/rbp/depends/Makefile @@ -0,0 +1,37 @@ +include ../Makefile.include + +ifeq ($(USE_BUILDROOT),1) + BUILDTOOLS = + SUBDIRS = xbmc +else + BUILDTOOLS = help2man autoconf automake libtool pkg-config yasm cmake patchelf + SUBDIRS = liblzo2 tiff libnfs jasper libplist libshairport tinyxml xbmc +endif + +IMAGENAME = xbmc-rbp-`date +'%y%m%d'` + +.PHONY: buildtools subdirs + +all: buildtools subdirs + +buildtools: + for d in $(BUILDTOOLS); do $(MAKE) -C $$d; done + +subdirs: + for d in $(SUBDIRS); do $(MAKE) -C $$d; done + +clean: + for d in $(BUILDTOOLS); do $(MAKE) -C $$d clean; done + for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done + +distclean:: + for d in $(BUILDTOOLS); do $(MAKE) -C $$d distclean; done + for d in $(SUBDIRS); do $(MAKE) -C $$d distclean; done + rm -rf $(XBMCPREFIX) + +image: + make -C $(BUILDROOT) tarroot IMAGE=$(CURDIR)/$(IMAGENAME) TAR_COMPRESSOR= + echo "S111xbmc S110rc.local" | cat $(TARGETFS)/etc/init.d/rc.deps - > $(CURDIR)/packaging/etc/init.d/rc.deps + cd packaging; tar -uf $(CURDIR)/$(IMAGENAME).tar * + tar -uf $(CURDIR)/$(IMAGENAME).tar $(XBMCPREFIX) + gzip $(IMAGENAME).tar diff --git a/tools/rbp/depends/autoconf/.gitignore b/tools/rbp/depends/autoconf/.gitignore new file mode 100644 index 0000000000000..e832e48ede73e --- /dev/null +++ b/tools/rbp/depends/autoconf/.gitignore @@ -0,0 +1 @@ +autoconf-2.63 diff --git a/tools/rbp/depends/autoconf/Makefile b/tools/rbp/depends/autoconf/Makefile new file mode 100644 index 0000000000000..2a5ca9efdb618 --- /dev/null +++ b/tools/rbp/depends/autoconf/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +# lib name, version +LIBNAME=autoconf +VERSION=2.63 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +export PREFIX=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) + +LIBDYLIB=$(SOURCE)/bin/autoconf + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/automake/.gitignore b/tools/rbp/depends/automake/.gitignore new file mode 100644 index 0000000000000..409d7e152909b --- /dev/null +++ b/tools/rbp/depends/automake/.gitignore @@ -0,0 +1 @@ +automake-1.10.3 diff --git a/tools/rbp/depends/automake/Makefile b/tools/rbp/depends/automake/Makefile new file mode 100644 index 0000000000000..38c5d6476c4c6 --- /dev/null +++ b/tools/rbp/depends/automake/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +# lib name, version +LIBNAME=automake +VERSION=1.10.3 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +export PREFIX=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) + +LIBDYLIB=$(SOURCE)/bin/automake + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/cmake/.gitignore b/tools/rbp/depends/cmake/.gitignore new file mode 100644 index 0000000000000..5acc817257093 --- /dev/null +++ b/tools/rbp/depends/cmake/.gitignore @@ -0,0 +1 @@ +cmake-2.8.4 diff --git a/tools/rbp/depends/cmake/Makefile b/tools/rbp/depends/cmake/Makefile new file mode 100644 index 0000000000000..a081187c9cc0d --- /dev/null +++ b/tools/rbp/depends/cmake/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +APPNAME=cmake +VERSION=2.8.4 +SOURCE=$(APPNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +PREFIX:=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./bootstrap --prefix=$(PREFIX) + +APP=$(SOURCE)/$(APPNAME) + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(APP) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(APP): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/depends.mk b/tools/rbp/depends/depends.mk new file mode 100644 index 0000000000000..68d0ffeca5a0a --- /dev/null +++ b/tools/rbp/depends/depends.mk @@ -0,0 +1,55 @@ +ifeq ($(USE_BUILDROOT),1) + export CFLAGS=-pipe -O3 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -mabi=aapcs-linux -Wno-psabi -Wa,-mno-warn-deprecated -Wno-deprecated-declarations + export HOST=arm-unknown-linux-gnueabi + export BUILD=i686-linux + export PREFIX=$(XBMCPREFIX) + export SYSROOT=$(BUILDROOT)/output/host/usr/arm-unknown-linux-gnueabi/sysroot + export CFLAGS+=-isystem$(SYSROOT)/usr/include -isystem$(SYSROOT)/opt/vc/include -isystem$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -isystem$(PREFIX)/include -isystem$(PREFIX)/usr/include/mysql --sysroot=$(SYSROOT) + export CXXFLAGS=$(CFLAGS) --sysroot=$(SYSROOT) + export CPPFLAGS=$(CFLAGS) --sysroot=$(SYSROOT) + export LDFLAGS=-L$(SYSROOT)/opt/vc/lib -L$(XBMCPREFIX)/lib + export LD=$(TOOLCHAIN)/bin/$(HOST)-ld + export CC=$(TOOLCHAIN)/bin/$(HOST)-gcc + export CXX=$(TOOLCHAIN)/bin/$(HOST)-g++ + export OBJDUMP=$(TOOLCHAIN)/bin/$(HOST)-objdump + export RANLIB=$(TOOLCHAIN)/bin/$(HOST)-ranlib + export STRIP=$(TOOLCHAIN)/bin/$(HOST)-strip + export AR=$(TOOLCHAIN)/bin/$(HOST)-ar + export CXXCPP=$(CXX) -E + export PKG_CONFIG_PATH=$(PREFIX)/lib/pkgconfig + export PYTHON_VERSION=2.7 + export PATH:=$(PREFIX)/bin:$(BUILDROOT)/output/host/usr/bin:$(PATH) + export TEXTUREPACKER_NATIVE_ROOT=/usr + export PYTHON_LDFLAGS=-L$(SDKSTAGE)/usr/lib -lpython$(PYTHON_VERSION) -lpthread -ldl -lutil -lm +else + export CFLAGS=-pipe -O3 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=softfp -mfpu=vfp -mabi=aapcs-linux -Wno-psabi -Wa,-mno-warn-deprecated -Wno-deprecated-declarations + export HOST=arm-bcm2708-linux-gnueabi + export BUILD=i686-linux + export PREFIX=${XBMCPREFIX} + export TARGETFS + export SYSROOT=/usr/local/bcm-gcc/arm-bcm2708-linux-gnueabi/sys-root + export RLINK_PATH=-Wl,-rpath-link,${SYSROOT}/lib -Wl,-rpath-link,${TARGETFS}/lib -Wl,-rpath-link,${TARGETFS}/usr/lib -Wl,-rpath-link,${TARGETFS}/opt/vc/ + export CFLAGS+=-isystem${XBMCPREFIX}/include -isystem${SDKSTAGE}/usr/include -isystem${SDKSTAGE}/opt/vc/include -isystem$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -isystem${SDKSTAGE}/opt/vc + export CFLAGS+=-L${XBMCPREFIX}/lib -L${SYSROOT}/lib -L${TARGETFS}/lib -L${TARGETFS}/usr/lib -L${TARGETFS}/opt/vc/lib ${RLINK_PATH} + export CXXFLAGS=${CFLAGS} + export CPPFLAGS=${CFLAGS} + export LDFLAGS=${RLINK_PATH} -L${TARGETFS}/lib -L${TARGETFS}/usr/lib -L${XBMCPREFIX}/lib + export LD=${TOOLCHAIN}/bin/${HOST}-ld + export AR=${TOOLCHAIN}/bin/${HOST}-ar + export CC=${TOOLCHAIN}/bin/${HOST}-gcc + export CXX=${TOOLCHAIN}/bin/${HOST}-g++ + export CXXCPP=${CXX} -E + export RANLIB=${TOOLCHAIN}/bin/${HOST}-ranlib + export STRIP=${TOOLCHAIN}/bin/${HOST}-strip + export OBJDUMP=${TOOLCHAIN}/bin/${HOST}-objdump + #export ACLOCAL=aclocal -I ${SDKSTAGE}/usr/share/aclocal -I ${TARGETFS}/usr/share/aclocal-1.11 + export PKG_CONFIG_LIBDIR=${PREFIX}/lib/pkgconfig:${SDKSTAGE}/lib/pkgconfig:${SDKSTAGE}/usr/lib/pkgconfig + export PKG_CONFIG_PATH=$(PREFIX)/bin/pkg-config + export PYTHON_VERSION=2.6 + export PATH:=${PREFIX}/bin:$(PATH):${TOOLCHAIN}/bin + export TEXTUREPACKER_NATIVE_ROOT=/usr + export PYTHON_LDFLAGS=-L$(SDKSTAGE)/usr/lib -lpython$(PYTHON_VERSION) +endif +export PYTHON_CPPFLAGS=-I$(SDKSTAGE)/usr/include/python$(PYTHON_VERSION) +export PYTHON_SITE_PKG=$(SDKSTAGE)/usr/lib/python$(PYTHON_VERSION)/site-packages +export PYTHON_NOVERSIONCHECK=no-check diff --git a/tools/rbp/depends/help2man/.gitignore b/tools/rbp/depends/help2man/.gitignore new file mode 100644 index 0000000000000..eecdd7be980b5 --- /dev/null +++ b/tools/rbp/depends/help2man/.gitignore @@ -0,0 +1 @@ +help2man-1.38.2 diff --git a/tools/rbp/depends/help2man/Makefile b/tools/rbp/depends/help2man/Makefile new file mode 100644 index 0000000000000..e8e53a50b6a63 --- /dev/null +++ b/tools/rbp/depends/help2man/Makefile @@ -0,0 +1,40 @@ +include ../../Makefile.include + +# lib name, version +LIBNAME=help2man +VERSION=1.38.2 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +export PREFIX=$(XBMCPREFIX) +CONFIGURE=./configure --prefix=$(PREFIX) + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +LIBDYLIB=$(SOURCE)/help2man + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/jasper/.gitignore b/tools/rbp/depends/jasper/.gitignore new file mode 100644 index 0000000000000..7030c19dfaed8 --- /dev/null +++ b/tools/rbp/depends/jasper/.gitignore @@ -0,0 +1 @@ +jasper-1.900.1 diff --git a/tools/rbp/depends/jasper/Makefile b/tools/rbp/depends/jasper/Makefile new file mode 100644 index 0000000000000..8898a56f83db3 --- /dev/null +++ b/tools/rbp/depends/jasper/Makefile @@ -0,0 +1,43 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=jasper +VERSION=1.900.1 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).zip +ARCHIVE_TOOL=unzip +ARCHIVE_TOOL_FLAGS= + +# configuration settings +CONFIGURE=./configure --prefix=$(PREFIX) --host=$(HOST) + +LIBDYLIB=$(SOURCE)/$(LIBNAME)/.libs/$(LIBNAME).so + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/liblzo2/.gitignore b/tools/rbp/depends/liblzo2/.gitignore new file mode 100644 index 0000000000000..ef00f815a5b8d --- /dev/null +++ b/tools/rbp/depends/liblzo2/.gitignore @@ -0,0 +1 @@ +lzo-2.03 diff --git a/tools/rbp/depends/liblzo2/Makefile b/tools/rbp/depends/liblzo2/Makefile new file mode 100644 index 0000000000000..7b627062ab313 --- /dev/null +++ b/tools/rbp/depends/liblzo2/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=lzo +VERSION=2.03 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +CONFIGURE=./configure --prefix=$(PREFIX) --host=$(HOST) + +LIBDYLIB=$(SOURCE)/src/.libs/lib$(LIBNAME)2.so + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/libnfs/.gitignore b/tools/rbp/depends/libnfs/.gitignore new file mode 100644 index 0000000000000..72f8fd72df12e --- /dev/null +++ b/tools/rbp/depends/libnfs/.gitignore @@ -0,0 +1 @@ +libnfs-0804e67 diff --git a/tools/rbp/depends/libnfs/Makefile b/tools/rbp/depends/libnfs/Makefile new file mode 100644 index 0000000000000..fb55937a1de7e --- /dev/null +++ b/tools/rbp/depends/libnfs/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=libnfs +VERSION=1.3.0 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +CONFIGURE=./configure CFLAGS=-D_FILE_OFFSET_BITS=64 --prefix=$(PREFIX) --host=$(HOST) + +SO_NAME=$(SOURCE)/lib/libnfs.so + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(SO_NAME) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); ./bootstrap + cd $(SOURCE); $(CONFIGURE) + +$(SO_NAME): $(SOURCE) + make -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ +clean: + make -C $(SOURCE) clean + rm -f .installed + rm -rf $(SOURCE) + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/libplist/0001-dontbuildswig.patch b/tools/rbp/depends/libplist/0001-dontbuildswig.patch new file mode 100644 index 0000000000000..7ff491df8d18b --- /dev/null +++ b/tools/rbp/depends/libplist/0001-dontbuildswig.patch @@ -0,0 +1,11 @@ +--- a/CMakeLists.txt 2011-08-18 21:45:33.000000000 +0200 ++++ b/CMakeLists.txt 2011-08-18 21:45:16.000000000 +0200 +@@ -36,7 +36,7 @@ + ADD_SUBDIRECTORY( test ) + + IF ( SWIG_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND ) +- ADD_SUBDIRECTORY( swig ) ++# ADD_SUBDIRECTORY( swig ) + ENDIF ( SWIG_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND ) + + # add uninstall target diff --git a/tools/rbp/depends/libplist/Makefile b/tools/rbp/depends/libplist/Makefile new file mode 100644 index 0000000000000..ebe049d59d6d1 --- /dev/null +++ b/tools/rbp/depends/libplist/Makefile @@ -0,0 +1,45 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=libplist +VERSION=1.6 +SOURCE=$(LIBNAME)-$(VERSION) +FILENAME=v$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +LIBDYLIB=$(SOURCE)/build/src/$(LIBNAME).so + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); patch -p1 < ../0001-dontbuildswig.patch + cd $(SOURCE); rm -rf build; mkdir -p build + cd $(SOURCE)/build; CC=$(CC) CXX=$(CXX) cmake \ + -DCMAKE_C_FLAGS:STRING="$(CPPFLAGS) $(CFLAGS)" \ + -DCMAKE_CXX_FLAGS:STRING="$(CPPFLAGS) $(CXXFLAGS)" \ + -DCMAKE_INCLUDE_PATH=$(PREFIX)/include \ + -DCMAKE_LIBRARY_PATH=$(PREFIX)/lib \ + -DCMAKE_INSTALL_NAME_DIR=$(PREFIX)/lib \ + -DCMAKE_INSTALL_PREFIX=$(PREFIX) \ + -DCMAKE_FIND_ROOT_PATH=$(platform_sdk_path) .. + +$(LIBDYLIB): $(SOURCE) + make -j 1 -C $(SOURCE)/build + +.installed: + make -C $(SOURCE)/build install + touch $@ + +clean: + rm -rf $(SOURCE) .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/libshairport/.gitignore b/tools/rbp/depends/libshairport/.gitignore new file mode 100644 index 0000000000000..fbc1ee7dff4dc --- /dev/null +++ b/tools/rbp/depends/libshairport/.gitignore @@ -0,0 +1 @@ +libshairport-1.2.0.20310_lib diff --git a/tools/rbp/depends/libshairport/001_add_ao.patch b/tools/rbp/depends/libshairport/001_add_ao.patch new file mode 100644 index 0000000000000..995f7b78eea86 --- /dev/null +++ b/tools/rbp/depends/libshairport/001_add_ao.patch @@ -0,0 +1,270 @@ +diff -uP libshairport-1.2.0.20310_lib/src/ao.h ../../../../../libshairport-1.2.0.20310_lib/src/ao.h +--- src/ao.h 1970-01-01 01:00:00.000000000 +0100 ++++ src/ao.h 2011-09-25 20:36:36.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* ++ * ++ * ao.h ++ * ++ * Original Copyright (C) Aaron Holtzman - May 1999 ++ * Modifications Copyright (C) Stan Seibert - July 2000, July 2001 ++ * More Modifications Copyright (C) Jack Moffitt - October 2000 ++ * ++ * This file is part of libao, a cross-platform audio outputlibrary. See ++ * README for a history of this source code. ++ * ++ * libao 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, or (at your option) ++ * any later version. ++ * ++ * libao 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 GNU Make; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++#ifndef __AO_H__ ++#define __AO_H__ ++ ++#ifdef __cplusplus ++extern "C" ++{ ++#endif /* __cplusplus */ ++ ++#include ++#include ++#include ++#include ++ ++/* --- Constants ---*/ ++ ++#define AO_TYPE_LIVE 1 ++#define AO_TYPE_FILE 2 ++ ++ ++#define AO_ENODRIVER 1 ++#define AO_ENOTFILE 2 ++#define AO_ENOTLIVE 3 ++#define AO_EBADOPTION 4 ++#define AO_EOPENDEVICE 5 ++#define AO_EOPENFILE 6 ++#define AO_EFILEEXISTS 7 ++#define AO_EBADFORMAT 8 ++ ++#define AO_EFAIL 100 ++ ++ ++#define AO_FMT_LITTLE 1 ++#define AO_FMT_BIG 2 ++#define AO_FMT_NATIVE 4 ++ ++/* --- Structures --- */ ++ ++typedef struct ao_info { ++ int type; /* live output or file output? */ ++ char *name; /* full name of driver */ ++ char *short_name; /* short name of driver */ ++ char *author; /* driver author */ ++ char *comment; /* driver comment */ ++ int preferred_byte_format; ++ int priority; ++ char **options; ++ int option_count; ++} ao_info; ++ ++typedef struct ao_functions ao_functions; ++typedef struct ao_device ao_device; ++ ++typedef struct ao_sample_format { ++ int bits; /* bits per sample */ ++ int rate; /* samples per second (in a single channel) */ ++ int channels; /* number of audio channels */ ++ int byte_format; /* Byte ordering in sample, see constants below */ ++ char *matrix; /* input channel location/ordering */ ++} ao_sample_format; ++ ++typedef struct ao_option { ++ char *key; ++ char *value; ++ struct ao_option *next; ++} ao_option; ++ ++#if defined(AO_BUILDING_LIBAO) ++#include "ao_private.h" ++#endif ++ ++/* --- Functions --- */ ++ ++/* library setup/teardown */ ++void ao_initialize(void); ++void ao_shutdown(void); ++ ++/* device setup/playback/teardown */ ++int ao_append_global_option(const char *key, ++ const char *value); ++int ao_append_option(ao_option **options, ++ const char *key, ++ const char *value); ++void ao_free_options(ao_option *options); ++ ++char* ao_get_option(ao_option *options, const char* key); ++ ++ao_device* ao_open_live(int driver_id, ++ ao_sample_format *format, ++ ao_option *option); ++ao_device* ao_open_file(int driver_id, ++ const char *filename, ++ int overwrite, ++ ao_sample_format *format, ++ ao_option *option); ++ ++int ao_play(ao_device *device, ++ char *output_samples, ++ uint32_t num_bytes); ++int ao_close(ao_device *device); ++ ++/* driver information */ ++int ao_driver_id(const char *short_name); ++int ao_default_driver_id(void); ++ao_info *ao_driver_info(int driver_id); ++ao_info **ao_driver_info_list(int *driver_count); ++char *ao_file_extension(int driver_id); ++ ++/* miscellaneous */ ++int ao_is_big_endian(void); ++ ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif /* __AO_H__ */ ++ ++extern struct AudioOutput g_ao; ++struct AudioOutput ++ { ++ void (*ao_initialize)(void); ++ int (*ao_play)(ao_device *, char *, uint32_t); ++ int (*ao_default_driver_id)(void); ++ ao_device* (*ao_open_live)( int, ao_sample_format *, ao_option *); ++ int (*ao_close)(ao_device *); ++ /* -- Device Setup/Playback/Teardown -- */ ++ int (*ao_append_option)(ao_option **, const char *, const char *); ++ void (*ao_free_options)(ao_option *); ++ char* (*ao_get_option)(ao_option *, const char* ); ++ }; +diff -uP libshairport-1.2.0.20310_lib/src/hairtunes.c ../../../../../libshairport-1.2.0.20310_lib/src/hairtunes.c +--- src/hairtunes.c 2011-09-23 21:55:48.000000000 +0200 ++++ src/hairtunes.c 2011-09-25 20:37:49.000000000 +0200 +@@ -25,7 +25,7 @@ + */ + + #define XBMC +-//#defined HAS_AO ++#define HAS_AO + + #include + #include +@@ -45,7 +45,7 @@ + #include + #include + #ifdef HAS_AO +-#include ++#include "ao.h" + #endif + + #ifdef FANCY_RESAMPLING +@@ -881,7 +881,7 @@ + } + #ifdef HAS_AO + } else { +- ao_play(dev, (char *)outbuf, play_samples*4); ++ g_ao.ao_play(dev, (char *)outbuf, play_samples*4); + #endif + } + } +@@ -906,7 +906,7 @@ + ao_device *dev; + + void* init_ao() { +- ao_initialize(); ++ g_ao.ao_initialize(); + + int driver; + #ifndef XBMC +@@ -921,7 +921,7 @@ + #endif + { + // otherwise choose the default +- driver = ao_default_driver_id(); ++ driver = g_ao.ao_default_driver_id(); + } + + ao_sample_format fmt; +@@ -944,9 +944,9 @@ + } + #endif + +- ao_append_option(&ao_opts, "name", "Streaming..."); ++ g_ao.ao_append_option(&ao_opts, "name", "Streaming..."); + +- dev = ao_open_live(driver, &fmt, ao_opts); ++ dev = g_ao.ao_open_live(driver, &fmt, ao_opts); + if (dev == NULL) { + die("Could not open ao device"); + } +@@ -985,7 +985,7 @@ + audio_running = 0; + pthread_join(audio_thread, NULL); + #ifdef HAS_AO +- ao_close(dev); ++ g_ao.ao_close(dev); + #endif + } + +diff -uP libshairport-1.2.0.20310_lib/src/shairport.c ../../../../../libshairport-1.2.0.20310_lib/src/shairport.c +--- src/shairport.c 2011-08-21 01:57:56.000000000 +0200 ++++ src/shairport.c 2011-09-25 20:44:40.000000000 +0200 +@@ -92,6 +92,14 @@ + static char tPassword[56] = ""; + static char tHWID[HWID_SIZE] = {0,51,52,53,54,55}; + ++#ifdef XBMC ++struct AudioOutput g_ao; ++void shairport_set_ao(struct AudioOutput *ao) ++{ ++ g_ao=*ao; ++} ++#endif ++ + #ifndef XBMC + int main(int argc, char **argv) + #else +diff -uP libshairport-1.2.0.20310_lib/src/shairport.h ../../../../../libshairport-1.2.0.20310_lib/src/shairport.h +--- src/shairport.h 2011-08-21 01:58:11.000000000 +0200 ++++ src/shairport.h 2011-09-25 20:36:43.000000000 +0200 +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include "ao.h" + + + #define HWID_SIZE 6 +@@ -62,9 +63,11 @@ + void shairport_exit(void); + int shairport_loop(void); + int shairport_is_running(void); ++void shairport_set_ao(struct AudioOutput *ao); + + #ifdef __cplusplus + } + #endif /* __cplusplus */ + + #endif ++ diff --git a/tools/rbp/depends/libshairport/002_fix_install_header.patch b/tools/rbp/depends/libshairport/002_fix_install_header.patch new file mode 100644 index 0000000000000..495b97346c8dc --- /dev/null +++ b/tools/rbp/depends/libshairport/002_fix_install_header.patch @@ -0,0 +1,12 @@ +--- src/Makefile.am.old 2011-09-23 23:14:39.000000000 +0200 ++++ src/Makefile.am 2011-09-25 21:38:08.000000000 +0200 +@@ -1,7 +1,7 @@ + lib_LTLIBRARIES=libshairport.la + +-library_includedir=$(includedir) +-library_include_HEADERS = shairport.h ++library_includedir=$(includedir)/shairport ++library_include_HEADERS = shairport.h ao.h socketlib.h + + libshairport_la_SOURCES=shairport.c hairtunes.c socketlib.c alac.c + libshairport_la_LDFLAGS=-dynamiclib diff --git a/tools/rbp/depends/libshairport/003_fix_deadlock.patch b/tools/rbp/depends/libshairport/003_fix_deadlock.patch new file mode 100644 index 0000000000000..c05970e138c28 --- /dev/null +++ b/tools/rbp/depends/libshairport/003_fix_deadlock.patch @@ -0,0 +1,10 @@ +--- src/hairtunes.c.orig 2011-09-25 21:58:08.000000000 +0200 ++++ src/hairtunes.c 2011-09-25 21:58:12.000000000 +0200 +@@ -991,6 +991,7 @@ + + void hairtunes_cleanup(void) + { ++ pthread_cond_signal(&ab_buffer_ready); + clean_output(); + clean_rtp(); + clean_buffer(); diff --git a/tools/rbp/depends/libshairport/004_fix_bad_access.patch b/tools/rbp/depends/libshairport/004_fix_bad_access.patch new file mode 100644 index 0000000000000..1976904c4d6ec --- /dev/null +++ b/tools/rbp/depends/libshairport/004_fix_bad_access.patch @@ -0,0 +1,22 @@ +--- src/hairtunes.c.orig 2011-09-25 23:26:56.000000000 +0200 ++++ src/hairtunes.c 2011-09-25 23:29:27.000000000 +0200 +@@ -89,7 +89,6 @@ + // maximal resampling shift - conservative + #define OUTFRAME_BYTES (4*(frame_size+3)) + +- + alac_file *decoder_info; + + #ifdef FANCY_RESAMPLING +@@ -849,6 +848,11 @@ + inbuf = buffer_get_frame(); + } while (!inbuf && audio_running); + ++ if(!audio_running) ++ { ++ return 0; //don't access inbuf if audio stopped ++ } ++ + #ifdef FANCY_RESAMPLING + if (fancy_resampling) { + int i; diff --git a/tools/rbp/depends/libshairport/005_fix_shutdown.patch b/tools/rbp/depends/libshairport/005_fix_shutdown.patch new file mode 100644 index 0000000000000..32bd282719dbd --- /dev/null +++ b/tools/rbp/depends/libshairport/005_fix_shutdown.patch @@ -0,0 +1,10 @@ +--- src/shairport.c 2011-09-25 23:58:06.000000000 +0200 ++++ src/shairport.c.new 2011-09-25 23:57:54.000000000 +0200 +@@ -368,6 +368,7 @@ + void shairport_exit(void) + { + m_running = 0; ++ close(tServerSock); + } + + int shairport_is_running(void) diff --git a/tools/rbp/depends/libshairport/006_no_printf.patch b/tools/rbp/depends/libshairport/006_no_printf.patch new file mode 100644 index 0000000000000..eb69d42b7492f --- /dev/null +++ b/tools/rbp/depends/libshairport/006_no_printf.patch @@ -0,0 +1,843 @@ +--- src/alac.c 2011-08-21 00:06:21.000000000 +0200 ++++ src/alac.c 2011-10-01 04:12:09.000000000 +0200 +@@ -804,7 +804,7 @@ + } + else + { +- fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type); ++ xprintf("FIXME: unhandled predicition type: %i\n", prediction_type); + /* i think the only other prediction type (or perhaps this is just a + * boolean?) runs adaptive fir twice.. like: + * predictor_decompress_fir_adapt(predictor_error, tempout, ...) +@@ -885,7 +885,7 @@ + } + case 20: + case 32: +- fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); ++ xprintf("FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); + break; + default: + break; +@@ -1004,7 +1004,7 @@ + } + else + { /* see mono case */ +- fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_a); ++ xprintf("FIXME: unhandled predicition type: %i\n", prediction_type_a); + } + + /* channel 2 */ +@@ -1029,7 +1029,7 @@ + } + else + { +- fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_b); ++ xprintf("FIXME: unhandled predicition type: %i\n", prediction_type_b); + } + } + else +@@ -1106,7 +1106,7 @@ + } + case 20: + case 32: +- fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); ++ xprintf("FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); + break; + default: + break; +--- src/hairtunes.c 2011-10-01 17:45:08.000000000 +0200 ++++ src/hairtunes.c 2011-10-01 17:42:07.000000000 +0200 +@@ -121,8 +121,8 @@ + pthread_cond_t ab_buffer_ready; + + static void die(char *why) { +- fprintf(stderr, "FATAL: %s\n", why); +- exit(1); ++ xprintf("FATAL: %s\n", why); ++ //exit(1); + } + + static int hex2bin(unsigned char *buf, char *hex) { +@@ -245,13 +245,13 @@ + continue; + } + if (!strcmp(line, "exit\n")) { +- exit(0); ++ ;//exit(0); + } + if (!strcmp(line, "flush\n")) { + hairtunes_flush(); + } + } +- fprintf(stderr, "bye!\n"); ++ xprintf("bye!\n"); + fflush(stderr); + #endif + +@@ -262,7 +262,7 @@ + { + assert(f<=0); + if (debug) +- fprintf(stderr, "VOL: %lf\n", f); ++ xprintf("VOL: %lf\n", f); + volume = pow(10.0,0.05*f); + fix_volume = 65536.0 * volume; + } +@@ -273,7 +273,7 @@ + ab_resync(); + pthread_mutex_unlock(&ab_mutex); + if (debug) +- fprintf(stderr, "FLUSH\n"); ++ xprintf("FLUSH\n"); + } + + #ifdef HAIRTUNES_STANDALONE +@@ -423,7 +423,7 @@ + } else if (seq_order(ab_read, seqno)) { // late but not yet played + abuf = audio_buffer + BUFIDX(seqno); + } else { // too late. +- fprintf(stderr, "\nlate packet %04X (%04X:%04X)\n", seqno, ab_read, ab_write); ++ xprintf("\nlate packet %04X (%04X:%04X)\n", seqno, ab_read, ab_write); + } + buf_fill = ab_write - ab_read; + pthread_mutex_unlock(&ab_mutex); +@@ -520,7 +520,7 @@ + if (seq_order(last, first)) + return; + +- fprintf(stderr, "requesting resend on %d packets (port %d)\n", last-first+1, controlport); ++ xprintf("requesting resend on %d packets (port %d)\n", last-first+1, controlport); + + char req[8]; // *not* a standard RTCP NACK + req[0] = 0x80; +@@ -604,8 +604,8 @@ + port += 3; + } + +- printf("port: %d\n", port); // let our handler know where we end up listening +- printf("cport: %d\n", port+1); ++ xprintf("port: %d\n", port); // let our handler know where we end up listening ++ xprintf("cport: %d\n", port+1); + + rtp_sockets[0] = sock; + rtp_sockets[1] = csock; +@@ -708,7 +708,7 @@ + bf_est_drift = biquad_filt(&bf_drift_lpf, CONTROL_B*(bf_est_err*CONTROL_A + err_deriv) + bf_est_drift); + + if (debug) +- fprintf(stderr, "bf %d err %f drift %f desiring %f ed %f estd %f\r", fill, bf_est_err, bf_est_drift, desired_fill, err_deriv, err_deriv + CONTROL_A*bf_est_err); ++ xprintf("bf %d err %f drift %f desiring %f ed %f estd %f\r", fill, bf_est_err, bf_est_drift, desired_fill, err_deriv, err_deriv + CONTROL_A*bf_est_err); + bf_playback_rate = 1.0 + CONTROL_A*bf_est_err + bf_est_drift; + + bf_last_err = bf_est_err; +@@ -724,7 +724,7 @@ + buf_fill = ab_write - ab_read; + if (buf_fill < 1 || !ab_synced || ab_buffering) { // init or underrun. stop and wait + if (ab_synced) +- fprintf(stderr, "\nunderrun\n"); ++ xprintf("\nunderrun\n"); + + ab_buffering = 1; + pthread_cond_wait(&ab_buffer_ready, &ab_mutex); +@@ -736,7 +736,7 @@ + return 0; + } + if (buf_fill >= BUFFER_FRAMES) { // overrunning! uh-oh. restart at a sane distance +- fprintf(stderr, "\noverrun.\n"); ++ xprintf("\noverrun.\n"); + ab_read = ab_write - START_FILL; + } + read = ab_read; +@@ -748,7 +748,7 @@ + + volatile abuf_t *curframe = audio_buffer + BUFIDX(read); + if (!curframe->ready) { +- fprintf(stderr, "\nmissing frame.\n"); ++ xprintf("\nmissing frame.\n"); + memset(curframe->data, 0, FRAME_BYTES); + } + curframe->ready = 0; +@@ -775,13 +775,13 @@ + if (stuff) { + if (stuff==1) { + if (debug) +- fprintf(stderr, "+++++++++\n"); ++ xprintf("+++++++++\n"); + // interpolate one sample + *outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1); + *outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1); + } else if (stuff==-1) { + if (debug) +- fprintf(stderr, "---------\n"); ++ xprintf("---------\n"); + inptr++; + inptr++; + } +--- src/shairport.c 2011-10-01 17:45:08.000000000 +0200 ++++ src/shairport.c 2011-10-01 17:41:04.000000000 +0200 +@@ -31,6 +31,27 @@ + #include "shairport.h" + #include "hairtunes.h" + ++static struct printfPtr g_printf={NULL}; ++ ++int xprintf(const char *format, ...) ++{ ++ char dbg[2048]; ++ va_list args; ++ va_start(args, format); ++ vsnprintf(dbg, sizeof(dbg), format, args); ++ va_end(args); ++ if(g_printf.extprintf) ++ { ++ g_printf.extprintf(dbg, sizeof(dbg)); ++ } ++ else ++ { ++ printf(dbg); ++ } ++ ++ return 1; ++} ++ + #ifndef TRUE + #define TRUE (-1) + #endif +@@ -98,6 +119,11 @@ + { + g_ao=*ao; + } ++ ++void shairport_set_printf(struct printfPtr *funcPtr) ++{ ++ g_printf = *funcPtr; ++} + #endif + + #ifndef XBMC +@@ -106,7 +132,7 @@ + int shairport_main(int argc, char **argv) + #endif + { +- printf("initializing shairport\n"); ++ xprintf("initializing shairport\n",NULL); + char tHWID_Hex[HWID_SIZE * 2 + 1]; + char tKnownHwid[32]; + +@@ -185,22 +211,22 @@ + } + else if(!strcmp(arg, "-h") || !strcmp(arg, "--help")) + { +- slog(LOG_INFO, "ShairPort version 0.05 C port - Airport Express emulator\n"); +- slog(LOG_INFO, "Usage:\nshairport [OPTION...]\n\nOptions:\n"); +- slog(LOG_INFO, " -a, --apname=AirPort Sets Airport name\n"); +- slog(LOG_INFO, " -p, --password=secret Sets Password (not working)\n"); +- slog(LOG_INFO, " -o, --server_port=5000 Sets Port for Avahi/dns-sd\n"); +- slog(LOG_INFO, " -b, --buffer=282 Sets Number of frames to buffer before beginning playback\n"); +- slog(LOG_INFO, " -d Daemon mode\n"); +- slog(LOG_INFO, " -q, --quiet Supresses all output.\n"); +- slog(LOG_INFO, " -v,-v2,-v3,-vv Various debugging levels\n"); +- slog(LOG_INFO, "\n"); ++ xprintf("ShairPort version 0.05 C port - Airport Express emulator\n"); ++ xprintf("Usage:\nshairport [OPTION...]\n\nOptions:\n"); ++ xprintf(" -a, --apname=AirPort Sets Airport name\n"); ++ xprintf(" -p, --password=secret Sets Password (not working)\n"); ++ xprintf(" -o, --server_port=5000 Sets Port for Avahi/dns-sd\n"); ++ xprintf(" -b, --buffer=282 Sets Number of frames to buffer before beginning playback\n"); ++ xprintf(" -d Daemon mode\n"); ++ xprintf(" -q, --quiet Supresses all output.\n"); ++ xprintf(" -v,-v2,-v3,-vv Various debugging levels\n"); ++ xprintf("\n"); + return 0; + } + } + + if ( buffer_start_fill < 30 || buffer_start_fill > BUFFER_FRAMES ) { +- fprintf(stderr, "buffer value must be > 30 and < %d\n", BUFFER_FRAMES); ++ xprintf("buffer value must be > 30 and < %d\n", BUFFER_FRAMES); + return(0); + } + +@@ -209,11 +235,11 @@ + int tPid = fork(); + if(tPid < 0) + { +- exit(1); // Error on fork ++ //exit(1); // Error on fork + } + else if(tPid > 0) + { +- exit(0); ++ //exit(0); + } + else + { +@@ -254,10 +280,10 @@ + sscanf(tHWID_Hex, "%02X%02X%02X%02X%02X%02X", &tHWID[0], &tHWID[1], &tHWID[2], &tHWID[3], &tHWID[4], &tHWID[5]); + } + +- slog(LOG_INFO, "LogLevel: %d\n", kCurrentLogLevel); +- slog(LOG_INFO, "AirName: %s\n", tServerName); +- slog(LOG_INFO, "HWID: %.*s\n", HWID_SIZE, tHWID+1); +- slog(LOG_INFO, "HWID_Hex(%d): %s\n", strlen(tHWID_Hex), tHWID_Hex); ++ xprintf("LogLevel: %d\n", kCurrentLogLevel); ++ xprintf("AirName: %s\n", tServerName); ++ xprintf("HWID: %.*s\n", HWID_SIZE, tHWID+1); ++ xprintf("HWID_Hex(%d): %s\n", strlen(tHWID_Hex), tHWID_Hex); + + if(tSimLevel >= 1) + { +@@ -271,12 +297,12 @@ + #ifndef XBMC + startAvahi(tHWID_Hex, tServerName, tPort); + #endif +- slog(LOG_DEBUG_V, "Starting connection server: specified server port: %d\n", tPort); ++ xprintf("Starting connection server: specified server port: %d\n", tPort); + tServerSock = setupListenServer(&tAddrInfo, tPort); + if(tServerSock < 0) + { + freeaddrinfo(tAddrInfo); +- slog(LOG_INFO, "Error setting up server socket on port %d, try specifying a different port\n", tPort); ++ xprintf("Error setting up server socket on port %d, try specifying a different port\n", tPort); + return 0; + } + +@@ -303,7 +329,7 @@ + + int readsock; + +- slog(LOG_DEBUG_V, "Waiting for clients to connect\n"); ++ xprintf("Waiting for clients to connect\n"); + + while(m_running) + { +@@ -335,7 +361,7 @@ + { + freeaddrinfo(tAddrInfo); + tAddrInfo = NULL; +- slog(LOG_DEBUG, "...Accepted Client Connection..\n"); ++ xprintf("...Accepted Client Connection..\n"); + close(tServerSock); + handleClient(tClientSock, tPassword, tHWID); + //close(tClientSock); +@@ -343,11 +369,11 @@ + } + else + { +- slog(LOG_DEBUG_VV, "Child now busy handling new client\n"); ++ xprintf("Child now busy handling new client\n"); + close(tClientSock); + } + #else +- slog(LOG_DEBUG, "...Accepted Client Connection..\n"); ++ xprintf("...Accepted Client Connection..\n"); + handleClient(tClientSock, tPassword, tHWID); + #endif + } +@@ -357,7 +383,7 @@ + } + } + +- slog(LOG_DEBUG_VV, "Finished\n"); ++ xprintf("Finished\n"); + if(tAddrInfo != NULL) + { + freeaddrinfo(tAddrInfo); +@@ -416,7 +442,7 @@ + + void handleClient(int pSock, char *pPassword, char *pHWADDR) + { +- slog(LOG_DEBUG_VV, "In Handle Client\n"); ++ xprintf("In Handle Client\n"); + fflush(stdout); + + socklen_t len; +@@ -435,7 +461,7 @@ + + // deal with both IPv4 and IPv6: + if (addr.ss_family == AF_INET) { +- slog(LOG_DEBUG_V, "Constructing ipv4 address\n"); ++ xprintf("Constructing ipv4 address\n"); + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + port = ntohs(s->sin_port); + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); +@@ -455,20 +481,20 @@ + if(memcmp(&addr.bin[0], "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff", 12) == 0) + { + // its ipv4... +- slog(LOG_DEBUG_V, "Constructing ipv4 from ipv6 address\n"); ++ xprintf("Constructing ipv4 from ipv6 address\n"); + memcpy(ipbin, &addr.bin[12], 4); + ipbinlen = 4; + } + else + { +- slog(LOG_DEBUG_V, "Constructing ipv6 address\n"); ++ xprintf("Constructing ipv6 address\n"); + memcpy(ipbin, &s->sin6_addr, 16); + ipbinlen = 16; + } + } + +- slog(LOG_DEBUG_V, "Peer IP address: %s\n", ipstr); +- slog(LOG_DEBUG_V, "Peer port : %d\n", port); ++ xprintf("Peer IP address: %s\n", ipstr); ++ xprintf("Peer port : %d\n", port); + + int tMoreDataNeeded = 1; + struct keyring tKeys; +@@ -489,16 +515,16 @@ + tError = readDataFromClient(pSock, &(tConn.recv)); + if(!tError && strlen(tConn.recv.data) > 0) + { +- slog(LOG_DEBUG_VV, "Finished Reading some data from client\n"); ++ xprintf("Finished Reading some data from client\n"); + // parse client request + tMoreDataNeeded = parseMessage(&tConn, ipbin, ipbinlen, pHWADDR); + if(1 == tMoreDataNeeded) + { +- slog(LOG_DEBUG_VV, "\n\nNeed to read more data\n"); ++ xprintf("\n\nNeed to read more data\n"); + } + else if(-1 == tMoreDataNeeded) // Forked process down below ended. + { +- slog(LOG_DEBUG_V, "Forked Process ended...cleaning up\n"); ++ xprintf("Forked Process ended...cleaning up\n"); + cleanup(&tConn); + // pSock was already closed + return; +@@ -507,13 +533,13 @@ + } + else + { +- slog(LOG_DEBUG, "Error reading from socket, closing client\n"); ++ xprintf("Error reading from socket, closing client\n"); + // Error reading data....quit. + cleanup(&tConn); + return; + } + } +- slog(LOG_DEBUG_VV, "Writing: %d chars to socket\n", tConn.resp.current); ++ xprintf("Writing: %d chars to socket\n", tConn.resp.current); + //tConn->resp.data[tConn->resp.current-1] = '\0'; + writeDataToClient(pSock, &(tConn.resp)); + // Finished reading one message... +@@ -526,9 +552,9 @@ + + void writeDataToClient(int pSock, struct shairbuffer *pResponse) + { +- slog(LOG_DEBUG_VV, "\n----Beg Send Response Header----\n%.*s\n", pResponse->current, pResponse->data); ++ xprintf("\n----Beg Send Response Header----\n%.*s\n", pResponse->current, pResponse->data); + send(pSock, pResponse->data, pResponse->current,0); +- slog(LOG_DEBUG_VV, "----Send Response Header----\n"); ++ xprintf("----Send Response Header----\n"); + } + + int readDataFromClient(int pSock, struct shairbuffer *pClientBuffer) +@@ -541,7 +567,7 @@ + while(tRetval > 0 && tEnd < 0) + { + // Read from socket until \n\n, \r\n\r\n, or \r\r is found +- slog(LOG_DEBUG_V, "Waiting To Read...\n"); ++ xprintf("Waiting To Read...\n"); + fflush(stdout); + tRetval = read(pSock, tReadBuf, MAX_SIZE); + // if new buffer contains the end of request string, only copy partial buffer? +@@ -552,40 +578,40 @@ + { + pClientBuffer->marker = tEnd+1; // Marks start of content + } +- slog(SOCKET_LOG_LEVEL, "Found end of http request at: %d\n", tEnd); ++ xprintf("Found end of http request at: %d\n", tEnd); + fflush(stdout); + } + else + { + tEnd = MAX_SIZE; +- slog(SOCKET_LOG_LEVEL, "Read %d of data so far\n%s\n", tRetval, tReadBuf); ++ xprintf("Read %d of data so far\n%s\n", tRetval, tReadBuf); + fflush(stdout); + } + if(tRetval > 0) + { + // Copy read data into tReceive; +- slog(SOCKET_LOG_LEVEL, "Read %d data, using %d of it\n", tRetval, tEnd); ++ xprintf("Read %d data, using %d of it\n", tRetval, tEnd); + addNToShairBuffer(pClientBuffer, tReadBuf, tRetval); +- slog(LOG_DEBUG_VV, "Finished copying data\n"); ++ xprintf("Finished copying data\n"); + } + else + { +- slog(LOG_DEBUG, "Error reading data from socket, got: %d bytes", tRetval); ++ xprintf("Error reading data from socket, got: %d bytes", tRetval); + return tRetval; + } + } + if(tEnd + 1 != tRetval) + { +- slog(SOCKET_LOG_LEVEL, "Read more data after end of http request. %d instead of %d\n", tRetval, tEnd+1); ++ xprintf("Read more data after end of http request. %d instead of %d\n", tRetval, tEnd+1); + } +- slog(SOCKET_LOG_LEVEL, "Finished Reading Data:\n%s\nEndOfData\n", pClientBuffer->data); ++ xprintf("Finished Reading Data:\n%s\nEndOfData\n", pClientBuffer->data); + fflush(stdout); + return 0; + } + + char *getFromBuffer(char *pBufferPtr, const char *pField, int pLenAfterField, int *pReturnSize, char *pDelims) + { +- slog(LOG_DEBUG_V, "GettingFromBuffer: %s\n", pField); ++ xprintf("GettingFromBuffer: %s\n", pField); + char* tFound = strstr(pBufferPtr, pField); + int tSize = 0; + if(tFound != NULL) +@@ -606,7 +632,7 @@ + } + + tSize = (int) (tShortest - tFound); +- slog(LOG_DEBUG_VV, "Found %.*s length: %d\n", tSize, tFound, tSize); ++ xprintf("Found %.*s length: %d\n", tSize, tFound, tSize); + if(pReturnSize != NULL) + { + *pReturnSize = tSize; +@@ -614,7 +640,7 @@ + } + else + { +- slog(LOG_DEBUG_V, "Not Found\n"); ++ xprintf("Not Found\n"); + } + return tFound; + } +@@ -648,10 +674,10 @@ + { + char tTrim[tFoundSize + 2]; + getTrimmed(tFound, tFoundSize, TRUE, TRUE, tTrim); +- slog(LOG_DEBUG_VV, "HeaderChallenge: [%s] len: %d sizeFound: %d\n", tTrim, strlen(tTrim), tFoundSize); ++ xprintf("HeaderChallenge: [%s] len: %d sizeFound: %d\n", tTrim, strlen(tTrim), tFoundSize); + int tChallengeDecodeSize = 16; + char *tChallenge = decode_base64((unsigned char *)tTrim, tFoundSize, &tChallengeDecodeSize); +- slog(LOG_DEBUG_VV, "Challenge Decode size: %d expected 16\n", tChallengeDecodeSize); ++ xprintf("Challenge Decode size: %d expected 16\n", tChallengeDecodeSize); + + int tCurSize = 0; + unsigned char tChalResp[38]; +@@ -673,7 +699,7 @@ + } + + char *tTmp = encode_base64((unsigned char *)tChalResp, tCurSize); +- slog(LOG_DEBUG_VV, "Full sig: %s\n", tTmp); ++ xprintf("Full sig: %s\n", tTmp); + free(tTmp); + + // RSA Encrypt +@@ -722,10 +748,10 @@ + { + if(isLogEnabledFor(HEADER_LOG_LEVEL)) + { +- slog(HEADER_LOG_LEVEL, "Content-Length: %s value -> %d\n", tContent, tContentSize); ++ xprintf("Content-Length: %s value -> %d\n", tContent, tContentSize); + if(pConn->recv.marker != 0) + { +- slog(HEADER_LOG_LEVEL, "ContentPtr has %d, but needs %d\n", ++ xprintf("ContentPtr has %d, but needs %d\n", + strlen(pConn->recv.data+pConn->recv.marker), tContentSize); + } + } +@@ -735,7 +761,7 @@ + } + else + { +- slog(LOG_DEBUG_VV, "No content, header only\n"); ++ xprintf("No content, header only\n"); + } + + // "Creates" a new Response Header for our response message +@@ -748,7 +774,7 @@ + { + tLen = 20; + } +- slog(LOG_INFO, "********** RECV %.*s **********\n", tLen, pConn->recv.data); ++ xprintf("********** RECV %.*s **********\n", tLen, pConn->recv.data); + } + + if(pConn->password != NULL) +@@ -758,7 +784,7 @@ + + if(buildAppleResponse(pConn, pIpBin, pIpBinLen, pHWID)) // need to free sig + { +- slog(LOG_DEBUG_V, "Added AppleResponse to Apple-Challenge request\n"); ++ xprintf("Added AppleResponse to Apple-Challenge request\n"); + } + + // Find option, then based on option, do different actions. +@@ -778,14 +804,14 @@ + int tKeySize = 0; + char tEncodedAesIV[tSize + 2]; + getTrimmed(tHeaderVal, tSize, TRUE, TRUE, tEncodedAesIV); +- slog(LOG_DEBUG_VV, "AESIV: [%.*s] Size: %d Strlen: %d\n", tSize, tEncodedAesIV, tSize, strlen(tEncodedAesIV)); ++ xprintf("AESIV: [%.*s] Size: %d Strlen: %d\n", tSize, tEncodedAesIV, tSize, strlen(tEncodedAesIV)); + char *tDecodedIV = decode_base64((unsigned char*) tEncodedAesIV, tSize, &tSize); + + // grab the key, copy it out of the receive buffer + tHeaderVal = getFromContent(tContent, "a=rsaaeskey", &tKeySize); + char tEncodedAesKey[tKeySize + 2]; // +1 for nl, +1 for \0 + getTrimmed(tHeaderVal, tKeySize, TRUE, TRUE, tEncodedAesKey); +- slog(LOG_DEBUG_VV, "AES KEY: [%s] Size: %d Strlen: %d\n", tEncodedAesKey, tKeySize, strlen(tEncodedAesKey)); ++ xprintf("AES KEY: [%s] Size: %d Strlen: %d\n", tEncodedAesKey, tKeySize, strlen(tEncodedAesKey)); + // remove base64 coding from key + char *tDecodedAesKey = decode_base64((unsigned char*) tEncodedAesKey, + tKeySize, &tKeySize); // Need to free DecodedAesKey +@@ -794,7 +820,7 @@ + int tFmtpSize = 0; + char *tFmtp = getFromContent(tContent, "a=fmtp", &tFmtpSize); // Don't need to free + tFmtp = getTrimmedMalloc(tFmtp, tFmtpSize, TRUE, FALSE); // will need to free +- slog(LOG_DEBUG_VV, "Format: %s\n", tFmtp); ++ xprintf("Format: %s\n", tFmtp); + + RSA *rsa = loadKey(); + // Decrypt the binary aes key +@@ -803,11 +829,11 @@ + if(RSA_private_decrypt(tKeySize, (unsigned char *)tDecodedAesKey, + (unsigned char*) tDecryptedKey, rsa, RSA_PKCS1_OAEP_PADDING) >= 0) + { +- slog(LOG_DEBUG, "Decrypted AES key from RSA Successfully\n"); ++ xprintf("Decrypted AES key from RSA Successfully\n"); + } + else + { +- slog(LOG_INFO, "Error Decrypting AES key from RSA\n"); ++ xprintf("Error Decrypting AES key from RSA\n"); + } + free(tDecodedAesKey); + RSA_free(rsa); +@@ -823,13 +849,13 @@ + // struct comms *tComms = pConn->hairtunes; + // if (! (pipe(tComms->in) == 0 && pipe(tComms->out) == 0)) + // { +-// slog(LOG_INFO, "Error setting up hairtunes communications...some things probably wont work very well.\n"); ++// xprintf("Error setting up hairtunes communications...some things probably wont work very well.\n"); + // } + + // Setup fork + char tPort[8] = "6000"; // get this from dup()'d stdout of child pid + +- printf("******** SETUP!!!!!\n"); ++ xprintf("******** SETUP!!!!!\n",NULL); + #ifndef XBMC + int tPid = fork(); + if(tPid == 0) +@@ -845,11 +871,11 @@ + tFound = getFromSetup(pConn->recv.data, "timing_port", &tSize); + getTrimmed(tFound, tSize, 1, 0, tTPortStr); + +- slog(LOG_DEBUG_VV, "converting %s and %s from str->int\n", tCPortStr, tTPortStr); ++ xprintf("converting %s and %s from str->int\n", tCPortStr, tTPortStr); + int tControlport = atoi(tCPortStr); + int tTimingport = atoi(tTPortStr); + +- slog(LOG_DEBUG_V, "Got %d for CPort and %d for TPort\n", tControlport, tTimingport); ++ xprintf("Got %d for CPort and %d for TPort\n", tControlport, tTimingport); + char *tRtp = NULL; + char *tPipe = NULL; + char *tAoDriver = NULL; +@@ -884,7 +910,7 @@ + tDataport, tRtp, tPipe, tAoDriver, tAoDeviceName, tAoDeviceId); + #ifndef XBMC + // Quit when finished. +- slog(LOG_DEBUG, "Returned from hairtunes init....returning -1, should close out this whole side of the fork\n"); ++ xprintf("Returned from hairtunes init....returning -1, should close out this whole side of the fork\n"); + return -1; + } + else if(tPid >0) +@@ -897,7 +923,7 @@ + int tRead = read(tComms->out[0], tFromHairtunes, 80); + if(tRead <= 0) + { +- slog(LOG_INFO, "Error reading port from hairtunes function, assuming default port: %d\n", tPort); ++ xprintf("Error reading port from hairtunes function, assuming default port: %d\n", tPort); + } + else + { +@@ -909,7 +935,7 @@ + } + else + { +- slog(LOG_INFO, "Read %d bytes, Error translating %s into a port\n", tRead, tFromHairtunes); ++ xprintf("Read %d bytes, Error translating %s into a port\n", tRead, tFromHairtunes); + } + } + +@@ -930,7 +956,7 @@ + } + else + { +- slog(LOG_INFO, "Error forking process....dere' be errors round here.\n"); ++ xprintf("Error forking process....dere' be errors round here.\n"); + return -1; + } + #endif +@@ -942,7 +968,7 @@ + propogateCSeq(pConn); + #ifndef XBMC + close(pConn->hairtunes->in[1]); +- slog(LOG_DEBUG, "Tearing down connection, closing pipes\n"); ++ xprintf("Tearing down connection, closing pipes\n"); + #else + hairtunes_cleanup(); + #endif +@@ -964,7 +990,7 @@ + propogateCSeq(pConn); + int tSize = 0; + char *tVol = getFromHeader(pConn->recv.data, "volume", &tSize); +- slog(LOG_DEBUG_VV, "About to write [vol: %.*s] data to hairtunes\n", tSize, tVol); ++ xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol); + // TBD VOLUME + #ifndef XBMC + write(pConn->hairtunes->in[1], "vol: ", 5); +@@ -973,11 +999,11 @@ + #else + hairtunes_setvolume(atof(tVol)); + #endif +- slog(LOG_DEBUG_VV, "Finished writing data write data to hairtunes\n"); ++ xprintf("Finished writing data write data to hairtunes\n"); + } + else + { +- slog(LOG_DEBUG, "\n\nUn-Handled recv: %s\n", pConn->recv.data); ++ xprintf("\n\nUn-Handled recv: %s\n", pConn->recv.data); + propogateCSeq(pConn); + } + addToShairBuffer(&(pConn->resp), "\r\n"); +@@ -1056,7 +1082,7 @@ + char tName[100 + HWID_SIZE + 3]; + if(strlen(pServerName) > tMaxServerName) + { +- slog(LOG_INFO,"Hey dog, we see you like long server names, " ++ xprintf("Hey dog, we see you like long server names, " + "so we put a strncat in our command so we don't buffer overflow, while you listen to your flow.\n" + "We just used the first %d characters. Pick something shorter if you want\n", tMaxServerName); + } +@@ -1067,7 +1093,7 @@ + strcat(tName, pHWStr); + strcat(tName, "@"); + strncat(tName, pServerName, tMaxServerName); +- slog(AVAHI_LOG_LEVEL, "Avahi/DNS-SD Name: %s\n", tName); ++ xprintf("Avahi/DNS-SD Name: %s\n", tName); + + execlp("avahi-publish-service", "avahi-publish-service", tName, + "_raop._tcp", tPort, "tp=UDP","sm=false","sv=false","ek=1","et=0,1", +@@ -1079,12 +1105,12 @@ + perror("error"); + } + +- slog(LOG_INFO, "Bad error... couldn't find or failed to run: avahi-publish-service OR dns-sd\n"); +- exit(1); ++ xprintf("Bad error... couldn't find or failed to run: avahi-publish-service OR dns-sd\n"); ++ //exit(1); + } + else + { +- slog(LOG_DEBUG_VV, "Avahi/DNS-SD started on PID: %d\n", tPid); ++ xprintf("Avahi/DNS-SD started on PID: %d\n", tPid); + } + return tPid; + } +@@ -1092,7 +1118,7 @@ + + void printBufferInfo(struct shairbuffer *pBuf, int pLevel) + { +- slog(pLevel, "Buffer: [%s] size: %d maxchars:%d\n", pBuf->data, pBuf->current, pBuf->maxsize/sizeof(char)); ++ xprintf("Buffer: [%s] size: %d maxchars:%d\n", pBuf->data, pBuf->current, pBuf->maxsize/sizeof(char)); + } + + int getAvailChars(struct shairbuffer *pBuf) +@@ -1173,7 +1199,8 @@ + { + va_list argp; + va_start(argp, pFormat); +- vprintf(pFormat, argp); ++ xprintf(pFormat, argp); ++ //vprintf(pFormat, argp); + va_end(argp); + } + //#endif +@@ -1227,9 +1254,9 @@ + { + if(pBuf->data != NULL) + { +- slog(LOG_DEBUG_VV, "Hrm, buffer wasn't cleaned up....trying to free\n"); ++ xprintf("Hrm, buffer wasn't cleaned up....trying to free\n"); + free(pBuf->data); +- slog(LOG_DEBUG_VV, "Free didn't seem to seg fault....huzzah\n"); ++ xprintf("Free didn't seem to seg fault....huzzah\n"); + } + pBuf->current = 0; + pBuf->marker = 0; +@@ -1287,6 +1314,6 @@ + BIO *tBio = BIO_new_mem_buf(AIRPORT_PRIVATE_KEY, -1); + RSA *rsa = PEM_read_bio_RSAPrivateKey(tBio, NULL, NULL, NULL); //NULL, NULL, NULL); + BIO_free(tBio); +- slog(RSA_LOG_LEVEL, "RSA Key: %d\n", RSA_check_key(rsa)); ++ xprintf("RSA Key: %d\n", RSA_check_key(rsa)); + return rsa; + } +--- src/shairport.h 2011-10-01 17:45:08.000000000 +0200 ++++ src/shairport.h 2011-10-01 17:19:43.000000000 +0200 +@@ -59,11 +59,17 @@ + { + #endif /* __cplusplus */ + ++struct printfPtr ++{ ++ int (*extprintf)(const char* msg, size_t msgSize); ++}; ++ + int shairport_main(int argc, char **argv); + void shairport_exit(void); + int shairport_loop(void); + int shairport_is_running(void); + void shairport_set_ao(struct AudioOutput *ao); ++void shairport_set_printf(struct printfPtr *funcPtr); + + #ifdef __cplusplus + } +--- src/socketlib.c 2011-09-23 22:00:48.000000000 +0200 ++++ src/socketlib.c 2011-10-01 17:42:39.000000000 +0200 +@@ -82,7 +82,7 @@ + delay(RETRY_DELAY, &tRes); + } + } +- printf("%d Retry attempts exceeded\n", RETRY_COUNT); ++ xprintf("%d Retry attempts exceeded\n", RETRY_COUNT); + return ERROR; + } + +@@ -102,7 +102,7 @@ + tError = getaddrinfo(pHostname, pService, &hints, pAddrInfo); + if(tError != 0) + { +- printf("Error getting address info\n"); ++ xprintf("Error getting address info\n"); + } + return tError; + } +@@ -200,7 +200,7 @@ + else + { + // Invalid encoded data, no other cases are possible. +- printf("Unrecoverable error....base64 values are incorrectly encoded\n"); ++ xprintf("Unrecoverable error....base64 values are incorrectly encoded\n"); + return pSize; + } + } +@@ -226,7 +226,7 @@ + memset(input, 0, length); + memcpy(input, pInput, pLength); + memset(input+pLength, '=', length-pLength); +- printf("Fixed value: [%.*s]\n", length, input); ++ xprintf("Fixed value: [%.*s]\n", length, input); + } + char *buffer = (char *)malloc(length); + memset(buffer, 0, length); diff --git a/tools/rbp/depends/libshairport/007_fix_syslog_defines.patch b/tools/rbp/depends/libshairport/007_fix_syslog_defines.patch new file mode 100644 index 0000000000000..4d9b834f9c5de --- /dev/null +++ b/tools/rbp/depends/libshairport/007_fix_syslog_defines.patch @@ -0,0 +1,24 @@ +--- src/shairport.h 2011-10-01 04:09:16.000000000 +0200 ++++ src/shairport.h 2011-11-07 18:05:05.000000000 +0100 +@@ -16,10 +16,17 @@ + + #define HWID_SIZE 6 + #define SHAIRPORT_LOG 1 +-#define LOG_INFO 1 +-#define LOG_DEBUG 5 +-#define LOG_DEBUG_V 6 +-#define LOG_DEBUG_VV 7 ++ ++#ifndef LOG_INFO ++#define LOG_INFO 5 ++#endif ++ ++#ifndef LOG_DEBUG ++#define LOG_DEBUG 6 ++#endif ++ ++#define LOG_DEBUG_V 7 ++#define LOG_DEBUG_VV 8 + + struct shairbuffer + { diff --git a/tools/rbp/depends/libshairport/008-add-missing-libs.patch b/tools/rbp/depends/libshairport/008-add-missing-libs.patch new file mode 100644 index 0000000000000..6efd30222a054 --- /dev/null +++ b/tools/rbp/depends/libshairport/008-add-missing-libs.patch @@ -0,0 +1,21 @@ +--- configure.in-org 2011-11-08 11:53:42.802419355 -0500 ++++ configure.in 2011-11-08 11:55:38.082419413 -0500 +@@ -11,8 +11,9 @@ AC_PROG_LIBTOOL + + # Checks for libraries. + #AC_CHECK_LIB([c], [main]) +-#AC_CHECK_LIB([m], [main]) ++AC_CHECK_LIB([m], [main]) + AC_CHECK_LIB([ssl], [main],, AC_MSG_ERROR($missing_library)) ++AC_CHECK_LIB([crypto], [main],, AC_MSG_ERROR($missing_library)) + AC_CHECK_LIB([pthread], [main],, AC_MSG_ERROR($missing_library)) + + OUTPUT_FILES="Makefile" +@@ -21,4 +22,4 @@ LIBDIR=$PREFIX + + AC_CONFIG_FILES([${OUTPUT_FILES}]) + AC_OUTPUT(Makefile src/Makefile) +-AC_OUTPUT +\ No newline at end of file ++AC_OUTPUT + diff --git a/tools/rbp/depends/libshairport/009_fix_ipv6.patch b/tools/rbp/depends/libshairport/009_fix_ipv6.patch new file mode 100644 index 0000000000000..01ff026d071c9 --- /dev/null +++ b/tools/rbp/depends/libshairport/009_fix_ipv6.patch @@ -0,0 +1,22 @@ +--- src/socketlib.c 2012-01-04 20:41:05.000000000 +0100 ++++ src/socketlib.c 2012-01-04 20:35:53.000000000 +0100 +@@ -48,7 +48,7 @@ + if((tSock==-1) && (pAddrInfo->ai_family == AF_INET6) && (errno == EAFNOSUPPORT)) + { + //Fallback to ipv4 +- perror("Failed to create ipv6 socket. Trying ipv4"); ++ xprintf("Failed to create ipv6 socket. Trying ipv4"); + pAddrInfo->ai_family = AF_INET; + tSock = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, 0); + } +@@ -158,8 +158,8 @@ + sprintf(tService, "%d", pPort); // copies port to string + int tFamily = AF_INET; + #ifdef AF_INET6 +- //printf("Listening on IPv6 Socket\n"); +- //tFamily = AF_INET6; ++ xprintf("Listening on IPv6 Socket\n"); ++ tFamily = AF_INET6; + #else + //printf("Listening on IPv4 Socket"); + #endif diff --git a/tools/rbp/depends/libshairport/010_handle_metadata.patch b/tools/rbp/depends/libshairport/010_handle_metadata.patch new file mode 100644 index 0000000000000..a48782ed08c53 --- /dev/null +++ b/tools/rbp/depends/libshairport/010_handle_metadata.patch @@ -0,0 +1,154 @@ +diff -ruP src/ao.h libshairport.new/src/ao.h +--- src/ao.h 2012-05-07 22:26:53.000000000 +0200 ++++ src/ao.h 2012-05-08 18:46:42.000000000 +0200 +@@ -152,5 +152,7 @@ + /* -- Device Setup/Playback/Teardown -- */ + int (*ao_append_option)(ao_option **, const char *, const char *); + void (*ao_free_options)(ao_option *); +- char* (*ao_get_option)(ao_option *, const char* ); ++ char* (*ao_get_option)(ao_option *, const char* ); ++ void (*ao_set_metadata)(const char *buffer, unsigned int size); ++ void (*ao_set_metadata_coverart)(const char *buffer, unsigned int size); + }; +diff -ruP src/hairtunes.c src/hairtunes.c +--- src/hairtunes.c 2012-05-07 22:26:53.000000000 +0200 ++++ src/hairtunes.c 2012-05-08 18:45:51.000000000 +0200 +@@ -267,6 +267,16 @@ + fix_volume = 65536.0 * volume; + } + ++void hairtunes_set_metadata(const char *buffer, unsigned int size) ++{ ++ g_ao.ao_set_metadata(buffer, size); ++} ++ ++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size) ++{ ++ g_ao.ao_set_metadata_coverart(buffer, size); ++} ++ + void hairtunes_flush(void) + { + pthread_mutex_lock(&ab_mutex); +diff -ruP src/hairtunes.h src/hairtunes.h +--- src/hairtunes.h 2011-08-21 00:06:21.000000000 +0200 ++++ src/hairtunes.h 2012-05-08 18:46:00.000000000 +0200 +@@ -4,6 +4,8 @@ + int hairtunes_init(char *pAeskey, char *pAesiv, char *pFmtpstr, int pCtrlPort, int pTimingPort, + int pDataPort, char *pRtpHost, char*pPipeName, char *pLibaoDriver, char *pLibaoDeviceName, char *pLibaoDeviceId); + void hairtunes_setvolume(float vol); ++void hairtunes_set_metadata(const char *buffer, unsigned int size); ++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size); + void hairtunes_flush(void); + void hairtunes_cleanup(void); + +diff -ruP src/shairport.c src/shairport.c +--- src/shairport.c 2012-05-07 22:26:53.000000000 +0200 ++++ src/shairport.c 2012-05-08 18:45:30.000000000 +0200 +@@ -513,7 +513,8 @@ + while(1 == tMoreDataNeeded) + { + tError = readDataFromClient(pSock, &(tConn.recv)); +- if(!tError && strlen(tConn.recv.data) > 0) ++ //if(!tError && strlen(tConn.recv.data) > 0) ++ if(!tError && tConn.recv.current > 0) + { + xprintf("Finished Reading some data from client\n"); + // parse client request +@@ -632,7 +633,7 @@ + } + + tSize = (int) (tShortest - tFound); +- xprintf("Found %.*s length: %d\n", tSize, tFound, tSize); ++ xprintf("Found %s length: %d\n",tFound, tSize); + if(pReturnSize != NULL) + { + *pReturnSize = tSize; +@@ -744,7 +745,7 @@ + if(tContent != NULL) + { + int tContentSize = atoi(tContent); +- if(pConn->recv.marker == 0 || strlen(pConn->recv.data+pConn->recv.marker) != tContentSize) ++ if(pConn->recv.marker == 0 || pConn->recv.current-pConn->recv.marker != tContentSize) + { + if(isLogEnabledFor(HEADER_LOG_LEVEL)) + { +@@ -752,7 +753,7 @@ + if(pConn->recv.marker != 0) + { + xprintf("ContentPtr has %d, but needs %d\n", +- strlen(pConn->recv.data+pConn->recv.marker), tContentSize); ++ (pConn->recv.current-pConn->recv.marker), tContentSize); + } + } + // check if value in tContent > 2nd read from client. +@@ -989,15 +990,67 @@ + { + propogateCSeq(pConn); + int tSize = 0; ++ char *buffer = NULL; ++ char *contentType = getFromHeader(pConn->recv.data, "Content-Type", &tSize); ++ char *tContent = getFromHeader(pConn->recv.data, "Content-Length", NULL); ++ int iContentSize = 0; ++ int isJpg = 0; ++ ++ if(tContent != NULL) ++ { ++ iContentSize = atoi(tContent); ++ } ++ ++ if( tSize > 1 && ++ (strncmp(contentType, "application/x-dmap-tagged", tSize) == 0) || ++ (strncmp(contentType, "image/jpeg", tSize) == 0) ) ++ { ++ if( (pConn->recv.current - pConn->recv.marker) == iContentSize && pConn->recv.marker != 0) ++ { ++ if(strncmp(contentType, "image/jpeg", tSize) == 0) ++ { ++ isJpg = 1; ++ } ++ buffer = (char *)malloc(iContentSize * sizeof(char)); ++ memcpy(buffer, pConn->recv.data + pConn->recv.marker, iContentSize); ++ } ++ else ++ { ++ iContentSize = 0; ++ } ++ } ++ else ++ { ++ iContentSize = 0; ++ } + char *tVol = getFromHeader(pConn->recv.data, "volume", &tSize); +- xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol); ++ if( tVol) ++ { ++ xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol); ++ } + // TBD VOLUME + #ifndef XBMC + write(pConn->hairtunes->in[1], "vol: ", 5); + write(pConn->hairtunes->in[1], tVol, tSize); + write(pConn->hairtunes->in[1], "\n", 1); + #else +- hairtunes_setvolume(atof(tVol)); ++ if(tVol) ++ { ++ hairtunes_setvolume(atof(tVol)); ++ } ++ ++ if(iContentSize) ++ { ++ if(isJpg) ++ { ++ hairtunes_set_metadata_coverart(buffer, iContentSize); ++ } ++ else ++ { ++ hairtunes_set_metadata(buffer, iContentSize); ++ } ++ free(buffer); ++ } + #endif + xprintf("Finished writing data write data to hairtunes\n"); + } diff --git a/tools/rbp/depends/libshairport/Makefile b/tools/rbp/depends/libshairport/Makefile new file mode 100644 index 0000000000000..7993c0dafbe3e --- /dev/null +++ b/tools/rbp/depends/libshairport/Makefile @@ -0,0 +1,50 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=libshairport +VERSION=1.2.0.20310_lib +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +CONFIGURE=./configure --prefix=$(PREFIX) --host=$(HOST) + + +LIBDYLIB=$(SOURCE)/.libs/$(LIBNAME).so + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); patch -p0 < ../001_add_ao.patch + cd $(SOURCE); patch -p0 < ../002_fix_install_header.patch + cd $(SOURCE); patch -p0 < ../003_fix_deadlock.patch + cd $(SOURCE); patch -p0 < ../004_fix_bad_access.patch + cd $(SOURCE); patch -p0 < ../005_fix_shutdown.patch + cd $(SOURCE); patch -p0 < ../006_no_printf.patch + cd $(SOURCE); patch -p0 < ../007_fix_syslog_defines.patch + cd $(SOURCE); patch -p0 < ../008-add-missing-libs.patch + #cd $(SOURCE); patch -p0 < ../009_fix_ipv6.patch + cd $(SOURCE); patch -p0 < ../010_handle_metadata.patch + cd $(SOURCE); autoreconf -vif + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j 1 -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + rm -rf $(SOURCE) .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/libtool/.gitignore b/tools/rbp/depends/libtool/.gitignore new file mode 100644 index 0000000000000..717e6efd3584e --- /dev/null +++ b/tools/rbp/depends/libtool/.gitignore @@ -0,0 +1 @@ +libtool-2.2.6 diff --git a/tools/rbp/depends/libtool/Makefile b/tools/rbp/depends/libtool/Makefile new file mode 100644 index 0000000000000..b6edefee222c6 --- /dev/null +++ b/tools/rbp/depends/libtool/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +# lib name, version +LIBNAME=libtool +VERSION=2.2.6 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE)a.tar.gz + +# configuration settings +export PREFIX=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) + +LIBDYLIB=$(SOURCE)/bin/libtool + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/patchelf/.gitignore b/tools/rbp/depends/patchelf/.gitignore new file mode 100644 index 0000000000000..28b53ff265137 --- /dev/null +++ b/tools/rbp/depends/patchelf/.gitignore @@ -0,0 +1 @@ +patchelf-0.6pre25969 diff --git a/tools/rbp/depends/patchelf/Makefile b/tools/rbp/depends/patchelf/Makefile new file mode 100644 index 0000000000000..7846462a0353f --- /dev/null +++ b/tools/rbp/depends/patchelf/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +# lib name, version +LIBNAME=patchelf +VERSION=0.6pre25969 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +export PREFIX=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) + +LIBDYLIB=$(SOURCE)/src/patchelf + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/pkg-config/.gitignore b/tools/rbp/depends/pkg-config/.gitignore new file mode 100644 index 0000000000000..b6b0a86fe26bc --- /dev/null +++ b/tools/rbp/depends/pkg-config/.gitignore @@ -0,0 +1 @@ +pkg-config-0.23 diff --git a/tools/rbp/depends/pkg-config/Makefile b/tools/rbp/depends/pkg-config/Makefile new file mode 100644 index 0000000000000..716e6e3db7983 --- /dev/null +++ b/tools/rbp/depends/pkg-config/Makefile @@ -0,0 +1,42 @@ +include ../../Makefile.include + +# lib name, version +# lib name, version +LIBNAME=pkg-config +VERSION=0.23 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +PREFIX:=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) --enable-indirect-deps --with-pc-path=$(PREFIX)/lib/pkgconfig:$(PREFIX)/share/pkgconfig + +LIBDYLIB=$(SOURCE)/bin/pkg-config + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/tiff/.gitignore b/tools/rbp/depends/tiff/.gitignore new file mode 100644 index 0000000000000..48e22370892d5 --- /dev/null +++ b/tools/rbp/depends/tiff/.gitignore @@ -0,0 +1 @@ +tiff-3.8.2 diff --git a/tools/rbp/depends/tiff/Makefile b/tools/rbp/depends/tiff/Makefile new file mode 100644 index 0000000000000..e6539c38b79c9 --- /dev/null +++ b/tools/rbp/depends/tiff/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=tiff +VERSION=3.8.2 +SOURCE=$(LIBNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +CONFIGURE=./configure --prefix=$(PREFIX) --host=$(HOST) + +LIBDYLIB=$(SOURCE)/libtiff/.libs/lib$(LIBNAME).so + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/depends/tinyxml/.gitignore b/tools/rbp/depends/tinyxml/.gitignore new file mode 100644 index 0000000000000..f2c5bb8d1f1fe --- /dev/null +++ b/tools/rbp/depends/tinyxml/.gitignore @@ -0,0 +1 @@ +tinyxml diff --git a/tools/rbp/depends/tinyxml/Makefile b/tools/rbp/depends/tinyxml/Makefile new file mode 100644 index 0000000000000..98212e2c4ff30 --- /dev/null +++ b/tools/rbp/depends/tinyxml/Makefile @@ -0,0 +1,49 @@ +include ../../Makefile.include +include ../depends.mk + +# lib name, version +LIBNAME=tinyxml +VERSION=2.6.2 +SOURCE=$(LIBNAME) +ARCHIVE=$(LIBNAME)_2_6_2.tar.gz +BASE_URL=http://sourceforge.net/projects/tinyxml/files/tinyxml/$(VERSION) + +# configuration settings +CONFIGURE=./configure --prefix=$(PREFIX) --host=$(HOST) + +LIBDYLIB=$(SOURCE)/.libs/lib$(LIBNAME).so + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(LIBDYLIB) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + cd $(SOURCE); patch -p1 -i ../enforce-use-stl.patch + cd $(SOURCE); patch -p1 -i ../entity.patch + cd $(SOURCE); patch -p1 -i ../makefix.patch + cd $(SOURCE); sed -i -e '/^TINYXML_USE_STL/ s|=.*|=YES|' -e "s|^RELEASE_CFLAGS.*|& $(CXXFLAGS) -fPIC|" Makefile + echo $(SOURCE) > .gitignore + +$(LIBDYLIB): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) CFLAGS="$(CFLAGS) -DTIXML_USE_STL" CXXFLAGS="$(CXXFLAGS) -DTIXML_USE_STL" LDFLAGS="$(LDFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)" CC="$(CC)" CXX="$(CXX)" LD="$(LD)" LIBS="-ldl -lc -lstdc++" + cd $(SOURCE); $(CXX) -fPIC $(CXXFLAGS) $(LDFLAGS) -shared -o lib$(LIBNAME).so.$(VERSION) -Wl,-soname,lib$(LIBNAME).so.0 *.o + +.installed: + install -m 0644 $(SOURCE)/tinyxml.h $(PREFIX)/include/ + install -m 0644 $(SOURCE)/tinystr.h $(PREFIX)/include/ + install -m 0755 $(SOURCE)/libtinyxml.so.$(VERSION) $(PREFIX)/lib + cd $(PREFIX)/lib; rm -f libtinyxml.so; ln -s libtinyxml.so.$(VERSION) libtinyxml.so + cd $(PREFIX)/lib; rm -f libtinyxml.so.0; ln -s libtinyxml.so.$(VERSION) libtinyxml.so.0 + touch $@ + +clean: + make -C $(SOURCE) clean + rm -f .installed + +distclean:: + rm -rf $(SOURCE) .installed diff --git a/tools/rbp/depends/tinyxml/disable_xmltest.patch b/tools/rbp/depends/tinyxml/disable_xmltest.patch new file mode 100644 index 0000000000000..abc9f6c068f7f --- /dev/null +++ b/tools/rbp/depends/tinyxml/disable_xmltest.patch @@ -0,0 +1,20 @@ +--- a/Makefile 2012-05-04 07:53:05.910023997 +0200 ++++ b/Makefile 2012-05-04 07:53:19.030023561 +0200 +@@ -81,7 +81,7 @@ + # Targets of the build + #**************************************************************************** + +-OUTPUT := xmltest ++OUTPUT := xmltest + + all: ${OUTPUT} + +@@ -90,7 +90,7 @@ + # Source files + #**************************************************************************** + +-SRCS := tinyxml.cpp tinyxmlparser.cpp xmltest.cpp tinyxmlerror.cpp tinystr.cpp ++SRCS := tinyxml.cpp tinyxmlparser.cpp tinyxmlerror.cpp tinystr.cpp + + # Add on the sources for libraries + SRCS := ${SRCS} diff --git a/tools/rbp/depends/tinyxml/enforce-use-stl.patch b/tools/rbp/depends/tinyxml/enforce-use-stl.patch new file mode 100644 index 0000000000000..f930ba9c2c837 --- /dev/null +++ b/tools/rbp/depends/tinyxml/enforce-use-stl.patch @@ -0,0 +1,18 @@ +Description: TinyXml is built with TIXML_USE_STL, so we have to + enforce it when the library is used. +Author: Felix Geyer + +diff -Nur tinyxml-2.5.3/tinyxml.h tinyxml-2.5.3.patch/tinyxml.h +--- tinyxml-2.5.3/tinyxml.h 2007-05-07 00:41:23.000000000 +0200 ++++ tinyxml-2.5.3.patch/tinyxml.h 2009-07-08 22:32:03.000000000 +0200 +@@ -26,6 +26,10 @@ + #ifndef TINYXML_INCLUDED + #define TINYXML_INCLUDED + ++#ifndef TIXML_USE_STL ++ #define TIXML_USE_STL ++#endif ++ + #ifdef _MSC_VER + #pragma warning( push ) + #pragma warning( disable : 4530 ) diff --git a/tools/rbp/depends/tinyxml/entity.patch b/tools/rbp/depends/tinyxml/entity.patch new file mode 100644 index 0000000000000..2ccbd701ac495 --- /dev/null +++ b/tools/rbp/depends/tinyxml/entity.patch @@ -0,0 +1,64 @@ +? entity.patch +Index: tinyxml.cpp +=================================================================== +RCS file: /cvsroot/tinyxml/tinyxml/tinyxml.cpp,v +retrieving revision 1.105 +diff -u -r1.105 tinyxml.cpp +--- a/tinyxml.cpp 5 Jun 2010 19:06:57 -0000 1.105 ++++ b/tinyxml.cpp 19 Jul 2010 21:24:16 -0000 +@@ -57,30 +57,7 @@ + { + unsigned char c = (unsigned char) str[i]; + +- if ( c == '&' +- && i < ( (int)str.length() - 2 ) +- && str[i+1] == '#' +- && str[i+2] == 'x' ) +- { +- // Hexadecimal character reference. +- // Pass through unchanged. +- // © -- copyright symbol, for example. +- // +- // The -1 is a bug fix from Rob Laveaux. It keeps +- // an overflow from happening if there is no ';'. +- // There are actually 2 ways to exit this loop - +- // while fails (error case) and break (semicolon found). +- // However, there is no mechanism (currently) for +- // this function to return an error. +- while ( i<(int)str.length()-1 ) +- { +- outString->append( str.c_str() + i, 1 ); +- ++i; +- if ( str[i] == ';' ) +- break; +- } +- } +- else if ( c == '&' ) ++ if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; +Index: xmltest.cpp +=================================================================== +RCS file: /cvsroot/tinyxml/tinyxml/xmltest.cpp,v +retrieving revision 1.89 +diff -u -r1.89 xmltest.cpp +--- a/xmltest.cpp 5 Jun 2010 17:41:52 -0000 1.89 ++++ b/xmltest.cpp 19 Jul 2010 21:24:16 -0000 +@@ -1340,6 +1340,16 @@ + }*/ + } + ++ #ifdef TIXML_USE_STL ++ { ++ TiXmlDocument xml; ++ xml.Parse("foo&#xa+bar"); ++ std::string str; ++ str << xml; ++ XmlTest( "Entity escaping", "foo&#xa+bar", str.c_str() ); ++ } ++ #endif ++ + /* 1417717 experiment + { + TiXmlDocument xml; diff --git a/tools/rbp/depends/tinyxml/makefix.patch b/tools/rbp/depends/tinyxml/makefix.patch new file mode 100644 index 0000000000000..fbc65ac728094 --- /dev/null +++ b/tools/rbp/depends/tinyxml/makefix.patch @@ -0,0 +1,11 @@ +--- a/Makefile 2012-05-04 08:45:03.326587176 +0200 ++++ b/Makefile 2012-05-04 08:45:10.073253616 +0200 +@@ -102,7 +102,7 @@ + #**************************************************************************** + + ${OUTPUT}: ${OBJS} +- ${LD} -o $@ ${LDFLAGS} ${OBJS} ${LIBS} ${EXTRA_LIBS} ++ ${CXX} -o $@ ${LDFLAGS} ${OBJS} ${LIBS} ${EXTRA_LIBS} + + #**************************************************************************** + # common rules diff --git a/tools/rbp/depends/xbmc/Makefile b/tools/rbp/depends/xbmc/Makefile new file mode 100644 index 0000000000000..5c81a31038516 --- /dev/null +++ b/tools/rbp/depends/xbmc/Makefile @@ -0,0 +1,27 @@ +include ../../Makefile.include +include ../depends.mk + +SOURCE=../../../../ + +ifeq ($(USE_BUILDROOT),1) + export PATH:=$(PREFIX)/bin:$(BUILDROOT)/output/host/usr/bin:$(SYSROOT)/usr/bin:$(PATH) +endif + +CONFIGURE=./configure --prefix=$(PREFIX) --build=$(BUILD) --host=$(HOST) \ + --enable-gles --disable-sdl --disable-x11 --disable-xrandr --disable-openmax \ + --disable-optical-drive --disable-dvdcss --disable-joystick --disable-debug \ + --disable-crystalhd --disable-vtbdecoder --disable-vaapi --disable-vdpau \ + --disable-pulse --disable-projectm --with-platform=raspberry-pi --enable-optimizations \ + --enable-rpi-cec-api --enable-player=omxplayer + +all: configure + +clean: +distclean: + cd $(SOURCE); + +configure: + cd $(SOURCE); ./bootstrap + cd $(SOURCE); $(CONFIGURE) + #cd $(SOURCE); make -j $(JOBS) + #cd $(SOURCE); make install diff --git a/tools/rbp/depends/yasm/.gitignore b/tools/rbp/depends/yasm/.gitignore new file mode 100644 index 0000000000000..065ab0dec2131 --- /dev/null +++ b/tools/rbp/depends/yasm/.gitignore @@ -0,0 +1 @@ +yasm-1.1.0 diff --git a/tools/rbp/depends/yasm/Makefile b/tools/rbp/depends/yasm/Makefile new file mode 100644 index 0000000000000..10026b1d9419d --- /dev/null +++ b/tools/rbp/depends/yasm/Makefile @@ -0,0 +1,41 @@ +include ../../Makefile.include + +APPNAME=yasm +VERSION=1.1.0 +SOURCE=$(APPNAME)-$(VERSION) +ARCHIVE=$(SOURCE).tar.gz + +# configuration settings +PREFIX:=$(XBMCPREFIX) +export PATH:=$(PREFIX)/bin:$(PATH) +CONFIGURE=./configure --prefix=$(PREFIX) + +APP=$(SOURCE)/$(APPNAME) + +CLEAN_FILES=$(ARCHIVE) $(SOURCE) + +all: $(APP) .installed + +$(TARBALLS_LOCATION)/$(ARCHIVE): + $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE) + +$(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE) + -rm -rf $(SOURCE) + $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) + echo $(SOURCE) > .gitignore + cd $(SOURCE); $(CONFIGURE) + +$(APP): $(SOURCE) + make -j $(JOBS) -C $(SOURCE) + +.installed: + make -C $(SOURCE) install + touch $@ + +clean: + make -C $(SOURCE) clean + rm .installed + +distclean:: + rm -rf $(SOURCE) .installed + diff --git a/tools/rbp/setup-sdk.sh b/tools/rbp/setup-sdk.sh new file mode 100644 index 0000000000000..9da68cbcb6677 --- /dev/null +++ b/tools/rbp/setup-sdk.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +SCRIPT_PATH=$(cd `dirname $0` && pwd) + +USE_BUILDROOT=1 + +if [ "$USE_BUILDROOT" = "1" ]; then + BUILDROOT=/opt/xbmc-bcm/buildroot + TARBALLS=/opt/xbmc-tarballs + XBMCPREFIX=/opt/xbmc-bcm/xbmc-bin + + SDKSTAGE=$BUILDROOT/output/staging + TARGETFS=$BUILDROOT/output/target + TOOLCHAIN=$BUILDROOT/output/host/usr/ +else + TARBALLS=/opt/xbmc-tarballs + SDKSTAGE=/opt/bcm-rootfs + XBMCPREFIX=/opt/xbmc-bcm/xbmc-bin + TARGETFS=/opt/bcm-rootfs + TOOLCHAIN=/usr/local/bcm-gcc + BUILDROOT=/opt/bcm-rootfs +fi + +sudo mkdir -p $XBMCPREFIX +sudo chmod 777 $XBMCPREFIX +mkdir -p $XBMCPREFIX/lib +mkdir -p $XBMCPREFIX/include + +echo "SDKSTAGE=$SDKSTAGE" > $SCRIPT_PATH/Makefile.include +echo "XBMCPREFIX=$XBMCPREFIX" >> $SCRIPT_PATH/Makefile.include +echo "TARGETFS=$TARGETFS" >> $SCRIPT_PATH/Makefile.include +echo "TOOLCHAIN=$TOOLCHAIN" >> $SCRIPT_PATH/Makefile.include +echo "BUILDROOT=$BUILDROOT" >> $SCRIPT_PATH/Makefile.include +echo "USE_BUILDROOT=$USE_BUILDROOT" >> $SCRIPT_PATH/Makefile.include +echo "BASE_URL=http://mirrors.xbmc.org/build-deps/darwin-libs" >> $SCRIPT_PATH/Makefile.include +echo "TARBALLS_LOCATION=$TARBALLS" >> $SCRIPT_PATH/Makefile.include +echo "RETRIEVE_TOOL=/usr/bin/curl" >> $SCRIPT_PATH/Makefile.include +echo "RETRIEVE_TOOL_FLAGS=-Ls --create-dirs --output \$(TARBALLS_LOCATION)/\$(ARCHIVE)" >> $SCRIPT_PATH/Makefile.include +echo "ARCHIVE_TOOL=/bin/tar" >> $SCRIPT_PATH/Makefile.include +echo "ARCHIVE_TOOL_FLAGS=xf" >> $SCRIPT_PATH/Makefile.include +echo "JOBS=$((`grep -c processor /proc/cpuinfo -1`))" >> $SCRIPT_PATH/Makefile.include diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 93f76a1b9ccf0..9b7a6a9c247c2 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -4194,7 +4194,11 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart) // one of the players that allows gapless playback (paplayer, dvdplayer) if (m_pPlayer) { - if ( !(m_eCurrentPlayer == eNewCore && (m_eCurrentPlayer == EPC_DVDPLAYER || m_eCurrentPlayer == EPC_PAPLAYER)) ) + if ( !(m_eCurrentPlayer == eNewCore && (m_eCurrentPlayer == EPC_DVDPLAYER || m_eCurrentPlayer == EPC_PAPLAYER +#if defined(HAS_OMXPLAYER) + || m_eCurrentPlayer == EPC_OMXPLAYER +#endif + )) ) { delete m_pPlayer; m_pPlayer = NULL; @@ -5550,6 +5554,10 @@ void CApplication::SetHardwareVolume(float hardwareVolume) value = 1.0f; CAEFactory::SetVolume(value); + + /* for platforms where we do not have AE */ + if (m_pPlayer) + m_pPlayer->SetVolume(g_settings.m_fVolumeLevel); } int CApplication::GetVolume() const diff --git a/xbmc/SystemGlobals.cpp b/xbmc/SystemGlobals.cpp index 0d4bfbb378401..9593c84e15d47 100644 --- a/xbmc/SystemGlobals.cpp +++ b/xbmc/SystemGlobals.cpp @@ -49,6 +49,10 @@ +#ifdef TARGET_RASPBERRY_PI +#include "linux/RBP.h" +#endif + CGUISettings g_guiSettings; CSettings g_settings; @@ -80,3 +84,7 @@ CAlarmClock g_alarmClock; PLAYLIST::CPlayListPlayer g_playlistPlayer; CApplication g_application; + +#ifdef TARGET_RASPBERRY_PI + CRBP g_RBP; +#endif diff --git a/xbmc/cores/AudioEngine/AEFactory.cpp b/xbmc/cores/AudioEngine/AEFactory.cpp index 2e5a7780662e6..5cf295d8c310e 100644 --- a/xbmc/cores/AudioEngine/AEFactory.cpp +++ b/xbmc/cores/AudioEngine/AEFactory.cpp @@ -43,6 +43,10 @@ IAE *CAEFactory::GetEngine() bool CAEFactory::LoadEngine() { +#if defined(TARGET_RASPBERRY_PI) + return true; +#endif + bool loaded = false; std::string engine; diff --git a/xbmc/cores/AudioEngine/Utils/AEConvert.cpp b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp index 21724435a9904..838600f3c2788 100644 --- a/xbmc/cores/AudioEngine/Utils/AEConvert.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp @@ -156,7 +156,7 @@ unsigned int CAEConvert::S16LE_Float(uint8_t* data, const unsigned int samples, { static const float mul = 1.0f / (INT16_MAX + 0.5f); -#ifdef __arm__ +#if defined(__ARM_NEON__) || defined(__VFP_FP__) for (unsigned int i = 0; i < samples; i++) { __asm__ __volatile__ ( @@ -187,7 +187,7 @@ unsigned int CAEConvert::S16BE_Float(uint8_t* data, const unsigned int samples, { static const float mul = 1.0f / (INT16_MAX + 0.5f); -#ifdef __arm__ +#if defined(__ARM_NEON__) || defined(__VFP_FP__) for (unsigned int i = 0; i < samples; i++) { __asm__ __volatile__ ( diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp index e05e38adc47a4..0d6a15af0b315 100644 --- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp +++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp @@ -29,6 +29,7 @@ #include "utils/log.h" #include "utils/MathUtils.h" #include "settings/AdvancedSettings.h" +#include "cores/VideoRenderers/RenderFlags.h" CBaseRenderer::CBaseRenderer() @@ -191,30 +192,84 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight) RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RESOLUTION current, float& weight) { + RESOLUTION_INFO &curr = g_settings.m_ResInfo[current]; + + int iWidth = curr.iWidth; + int iHeight = curr.iHeight; + float fRefreshRate = fps; + + /* + * For 3D modes the following is assumed : + * + * fps is fps * 2 : 25 fps -> 50 fps + * + * side-by-side : + * + * width is width / 2 : 1920 -> 960 + * + * tob-bottom : + * + * height is height / 2 : 1080 -> 540 + * + */ + + if(m_iFlags & CONF_FLAGS_FORMAT_SBS) + { + iWidth /= 2; + fRefreshRate *= 2; + } + else if(m_iFlags & CONF_FLAGS_FORMAT_TB) + { + iHeight /= 2; + fRefreshRate *= 2; + } + + float last_diff = fRefreshRate; + // Find closest refresh rate for (size_t i = (int)RES_DESKTOP; i < g_settings.m_ResInfo.size(); i++) { - RESOLUTION_INFO &curr = g_settings.m_ResInfo[current]; RESOLUTION_INFO &info = g_settings.m_ResInfo[i]; //discard resolutions that are not the same width and height //or have a too low refreshrate - if (info.iWidth != curr.iWidth - || info.iHeight != curr.iHeight + if (info.iWidth != iWidth + || info.iHeight != iHeight || info.iScreen != curr.iScreen - || info.fRefreshRate < (fps * multiplier / 1.001) - 0.001) + || info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001) continue; - int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fps * multiplier) * 1000.0); - int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fps * multiplier) * 1000.0); + // For 3D choose the closest refresh rate + if(m_iFlags & CONF_FLAGS_FORMAT_SBS || m_iFlags & CONF_FLAGS_FORMAT_TB) + { + float diff = (info.fRefreshRate - fRefreshRate); + if(diff < 0) + diff *= -1.0f; - // Closer the better, prefer higher refresh rate if the same - if ((i_weight < c_weight) - || (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate)) - current = (RESOLUTION)i; + if(diff < last_diff) + { + last_diff = diff; + current = (RESOLUTION)i; + } + } + else + { + //discard resolutions that are not the same width and height + int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier) * 1000.0); + int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fRefreshRate * multiplier) * 1000.0); + + // Closer the better, prefer higher refresh rate if the same + if ((i_weight < c_weight) + || (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate)) + current = (RESOLUTION)i; + } } - weight = RefreshWeight(g_settings.m_ResInfo[current].fRefreshRate, fps * multiplier); + // For 3D overwrite weight + if(m_iFlags & CONF_FLAGS_FORMAT_SBS || m_iFlags & CONF_FLAGS_FORMAT_TB) + weight = 0; + else + weight = RefreshWeight(g_settings.m_ResInfo[current].fRefreshRate, fRefreshRate * multiplier); return current; } @@ -444,6 +499,11 @@ void CBaseRenderer::CalcNormalDisplayRect(float offsetX, float offsetY, float sc //*************************************************************************************** void CBaseRenderer::CalculateFrameAspectRatio(unsigned int desired_width, unsigned int desired_height) { + if(m_iFlags & CONF_FLAGS_FORMAT_SBS) + desired_width /= 2; + else if(m_iFlags & CONF_FLAGS_FORMAT_TB) + desired_height /= 2; + m_sourceFrameRatio = (float)desired_width / desired_height; // Check whether mplayer has decided that the size of the video file should be changed @@ -522,6 +582,10 @@ void CBaseRenderer::SetViewMode(int viewMode) RESOLUTION res = GetResolution(); float screenWidth = (float)(g_settings.m_ResInfo[res].Overscan.right - g_settings.m_ResInfo[res].Overscan.left); float screenHeight = (float)(g_settings.m_ResInfo[res].Overscan.bottom - g_settings.m_ResInfo[res].Overscan.top); + if(m_iFlags & CONF_FLAGS_FORMAT_SBS) + screenWidth /= 2; + else if(m_iFlags & CONF_FLAGS_FORMAT_TB) + screenHeight /= 2; // and the source frame ratio float sourceFrameRatio = GetAspectRatio(); diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.h b/xbmc/cores/VideoRenderers/BaseRenderer.h index c7f2b489b6195..d80613328e88f 100644 --- a/xbmc/cores/VideoRenderers/BaseRenderer.h +++ b/xbmc/cores/VideoRenderers/BaseRenderer.h @@ -120,6 +120,9 @@ class CBaseRenderer CRect m_oldDestRect; // destrect of the previous frame CRect m_sourceRect; + // rendering flags + unsigned m_iFlags; + const void* m_RenderUpdateCallBackCtx; RenderUpdateCallBackFn m_RenderUpdateCallBackFn; }; diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h index 87666e945b829..9185327c55d8d 100644 --- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h @@ -205,7 +205,6 @@ class CLinuxRendererGLES : public CBaseRenderer bool m_bValidated; std::vector m_formats; bool m_bImageReady; - unsigned m_iFlags; ERenderFormat m_format; GLenum m_textureTarget; unsigned short m_renderMethod; diff --git a/xbmc/cores/VideoRenderers/RenderFlags.h b/xbmc/cores/VideoRenderers/RenderFlags.h index 33d737b0bdafe..a7d57eb89e28c 100644 --- a/xbmc/cores/VideoRenderers/RenderFlags.h +++ b/xbmc/cores/VideoRenderers/RenderFlags.h @@ -70,4 +70,8 @@ #define CONF_FLAGS_TRC_GAMMA22 0x0800 #define CONF_FLAGS_TRC_GAMMA28 0x0c00 +/* defines 3d modes */ +#define CONF_FLAGS_FORMAT_SBS 0x001000 +#define CONF_FLAGS_FORMAT_TB 0x002000 + #endif diff --git a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp index dd5eee05aa33a..7f9bc9c2396c4 100644 --- a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp +++ b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp @@ -164,7 +164,9 @@ MsgQueueReturnCode CDVDMessageQueue::Get(CDVDMsg** pMsg, unsigned int iTimeoutIn if(m_list.empty() && m_bEmptied == false && priority == 0 && m_owner != "teletext") { +#if !defined(TARGET_RASPBERRY_PI) CLog::Log(LOGWARNING, "CDVDMessageQueue(%s)::Get - asked for new data packet, with nothing available", m_owner.c_str()); +#endif m_bEmptied = true; } diff --git a/xbmc/cores/omxplayer/BitstreamConverter.cpp b/xbmc/cores/omxplayer/BitstreamConverter.cpp new file mode 100644 index 0000000000000..8ae5d02e10487 --- /dev/null +++ b/xbmc/cores/omxplayer/BitstreamConverter.cpp @@ -0,0 +1,861 @@ +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif + +#include "BitstreamConverter.h" + +void CBitstreamConverter::bits_reader_set( bits_reader_t *br, uint8_t *buf, int len ) +{ + br->buffer = br->start = buf; + br->offbits = 0; + br->length = len; + br->oflow = 0; +} + +uint32_t CBitstreamConverter::read_bits( bits_reader_t *br, int nbits ) +{ + int i, nbytes; + uint32_t ret = 0; + uint8_t *buf; + + buf = br->buffer; + nbytes = (br->offbits + nbits)/8; + if ( ((br->offbits + nbits) %8 ) > 0 ) + nbytes++; + if ( (buf + nbytes) > (br->start + br->length) ) { + br->oflow = 1; + return 0; + } + for ( i=0; ioffbits; + ret = ((ret<>i)>>((nbytes*8)-nbits-br->offbits); + + br->offbits += nbits; + br->buffer += br->offbits / 8; + br->offbits %= 8; + + return ret; +} + +void CBitstreamConverter::skip_bits( bits_reader_t *br, int nbits ) +{ + br->offbits += nbits; + br->buffer += br->offbits / 8; + br->offbits %= 8; + if ( br->buffer > (br->start + br->length) ) { + br->oflow = 1; + } +} + +uint32_t CBitstreamConverter::get_bits( bits_reader_t *br, int nbits ) +{ + int i, nbytes; + uint32_t ret = 0; + uint8_t *buf; + + buf = br->buffer; + nbytes = (br->offbits + nbits)/8; + if ( ((br->offbits + nbits) %8 ) > 0 ) + nbytes++; + if ( (buf + nbytes) > (br->start + br->length) ) { + br->oflow = 1; + return 0; + } + for ( i=0; ioffbits; + ret = ((ret<>i)>>((nbytes*8)-nbits-br->offbits); + + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// +// GStreamer h264 parser +// Copyright (C) 2005 Michal Benes +// (C) 2008 Wim Taymans +// gsth264parse.c: +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +void CBitstreamConverter::nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size) +{ + bs->data = data; + bs->end = data + size; + bs->head = 0; + // fill with something other than 0 to detect + // emulation prevention bytes + bs->cache = 0xffffffff; +} + +uint32_t CBitstreamConverter::nal_bs_read(nal_bitstream *bs, int n) +{ + uint32_t res = 0; + int shift; + + if (n == 0) + return res; + + // fill up the cache if we need to + while (bs->head < n) + { + uint8_t a_byte; + bool check_three_byte; + + check_three_byte = true; +next_byte: + if (bs->data >= bs->end) + { + // we're at the end, can't produce more than head number of bits + n = bs->head; + break; + } + // get the byte, this can be an emulation_prevention_three_byte that we need + // to ignore. + a_byte = *bs->data++; + if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0)) + { + // next byte goes unconditionally to the cache, even if it's 0x03 + check_three_byte = false; + goto next_byte; + } + // shift bytes in cache, moving the head bits of the cache left + bs->cache = (bs->cache << 8) | a_byte; + bs->head += 8; + } + + // bring the required bits down and truncate + if ((shift = bs->head - n) > 0) + res = bs->cache >> shift; + else + res = bs->cache; + + // mask out required bits + if (n < 32) + res &= (1 << n) - 1; + bs->head = shift; + + return res; +} + +bool CBitstreamConverter::nal_bs_eos(nal_bitstream *bs) +{ + return (bs->data >= bs->end) && (bs->head == 0); +} + +// read unsigned Exp-Golomb code +int CBitstreamConverter::nal_bs_read_ue(nal_bitstream *bs) +{ + int i = 0; + + while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 32) + i++; + + return ((1 << i) - 1 + nal_bs_read(bs, i)); +} + +void CBitstreamConverter::parseh264_sps(uint8_t *sps, uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames) +{ + nal_bitstream bs; + sps_info_struct sps_info; + + nal_bs_init(&bs, sps, sps_size); + + sps_info.profile_idc = nal_bs_read(&bs, 8); + nal_bs_read(&bs, 1); // constraint_set0_flag + nal_bs_read(&bs, 1); // constraint_set1_flag + nal_bs_read(&bs, 1); // constraint_set2_flag + nal_bs_read(&bs, 1); // constraint_set3_flag + nal_bs_read(&bs, 4); // reserved + sps_info.level_idc = nal_bs_read(&bs, 8); + sps_info.sps_id = nal_bs_read_ue(&bs); + + if (sps_info.profile_idc == 100 || + sps_info.profile_idc == 110 || + sps_info.profile_idc == 122 || + sps_info.profile_idc == 244 || + sps_info.profile_idc == 44 || + sps_info.profile_idc == 83 || + sps_info.profile_idc == 86) + { + sps_info.chroma_format_idc = nal_bs_read_ue(&bs); + if (sps_info.chroma_format_idc == 3) + sps_info.separate_colour_plane_flag = nal_bs_read(&bs, 1); + sps_info.bit_depth_luma_minus8 = nal_bs_read_ue(&bs); + sps_info.bit_depth_chroma_minus8 = nal_bs_read_ue(&bs); + sps_info.qpprime_y_zero_transform_bypass_flag = nal_bs_read(&bs, 1); + + sps_info.seq_scaling_matrix_present_flag = nal_bs_read (&bs, 1); + if (sps_info.seq_scaling_matrix_present_flag) + { + /* TODO: unfinished */ + } + } + sps_info.log2_max_frame_num_minus4 = nal_bs_read_ue(&bs); + if (sps_info.log2_max_frame_num_minus4 > 12) + { // must be between 0 and 12 + return; + } + sps_info.pic_order_cnt_type = nal_bs_read_ue(&bs); + if (sps_info.pic_order_cnt_type == 0) + { + sps_info.log2_max_pic_order_cnt_lsb_minus4 = nal_bs_read_ue(&bs); + } + else if (sps_info.pic_order_cnt_type == 1) + { // TODO: unfinished + /* + delta_pic_order_always_zero_flag = gst_nal_bs_read (bs, 1); + offset_for_non_ref_pic = gst_nal_bs_read_se (bs); + offset_for_top_to_bottom_field = gst_nal_bs_read_se (bs); + + num_ref_frames_in_pic_order_cnt_cycle = gst_nal_bs_read_ue (bs); + for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) + offset_for_ref_frame[i] = gst_nal_bs_read_se (bs); + */ + } + + sps_info.max_num_ref_frames = nal_bs_read_ue(&bs); + sps_info.gaps_in_frame_num_value_allowed_flag = nal_bs_read(&bs, 1); + sps_info.pic_width_in_mbs_minus1 = nal_bs_read_ue(&bs); + sps_info.pic_height_in_map_units_minus1 = nal_bs_read_ue(&bs); + + sps_info.frame_mbs_only_flag = nal_bs_read(&bs, 1); + if (!sps_info.frame_mbs_only_flag) + sps_info.mb_adaptive_frame_field_flag = nal_bs_read(&bs, 1); + + sps_info.direct_8x8_inference_flag = nal_bs_read(&bs, 1); + + sps_info.frame_cropping_flag = nal_bs_read(&bs, 1); + if (sps_info.frame_cropping_flag) + { + sps_info.frame_crop_left_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_right_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_top_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_bottom_offset = nal_bs_read_ue(&bs); + } + + *interlaced = !sps_info.frame_mbs_only_flag; + *max_ref_frames = sps_info.max_num_ref_frames; +} + +const uint8_t *CBitstreamConverter::avc_find_startcode_internal(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *a = p + 4 - ((intptr_t)p & 3); + + for (end -= 3; p < a && p < end; p++) + { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + for (end -= 3; p < end; p += 4) + { + uint32_t x = *(const uint32_t*)p; + if ((x - 0x01010101) & (~x) & 0x80808080) // generic + { + if (p[1] == 0) + { + if (p[0] == 0 && p[2] == 1) + return p; + if (p[2] == 0 && p[3] == 1) + return p+1; + } + if (p[3] == 0) + { + if (p[2] == 0 && p[4] == 1) + return p+2; + if (p[4] == 0 && p[5] == 1) + return p+3; + } + } + } + + for (end += 3; p < end; p++) + { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + return end + 3; +} + +const uint8_t *CBitstreamConverter::avc_find_startcode(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *out= avc_find_startcode_internal(p, end); + if (pavio_wb32(pb, nal_end - nal_start); + m_dllAvFormat->avio_write(pb, nal_start, nal_end - nal_start); + size += 4 + nal_end - nal_start; + nal_start = nal_end; + } + return size; +} + +const int CBitstreamConverter::avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) +{ + AVIOContext *pb; + int ret = m_dllAvFormat->avio_open_dyn_buf(&pb); + if (ret < 0) + return ret; + + avc_parse_nal_units(pb, buf_in, *size); + + m_dllAvUtil->av_freep(buf); + *size = m_dllAvFormat->avio_close_dyn_buf(pb, buf); + return 0; +} + +const int CBitstreamConverter::isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len) +{ + // extradata from bytestream h264, convert to avcC atom data for bitstream + if (len > 6) + { + /* check for h264 start code */ + if (OMX_RB32(data) == 0x00000001 || OMX_RB24(data) == 0x000001) + { + uint8_t *buf=NULL, *end, *start; + uint32_t sps_size=0, pps_size=0; + uint8_t *sps=0, *pps=0; + + int ret = avc_parse_nal_units_buf(data, &buf, &len); + if (ret < 0) + return ret; + start = buf; + end = buf + len; + + /* look for sps and pps */ + while (end - buf > 4) + { + uint32_t size; + uint8_t nal_type; + size = FFMIN(OMX_RB32(buf), end - buf - 4); + buf += 4; + nal_type = buf[0] & 0x1f; + if (nal_type == 7) /* SPS */ + { + sps = buf; + sps_size = size; + } + else if (nal_type == 8) /* PPS */ + { + pps = buf; + pps_size = size; + } + buf += size; + } + if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX) + assert(0); + + m_dllAvFormat->avio_w8(pb, 1); /* version */ + m_dllAvFormat->avio_w8(pb, sps[1]); /* profile */ + m_dllAvFormat->avio_w8(pb, sps[2]); /* profile compat */ + m_dllAvFormat->avio_w8(pb, sps[3]); /* level */ + m_dllAvFormat->avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ + m_dllAvFormat->avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ + + m_dllAvFormat->avio_wb16(pb, sps_size); + m_dllAvFormat->avio_write(pb, sps, sps_size); + if (pps) + { + m_dllAvFormat->avio_w8(pb, 1); /* number of pps */ + m_dllAvFormat->avio_wb16(pb, pps_size); + m_dllAvFormat->avio_write(pb, pps, pps_size); + } + m_dllAvUtil->av_free(start); + } + else + { + m_dllAvFormat->avio_write(pb, data, len); + } + } + return 0; +} + +CBitstreamConverter::CBitstreamConverter() +{ + m_convert_bitstream = false; + m_convertBuffer = NULL; + m_convertSize = 0; + m_inputBuffer = NULL; + m_inputSize = 0; + m_to_annexb = false; + m_extradata = NULL; + m_extrasize = 0; + m_convert_3byteTo4byteNALSize = false; + m_dllAvUtil = NULL; + m_dllAvFormat = NULL; + m_convert_bytestream = false; +} + +CBitstreamConverter::~CBitstreamConverter() +{ + Close(); +} + +bool CBitstreamConverter::Open(enum CodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb) +{ + m_to_annexb = to_annexb; + + m_codec = codec; + + switch(codec) + { + case CODEC_ID_H264: + if (in_extrasize < 7 || in_extradata == NULL) + { + CLog::Log(LOGERROR, "CBitstreamConverter::Open avcC data too small or missing\n"); + return false; + } + // valid avcC data (bitstream) always starts with the value 1 (version) + if(m_to_annexb) + { + if ( *(char*)in_extradata == 1 ) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open bitstream to annexb init\n"); + m_convert_bitstream = BitstreamConvertInit(in_extradata, in_extrasize); + return true; + } + } + else + { + // valid avcC atom data always starts with the value 1 (version) + if ( *in_extradata != 1 ) + { + if (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 0 && in_extradata[3] == 1) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init\n"); + // video content is from x264 or from bytestream h264 (AnnexB format) + // NAL reformating to bitstream format needed + m_dllAvUtil = new DllAvUtil; + m_dllAvFormat = new DllAvFormat; + if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load()) + return false; + + AVIOContext *pb; + if (m_dllAvFormat->avio_open_dyn_buf(&pb) < 0) + return false; + m_convert_bytestream = true; + // create a valid avcC atom data from ffmpeg's extradata + isom_write_avcc(pb, in_extradata, in_extrasize); + // unhook from ffmpeg's extradata + in_extradata = NULL; + // extract the avcC atom data into extradata then write it into avcCData for VDADecoder + in_extrasize = m_dllAvFormat->avio_close_dyn_buf(pb, &in_extradata); + // make a copy of extradata contents + m_extradata = (uint8_t *)malloc(in_extrasize); + memcpy(m_extradata, in_extradata, in_extrasize); + m_extrasize = in_extrasize; + // done with the converted extradata, we MUST free using av_free + m_dllAvUtil->av_free(in_extradata); + return true; + } + else + { + CLog::Log(LOGNOTICE, "CBitstreamConverter::Open invalid avcC atom data"); + return false; + } + } + else + { + if (in_extradata[4] == 0xFE) + { + CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init 3 byte to 4 byte nal\n"); + // video content is from so silly encoder that think 3 byte NAL sizes + // are valid, setup to convert 3 byte NAL sizes to 4 byte. + m_dllAvUtil = new DllAvUtil; + m_dllAvFormat = new DllAvFormat; + if (!m_dllAvUtil->Load() || !m_dllAvFormat->Load()) + return false; + + in_extradata[4] = 0xFF; + m_convert_3byteTo4byteNALSize = true; + + m_extradata = (uint8_t *)malloc(in_extrasize); + memcpy(m_extradata, in_extradata, in_extrasize); + m_extrasize = in_extrasize; + return true; + } + } + } + return false; + break; + default: + return false; + break; + } + return false; +} + +void CBitstreamConverter::Close(void) +{ + if (m_convert_bitstream) + { + if (m_sps_pps_context.sps_pps_data) + { + free(m_sps_pps_context.sps_pps_data); + m_sps_pps_context.sps_pps_data = NULL; + } + if(m_convertBuffer) + free(m_convertBuffer); + m_convertSize = 0; + } + + if (m_convert_bytestream) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + } + + if(m_extradata) + free(m_extradata); + m_extradata = NULL; + m_extrasize = 0; + + m_inputBuffer = NULL; + m_inputSize = 0; + m_convert_3byteTo4byteNALSize = false; + + m_convert_bitstream = false; + + if (m_dllAvUtil) + { + delete m_dllAvUtil; + m_dllAvUtil = NULL; + } + if (m_dllAvFormat) + { + delete m_dllAvFormat; + m_dllAvFormat = NULL; + } +} + +bool CBitstreamConverter::Convert(uint8_t *pData, int iSize) +{ + if(m_convertBuffer) + free(m_convertBuffer); + m_convertBuffer = NULL; + m_convertSize = 0; + m_inputBuffer = NULL; + m_inputSize = 0; + + if (pData) + { + if(m_codec == CODEC_ID_H264) + { + if(m_to_annexb) + { + int demuxer_bytes = iSize; + + uint8_t *demuxer_content = pData; + + if (m_convert_bitstream) + { + // convert demuxer packet from bitstream to bytestream (AnnexB) + int bytestream_size = 0; + uint8_t *bytestream_buff = NULL; + + BitstreamConvert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size); + if (bytestream_buff && (bytestream_size > 0)) + { + m_convertSize = bytestream_size; + m_convertBuffer = bytestream_buff; + } + else + { + Close(); + m_inputBuffer = pData; + m_inputSize = iSize; + CLog::Log(LOGERROR, "CBitstreamConverter::Convert error converting. disable converter\n"); + } + } + else + { + m_inputBuffer = pData; + m_inputSize = iSize; + } + + return true; + } + else + { + m_inputBuffer = pData; + m_inputSize = iSize; + + if (m_convert_bytestream) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + + // convert demuxer packet from bytestream (AnnexB) to bitstream + AVIOContext *pb; + + if(m_dllAvFormat->avio_open_dyn_buf(&pb) < 0) + { + return false; + } + m_convertSize = avc_parse_nal_units(pb, pData, iSize); + m_convertSize = m_dllAvFormat->avio_close_dyn_buf(pb, &m_convertBuffer); + } + else if (m_convert_3byteTo4byteNALSize) + { + if(m_convertBuffer) + { + m_dllAvUtil->av_free(m_convertBuffer); + m_convertBuffer = NULL; + } + m_convertSize = 0; + + // convert demuxer packet from 3 byte NAL sizes to 4 byte + AVIOContext *pb; + if (m_dllAvFormat->avio_open_dyn_buf(&pb) < 0) + return false; + + uint32_t nal_size; + uint8_t *end = pData + iSize; + uint8_t *nal_start = pData; + while (nal_start < end) + { + nal_size = OMX_RB24(nal_start); + m_dllAvFormat->avio_wb16(pb, nal_size); + nal_start += 3; + m_dllAvFormat->avio_write(pb, nal_start, nal_size); + nal_start += nal_size; + } + + m_convertSize = m_dllAvFormat->avio_close_dyn_buf(pb, &m_convertBuffer); + } + return true; + } + } + } + + return false; +} + + +uint8_t *CBitstreamConverter::GetConvertBuffer() +{ + if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL) + return m_convertBuffer; + else + return m_inputBuffer; +} + +int CBitstreamConverter::GetConvertSize() +{ + if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL) + return m_convertSize; + else + return m_inputSize; +} + +uint8_t *CBitstreamConverter::GetExtraData() +{ + return m_extradata; +} +int CBitstreamConverter::GetExtraSize() +{ + return m_extrasize; +} + +bool CBitstreamConverter::BitstreamConvertInit(void *in_extradata, int in_extrasize) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + m_sps_pps_size = 0; + m_sps_pps_context.sps_pps_data = NULL; + + // nothing to filter + if (!in_extradata || in_extrasize < 6) + return false; + + uint16_t unit_size; + uint32_t total_size = 0; + uint8_t *out = NULL, unit_nb, sps_done = 0; + const uint8_t *extradata = (uint8_t*)in_extradata + 4; + static const uint8_t nalu_header[4] = {0, 0, 0, 1}; + + // retrieve length coded size + m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1; + if (m_sps_pps_context.length_size == 3) + return false; + + // retrieve sps and pps unit(s) + unit_nb = *extradata++ & 0x1f; // number of sps unit(s) + if (!unit_nb) + { + unit_nb = *extradata++; // number of pps unit(s) + sps_done++; + } + while (unit_nb--) + { + unit_size = extradata[0] << 8 | extradata[1]; + total_size += unit_size + 4; + if ( (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize) ) + { + free(out); + return false; + } + out = (uint8_t*)realloc(out, total_size); + if (!out) + return false; + + memcpy(out + total_size - unit_size - 4, nalu_header, 4); + memcpy(out + total_size - unit_size, extradata + 2, unit_size); + extradata += 2 + unit_size; + + if (!unit_nb && !sps_done++) + unit_nb = *extradata++; // number of pps unit(s) + } + + m_sps_pps_context.sps_pps_data = out; + m_sps_pps_context.size = total_size; + m_sps_pps_context.first_idr = 1; + + return true; +} + +bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + + uint8_t *buf = pData; + uint32_t buf_size = iSize; + uint8_t unit_type; + int32_t nal_size; + uint32_t cumul_size = 0; + const uint8_t *buf_end = buf + buf_size; + + do + { + if (buf + m_sps_pps_context.length_size > buf_end) + goto fail; + + if (m_sps_pps_context.length_size == 1) + nal_size = buf[0]; + else if (m_sps_pps_context.length_size == 2) + nal_size = buf[0] << 8 | buf[1]; + else + nal_size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + + buf += m_sps_pps_context.length_size; + unit_type = *buf & 0x1f; + + if (buf + nal_size > buf_end || nal_size < 0) + goto fail; + + // prepend only to the first type 5 NAL unit of an IDR picture + if (m_sps_pps_context.first_idr && unit_type == 5) + { + BitstreamAllocAndCopy(poutbuf, poutbuf_size, + m_sps_pps_context.sps_pps_data, m_sps_pps_context.size, buf, nal_size); + m_sps_pps_context.first_idr = 0; + } + else + { + BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size); + if (!m_sps_pps_context.first_idr && unit_type == 1) + m_sps_pps_context.first_idr = 1; + } + + buf += nal_size; + cumul_size += nal_size + m_sps_pps_context.length_size; + } while (cumul_size < buf_size); + + return true; + +fail: + free(*poutbuf); + *poutbuf = NULL; + *poutbuf_size = 0; + return false; +} + +void CBitstreamConverter::BitstreamAllocAndCopy( uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size) +{ + // based on h264_mp4toannexb_bsf.c (ffmpeg) + // which is Copyright (c) 2007 Benoit Fouet + // and Licensed GPL 2.1 or greater + + #define CHD_WB32(p, d) { \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d) >> 8; \ + ((uint8_t*)(p))[1] = (d) >> 16; \ + ((uint8_t*)(p))[0] = (d) >> 24; } + + uint32_t offset = *poutbuf_size; + uint8_t nal_header_size = offset ? 3 : 4; + + *poutbuf_size += sps_pps_size + in_size + nal_header_size; + *poutbuf = (uint8_t*)realloc(*poutbuf, *poutbuf_size); + if (sps_pps) + memcpy(*poutbuf + offset, sps_pps, sps_pps_size); + + memcpy(*poutbuf + sps_pps_size + nal_header_size + offset, in, in_size); + if (!offset) + { + CHD_WB32(*poutbuf + sps_pps_size, 1); + } + else + { + (*poutbuf + offset + sps_pps_size)[0] = 0; + (*poutbuf + offset + sps_pps_size)[1] = 0; + (*poutbuf + offset + sps_pps_size)[2] = 1; + } +} + + diff --git a/xbmc/cores/omxplayer/BitstreamConverter.h b/xbmc/cores/omxplayer/BitstreamConverter.h new file mode 100644 index 0000000000000..e72f9caeb078e --- /dev/null +++ b/xbmc/cores/omxplayer/BitstreamConverter.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef _BITSTREAMCONVERTER_H_ +#define _BITSTREAMCONVERTER_H_ + +#include +#include "DllAvUtil.h" +#include "DllAvFormat.h" +#include "DllAvFilter.h" +#include "DllAvCodec.h" + +typedef struct { + uint8_t *buffer, *start; + int offbits, length, oflow; +} bits_reader_t; + +//////////////////////////////////////////////////////////////////////////////////////////// +// TODO: refactor this so as not to need these ffmpeg routines. +// These are not exposed in ffmpeg's API so we dupe them here. +// AVC helper functions for muxers, +// * Copyright (c) 2006 Baptiste Coudurier +// This is part of FFmpeg +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +#define OMX_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x)) [1]) + +#define OMX_RB24(x) \ + ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) + +#define OMX_RB32(x) \ + ((((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) + +#define OMX_WB32(p, d) { \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d) >> 8; \ + ((uint8_t*)(p))[1] = (d) >> 16; \ + ((uint8_t*)(p))[0] = (d) >> 24; } + +typedef struct +{ + const uint8_t *data; + const uint8_t *end; + int head; + uint64_t cache; +} nal_bitstream; + +typedef struct +{ + int profile_idc; + int level_idc; + int sps_id; + + int chroma_format_idc; + int separate_colour_plane_flag; + int bit_depth_luma_minus8; + int bit_depth_chroma_minus8; + int qpprime_y_zero_transform_bypass_flag; + int seq_scaling_matrix_present_flag; + + int log2_max_frame_num_minus4; + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + + int max_num_ref_frames; + int gaps_in_frame_num_value_allowed_flag; + int pic_width_in_mbs_minus1; + int pic_height_in_map_units_minus1; + + int frame_mbs_only_flag; + int mb_adaptive_frame_field_flag; + + int direct_8x8_inference_flag; + + int frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; +} sps_info_struct; + +class CBitstreamConverter +{ +public: + CBitstreamConverter(); + ~CBitstreamConverter(); + // Required overrides + static void bits_reader_set( bits_reader_t *br, uint8_t *buf, int len ); + static uint32_t read_bits( bits_reader_t *br, int nbits ); + static void skip_bits( bits_reader_t *br, int nbits ); + static uint32_t get_bits( bits_reader_t *br, int nbits ); + + bool Open(enum CodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb); + void Close(void); + bool NeedConvert(void) { return m_convert_bitstream; }; + bool Convert(uint8_t *pData, int iSize); + uint8_t *GetConvertBuffer(void); + int GetConvertSize(); + uint8_t *GetExtraData(void); + int GetExtraSize(); + void parseh264_sps(uint8_t *sps, uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames); +protected: + // bytestream (Annex B) to bistream conversion support. + void nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size); + uint32_t nal_bs_read(nal_bitstream *bs, int n); + bool nal_bs_eos(nal_bitstream *bs); + int nal_bs_read_ue(nal_bitstream *bs); + const uint8_t *avc_find_startcode_internal(const uint8_t *p, const uint8_t *end); + const uint8_t *avc_find_startcode(const uint8_t *p, const uint8_t *end); + const int avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size); + const int avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size); + const int isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len); + // bitstream to bytestream (Annex B) conversion support. + bool BitstreamConvertInit(void *in_extradata, int in_extrasize); + bool BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size); + void BitstreamAllocAndCopy( uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size); + + typedef struct omx_bitstream_ctx { + uint8_t length_size; + uint8_t first_idr; + uint8_t *sps_pps_data; + uint32_t size; + } omx_bitstream_ctx; + + uint8_t *m_convertBuffer; + int m_convertSize; + uint8_t *m_inputBuffer; + int m_inputSize; + + uint32_t m_sps_pps_size; + omx_bitstream_ctx m_sps_pps_context; + bool m_convert_bitstream; + bool m_to_annexb; + + uint8_t *m_extradata; + int m_extrasize; + bool m_convert_3byteTo4byteNALSize; + bool m_convert_bytestream; + DllAvUtil *m_dllAvUtil; + DllAvFormat *m_dllAvFormat; + CodecID m_codec; +}; + +#endif diff --git a/xbmc/cores/omxplayer/DllOMX.h b/xbmc/cores/omxplayer/DllOMX.h new file mode 100644 index 0000000000000..4bad4604c1d5e --- /dev/null +++ b/xbmc/cores/omxplayer/DllOMX.h @@ -0,0 +1,123 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_OMXLIB) + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#include "DynamicDll.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////// + +class DllOMXInterface +{ +public: + virtual ~DllOMXInterface() {} + + virtual OMX_ERRORTYPE OMX_Init(void) = 0; + virtual OMX_ERRORTYPE OMX_Deinit(void) = 0; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) = 0; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) = 0; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) = 0; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) = 0; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) = 0; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) = 0; + +}; + +#if (defined USE_EXTERNAL_OMX) +class DllOMX : public DllDynamic, DllOMXInterface +{ +public: + virtual OMX_ERRORTYPE OMX_Init(void) + { return ::OMX_Init(); }; + virtual OMX_ERRORTYPE OMX_Deinit(void) + { return ::OMX_Deinit(); }; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) + { return ::OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks); }; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) + { return ::OMX_FreeHandle(hComponent); }; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) + { return ::OMX_GetComponentsOfRole(role, pNumComps, compNames); }; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) + { return ::OMX_GetRolesOfComponent(compName, pNumRoles, roles); }; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) + { return ::OMX_ComponentNameEnum(cComponentName, nNameLength, nIndex); }; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) + { return ::OMX_SetupTunnel(hOutput, nPortOutput, hInput, nPortInput); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllOMX: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllOMX : public DllDynamic, DllOMXInterface +{ + //DECLARE_DLL_WRAPPER(DllLibOpenMax, "/usr/lib/libnvomx.so") + DECLARE_DLL_WRAPPER(DllOMX, "/opt/vc/lib/libopenmaxil.so") + + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Init) + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Deinit) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_GetHandle, (OMX_HANDLETYPE *p1, OMX_STRING p2, OMX_PTR p3, OMX_CALLBACKTYPE *p4)) + DEFINE_METHOD1(OMX_ERRORTYPE, OMX_FreeHandle, (OMX_HANDLETYPE p1)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetComponentsOfRole, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetRolesOfComponent, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_ComponentNameEnum, (OMX_STRING p1, OMX_U32 p2, OMX_U32 p3)) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_SetupTunnel, (OMX_HANDLETYPE p1, OMX_U32 p2, OMX_HANDLETYPE p3, OMX_U32 p4)); + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(OMX_Init) + RESOLVE_METHOD(OMX_Deinit) + RESOLVE_METHOD(OMX_GetHandle) + RESOLVE_METHOD(OMX_FreeHandle) + RESOLVE_METHOD(OMX_GetComponentsOfRole) + RESOLVE_METHOD(OMX_GetRolesOfComponent) + RESOLVE_METHOD(OMX_ComponentNameEnum) + RESOLVE_METHOD(OMX_SetupTunnel) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +#endif diff --git a/xbmc/cores/omxplayer/Makefile.in b/xbmc/cores/omxplayer/Makefile.in new file mode 100644 index 0000000000000..0d6ec5c00ece0 --- /dev/null +++ b/xbmc/cores/omxplayer/Makefile.in @@ -0,0 +1,20 @@ +CXXFLAGS += -D__STDC_FORMAT_MACROS + +SRCS= \ + OMXPlayer.cpp \ + OMXAudio.cpp \ + OMXVideo.cpp \ + OMXAudioCodecOMX.cpp \ + OMXPlayerAudio.cpp \ + OMXPlayerVideo.cpp \ + OMXImage.cpp \ + BitstreamConverter.cpp + +LIB= omxplayer.a + +@abs_top_srcdir@/system/advancedsettings.xml: $(LIB) + cp -f omxplayer_advancedsettings.xml $@ + +include @abs_top_srcdir@/Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp new file mode 100644 index 0000000000000..6e57e402db31e --- /dev/null +++ b/xbmc/cores/omxplayer/OMXAudio.cpp @@ -0,0 +1,1482 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 d7o3g4q and RUNTiME +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include "OMXAudio.h" +#include "utils/log.h" + +#define CLASSNAME "COMXAudio" + +#include "linux/XMemUtils.h" + +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "guilib/LocalizeStrings.h" +#include "cores/AudioEngine/Utils/AEConvert.h" + +#ifndef VOLUME_MINIMUM +#define VOLUME_MINIMUM -6000 // -60dB +#endif + +using namespace std; + +#define OMX_MAX_CHANNELS 9 + +static enum AEChannel OMXChannelMap[OMX_MAX_CHANNELS] = +{ + AE_CH_FL , AE_CH_FR, + AE_CH_FC , AE_CH_LFE, + AE_CH_BL , AE_CH_BR, + AE_CH_SL , AE_CH_SR, + AE_CH_RAW +}; + +static enum OMX_AUDIO_CHANNELTYPE OMXChannels[OMX_MAX_CHANNELS] = +{ + OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, + OMX_AUDIO_ChannelCF, OMX_AUDIO_ChannelLFE, + OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR, + OMX_AUDIO_ChannelLS, OMX_AUDIO_ChannelRS, + OMX_AUDIO_ChannelNone +}; + +static unsigned int WAVEChannels[OMX_MAX_CHANNELS] = +{ + SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, + SPEAKER_TOP_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, + SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, + SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, + SPEAKER_SIDE_RIGHT +}; + +static const uint16_t AC3Bitrates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640}; +static const uint16_t AC3FSCod [] = {48000, 44100, 32000, 0}; + +static const uint16_t DTSFSCod [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0}; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +//*********************************************************************************************** +COMXAudio::COMXAudio() : + m_pCallback (NULL ), + m_Initialized (false ), + m_Pause (false ), + m_CanPause (false ), + m_CurrentVolume (0 ), + m_Passthrough (false ), + m_HWDecode (false ), + m_BytesPerSec (0 ), + m_BufferLen (0 ), + m_ChunkLen (0 ), + m_OutputChannels (0 ), + m_BitsPerSample (0 ), + m_omx_clock (NULL ), + m_av_clock (NULL ), + m_external_clock (false ), + m_first_frame (true ), + m_LostSync (true ), + m_SampleRate (0 ), + m_eEncoding (OMX_AUDIO_CodingPCM), + m_extradata (NULL ), + m_extrasize (0 ), + m_last_pts (DVD_NOPTS_VALUE) +{ + m_vizBufferSize = m_vizRemapBufferSize = VIS_PACKET_SIZE * sizeof(float); + m_vizRemapBuffer = (uint8_t *)_aligned_malloc(m_vizRemapBufferSize,16); + m_vizBuffer = (uint8_t *)_aligned_malloc(m_vizBufferSize,16); +} + +COMXAudio::~COMXAudio() +{ + if(m_Initialized) + Deinitialize(); + + _aligned_free(m_vizRemapBuffer); + _aligned_free(m_vizBuffer); +} + + +CAEChannelInfo COMXAudio::GetChannelLayout(AEAudioFormat format) +{ + unsigned int count = 0; + + if(format.m_dataFormat == AE_FMT_AC3 || + format.m_dataFormat == AE_FMT_DTS || + format.m_dataFormat == AE_FMT_EAC3) + count = 2; + else if (format.m_dataFormat == AE_FMT_TRUEHD || + format.m_dataFormat == AE_FMT_DTSHD) + count = 8; + else + { + for (unsigned int c = 0; c < 8; ++c) + { + for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i) + { + if (format.m_channelLayout[i] == OMXChannelMap[c]) + { + count = c + 1; + break; + } + } + } + } + + CAEChannelInfo info; + for (unsigned int i = 0; i < count; ++i) + info += OMXChannelMap[i]; + + return info; +} + +bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode) +{ + m_HWDecode = bUseHWDecode; + m_Passthrough = bUsePassthrough; + + m_format = format; + + if(hints.samplerate == 0) + return false; + + /* passthrough overwrites hw decode */ + if(m_Passthrough) + { + m_HWDecode = false; + } + else if(m_HWDecode) + { + /* check again if we are capable to hw decode the format */ + m_HWDecode = CanHWDecode(hints.codec); + } + SetCodingType(format.m_dataFormat); + + SetClock(clock); + + if(hints.extrasize > 0 && hints.extradata != NULL) + { + m_extrasize = hints.extrasize; + m_extradata = (uint8_t *)malloc(m_extrasize); + memcpy(m_extradata, hints.extradata, hints.extrasize); + } + + return Initialize(format, device); +} + +bool COMXAudio::Initialize(AEAudioFormat format, std::string& device) +{ + if(m_Initialized) + Deinitialize(); + + m_format = format; + + if(m_format.m_channelLayout.Count() == 0) + return false; + + if(!m_dllAvUtil.Load()) + return false; + + if(m_av_clock == NULL) + { + /* no external clock set. generate one */ + m_external_clock = false; + + m_av_clock = new OMXClock(); + + if(!m_av_clock->OMXInitialize(false, true)) + { + delete m_av_clock; + m_av_clock = NULL; + CLog::Log(LOGERROR, "COMXAudio::Initialize error creating av clock\n"); + return false; + } + } + + m_omx_clock = m_av_clock->GetOMXClock(); + + /* + m_Passthrough = false; + + if(OMX_IS_RAW(m_format.m_dataFormat)) + m_Passthrough =true; + */ + + m_drc = 0; + + m_CurrentVolume = g_settings.m_fVolumeLevel; + + memset(m_input_channels, 0x0, sizeof(m_input_channels)); + memset(m_output_channels, 0x0, sizeof(m_output_channels)); + memset(&m_wave_header, 0x0, sizeof(m_wave_header)); + + for(int i = 0; i < OMX_MAX_CHANNELS; i++) + m_pcm_input.eChannelMapping[i] = OMX_AUDIO_ChannelNone; + + m_output_channels[0] = OMX_AUDIO_ChannelLF; + m_output_channels[1] = OMX_AUDIO_ChannelRF; + m_output_channels[2] = OMX_AUDIO_ChannelMax; + + m_input_channels[0] = OMX_AUDIO_ChannelLF; + m_input_channels[1] = OMX_AUDIO_ChannelRF; + m_input_channels[2] = OMX_AUDIO_ChannelMax; + + m_OutputChannels = 2; + m_wave_header.Format.nChannels = m_OutputChannels; + m_wave_header.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + + if (!m_Passthrough) + { + /* setup output channel map */ + /* + int ch = 0, map; + int chan = 0; + m_OutputChannels = 0; + + for (unsigned int ch = 0; ch < m_format.m_channelLayout.Count(); ++ch) + { + for(map = 0; map < OMX_MAX_CHANNELS; ++map) + { + if (m_output_channels[ch] == OMXChannelMap[map]) + { + printf("output %d\n", chan); + m_output_channels[chan] = OMXChannels[map]; + chan++; + break; + } + } + } + + m_OutputChannels = chan; + */ + + /* setup input channel map */ + int map = 0; + int chan = 0; + + for (unsigned int ch = 0; ch < m_format.m_channelLayout.Count(); ++ch) + { + for(map = 0; map < OMX_MAX_CHANNELS; ++map) + { + if (m_format.m_channelLayout[ch] == OMXChannelMap[map]) + { + m_input_channels[chan] = OMXChannels[map]; + m_wave_header.dwChannelMask |= WAVEChannels[map]; + chan++; + break; + } + } + } + + m_vizRemap.Initialize(m_format.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true); + } + + OMX_INIT_STRUCTURE(m_pcm_output); + OMX_INIT_STRUCTURE(m_pcm_input); + + memcpy(m_pcm_output.eChannelMapping, m_output_channels, sizeof(m_output_channels)); + memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels)); + + // set the m_pcm_output parameters + m_pcm_output.eNumData = OMX_NumericalDataSigned; + m_pcm_output.eEndian = OMX_EndianLittle; + m_pcm_output.bInterleaved = OMX_TRUE; + m_pcm_output.nBitPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat); + m_pcm_output.ePCMMode = OMX_AUDIO_PCMModeLinear; + m_pcm_output.nChannels = m_OutputChannels; + m_pcm_output.nSamplingRate = m_format.m_sampleRate; + + m_SampleRate = m_format.m_sampleRate; + m_BitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat); + m_BufferLen = m_BytesPerSec = m_format.m_sampleRate * + (CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3) * + m_format.m_channelLayout.Count(); + m_BufferLen *= AUDIO_BUFFER_SECONDS; + m_ChunkLen = 6144; + + m_wave_header.Samples.wSamplesPerBlock = 0; + m_wave_header.Format.nChannels = m_format.m_channelLayout.Count(); + m_wave_header.Format.nBlockAlign = m_format.m_channelLayout.Count() * + (CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3); + m_wave_header.Format.wFormatTag = WAVE_FORMAT_PCM; + m_wave_header.Format.nSamplesPerSec = m_format.m_sampleRate; + m_wave_header.Format.nAvgBytesPerSec = m_BytesPerSec; + m_wave_header.Format.wBitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat); + m_wave_header.Samples.wValidBitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat); + m_wave_header.Format.cbSize = 0; + m_wave_header.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + m_pcm_input.eNumData = OMX_NumericalDataSigned; + m_pcm_input.eEndian = OMX_EndianLittle; + m_pcm_input.bInterleaved = OMX_TRUE; + m_pcm_input.nBitPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat); + m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear; + m_pcm_input.nChannels = m_format.m_channelLayout.Count(); + m_pcm_input.nSamplingRate = m_format.m_sampleRate; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + std::string componentName = ""; + + componentName = "OMX.broadcom.audio_render"; + if(!m_omx_render.Initialize((const std::string)componentName, OMX_IndexParamAudioInit)) + return false; + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; + OMX_INIT_STRUCTURE(audioDest); + strncpy((char *)audioDest.sName, device.c_str(), strlen(device.c_str())); + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest); + if (omx_err != OMX_ErrorNone) + return false; + + OMX_CONFIG_BOOLEANTYPE configBool; + OMX_INIT_STRUCTURE(configBool); + configBool.bEnabled = OMX_FALSE; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmClockReferenceSource, &configBool); + if (omx_err != OMX_ErrorNone) + return false; + + componentName = "OMX.broadcom.audio_decode"; + if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamAudioInit)) + return false; + + if(!m_Passthrough) + { + componentName = "OMX.broadcom.audio_mixer"; + if(!m_omx_mixer.Initialize((const std::string)componentName, OMX_IndexParamAudioInit)) + return false; + } + + if(m_Passthrough) + { + OMX_CONFIG_BOOLEANTYPE boolType; + OMX_INIT_STRUCTURE(boolType); + boolType.bEnabled = OMX_TRUE; + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmDecoderPassThrough, &boolType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error OMX_IndexParamBrcmDecoderPassThrough 0x%08x", omx_err); + return false; + } + } + + // set up the number/size of buffers + OMX_PARAM_PORTDEFINITIONTYPE port_param; + OMX_INIT_STRUCTURE(port_param); + port_param.nPortIndex = m_omx_decoder.GetInputPort(); + + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_param); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error get OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + port_param.format.audio.eEncoding = m_eEncoding; + + port_param.nBufferSize = m_ChunkLen; + port_param.nBufferCountActual = m_BufferLen / m_ChunkLen; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_param); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error set OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + if(m_HWDecode) + { + OMX_AUDIO_PARAM_PORTFORMATTYPE formatType; + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_decoder.GetInputPort(); + + formatType.eEncoding = m_eEncoding; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + return false; + } + } + + m_omx_tunnel_clock.Initialize(m_omx_clock, m_omx_clock->GetInputPort(), &m_omx_render, m_omx_render.GetInputPort()+1); + + omx_err = m_omx_tunnel_clock.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize m_omx_tunnel_clock.Establish\n"); + return false; + } + + if(!m_external_clock) + { + omx_err = m_omx_clock->SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize m_omx_clock.SetStateForComponent\n"); + return false; + } + } + + omx_err = m_omx_decoder.AllocInputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error alloc buffers 0x%08x", omx_err); + return false; + } + + if(!m_Passthrough) + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_mixer, m_omx_mixer.GetInputPort()); + omx_err = m_omx_tunnel_decoder.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + + m_omx_tunnel_mixer.Initialize(&m_omx_mixer, m_omx_mixer.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); + omx_err = m_omx_tunnel_mixer.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_mixer.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + } + else + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); + omx_err = m_omx_tunnel_decoder.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_decoder.Establish 0x%08x", omx_err); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + } + + omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - Error setting OMX_StateExecuting 0x%08x", omx_err); + return false; + } + + if(m_eEncoding == OMX_AUDIO_CodingPCM) + { + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - buffer error 0x%08x", omx_err); + return false; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = sizeof(m_wave_header); + if(omx_buffer->nFilledLen > omx_buffer->nAllocLen) + { + CLog::Log(LOGERROR, "COMXAudio::Initialize - omx_buffer->nFilledLen > omx_buffer->nAllocLen"); + return false; + } + memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen); + memcpy((unsigned char *)omx_buffer->pBuffer, &m_wave_header, omx_buffer->nFilledLen); + omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + } + else if(m_HWDecode) + { + // send decoder config + if(m_extrasize > 0 && m_extradata != NULL) + { + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return false; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = m_extrasize; + if(omx_buffer->nFilledLen > omx_buffer->nAllocLen) + { + CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__); + return false; + } + + memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen); + memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen); + omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + } + } + + m_Initialized = true; + m_first_frame = true; + m_last_pts = DVD_NOPTS_VALUE; + + SetCurrentVolume(m_CurrentVolume); + + CLog::Log(LOGDEBUG, "COMXAudio::Initialize Ouput bps %d samplerate %d channels %d buffer size %d bytes per second %d", + (int)m_pcm_output.nBitPerSample, (int)m_pcm_output.nSamplingRate, (int)m_pcm_output.nChannels, m_BufferLen, m_BytesPerSec); + CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d buffer size %d bytes per second %d", + (int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, m_BufferLen, m_BytesPerSec); + CLog::Log(LOGDEBUG, "COMXAudio::Initialize device %s passthrough %d hwdecode %d external clock %d", + device.c_str(), m_Passthrough, m_HWDecode, m_external_clock); + + m_av_clock->OMXStateExecute(false); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Deinitialize() +{ + if(!m_Initialized) + return true; + + /* + if(m_av_clock && !m_external_clock) + m_av_clock->OMXStop(); + */ + + if(m_av_clock && !m_external_clock) + { + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + } + + m_omx_tunnel_decoder.Flush(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Flush(); + m_omx_tunnel_clock.Flush(); + + m_omx_tunnel_clock.Deestablish(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Deestablish(); + m_omx_tunnel_decoder.Deestablish(); + + m_omx_decoder.FlushInput(); + + m_omx_render.Deinitialize(); + if(!m_Passthrough) + m_omx_mixer.Deinitialize(); + m_omx_decoder.Deinitialize(); + + m_Initialized = false; + m_BytesPerSec = 0; + m_BufferLen = 0; + + if(m_av_clock && !m_external_clock) + { + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + } + + if(!m_external_clock && m_av_clock != NULL) + { + delete m_av_clock; + m_av_clock = NULL; + m_external_clock = false; + } + + m_omx_clock = NULL; + m_av_clock = NULL; + + m_Initialized = false; + m_LostSync = true; + m_HWDecode = false; + + if(m_extradata) + free(m_extradata); + m_extradata = NULL; + m_extrasize = 0; + + m_dllAvUtil.Unload(); + + m_first_frame = true; + m_last_pts = DVD_NOPTS_VALUE; + + return true; +} + +void COMXAudio::Flush() +{ + if(!m_Initialized) + return; + + m_omx_decoder.FlushInput(); + m_omx_tunnel_decoder.Flush(); + if(!m_Passthrough) + m_omx_tunnel_mixer.Flush(); + + m_last_pts = DVD_NOPTS_VALUE; + m_LostSync = true; + //m_first_frame = true; +} + +//*********************************************************************************************** +bool COMXAudio::Pause() +{ + if (!m_Initialized) + return -1; + + if(m_Pause) return true; + m_Pause = true; + + m_omx_decoder.SetStateForComponent(OMX_StatePause); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Resume() +{ + if (!m_Initialized) + return -1; + + if(!m_Pause) return true; + m_Pause = false; + + m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + + return true; +} + +//*********************************************************************************************** +bool COMXAudio::Stop() +{ + if (!m_Initialized) + return -1; + + Flush(); + + m_Pause = false; + + return true; +} + +//*********************************************************************************************** +long COMXAudio::GetCurrentVolume() const +{ + return m_CurrentVolume; +} + +//*********************************************************************************************** +void COMXAudio::Mute(bool bMute) +{ + if(!m_Initialized) + return; + + if (bMute) + SetCurrentVolume(VOLUME_MINIMUM); + else + SetCurrentVolume(m_CurrentVolume); +} + +//*********************************************************************************************** +bool COMXAudio::SetCurrentVolume(float fVolume) +{ + if(!m_Initialized || m_Passthrough) + return -1; + + m_CurrentVolume = fVolume; + + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCTURE(volume); + volume.nPortIndex = m_omx_render.GetInputPort(); + + volume.bLinear = OMX_TRUE; + float hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, fVolume)) * 100.0f; + volume.sVolume.nValue = (int)hardwareVolume; + + m_omx_render.SetConfig(OMX_IndexConfigAudioVolume, &volume); + + return true; +} + + +//*********************************************************************************************** +unsigned int COMXAudio::AddPackets(const void* data, unsigned int len) +{ + return AddPackets(data, len, 0, 0); +} + +//*********************************************************************************************** +unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dts, double pts) +{ + if(!m_Initialized) + { + CLog::Log(LOGERROR,"COMXAudio::AddPackets - sanity failed. no valid play handle!"); + return len; + } + + m_vizBufferSamples = 0; + + if (m_pCallback && len) + { + /* input samples */ + m_vizBufferSamples = len / (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3); + CAEConvert::AEConvertToFn m_convertFn = CAEConvert::ToFloat(AE_FMT_S16LE); + /* input frames */ + unsigned int frames = m_vizBufferSamples / m_format.m_channelLayout.Count(); + + /* check convert buffer */ + CheckOutputBufferSize((void **)&m_vizBuffer, &m_vizBufferSize, m_vizBufferSamples * (CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3)); + + /* convert to float */ + m_convertFn((uint8_t *)data, m_vizBufferSamples, (float *)m_vizBuffer); + + /* check remap buffer */ + CheckOutputBufferSize((void **)&m_vizRemapBuffer, &m_vizRemapBufferSize, frames * 2 * (CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3)); + + /* remap */ + m_vizRemap.Remap((float *)m_vizBuffer, (float*)m_vizRemapBuffer, frames); + + /* output samples */ + m_vizBufferSamples = m_vizBufferSamples / m_format.m_channelLayout.Count() * 2; + + /* viz size is limited */ + if(m_vizBufferSamples > VIS_PACKET_SIZE) + m_vizBufferSamples = VIS_PACKET_SIZE; + + if(m_pCallback) + m_pCallback->OnAudioData((float *)m_vizRemapBuffer, m_vizBufferSamples); + } + + if(m_eEncoding == OMX_AUDIO_CodingDTS && m_LostSync && (m_Passthrough || m_HWDecode)) + { + int skip = SyncDTS((uint8_t *)data, len); + if(skip > 0) + return len; + } + + if(m_eEncoding == OMX_AUDIO_CodingDDP && m_LostSync && (m_Passthrough || m_HWDecode)) + { + int skip = SyncAC3((uint8_t *)data, len); + if(skip > 0) + return len; + } + + unsigned int demuxer_bytes = (unsigned int)len; + uint8_t *demuxer_content = (uint8_t *)data; + + OMX_ERRORTYPE omx_err; + + OMX_BUFFERHEADERTYPE *omx_buffer = NULL; + + while(demuxer_bytes) + { + // 200ms timeout + omx_buffer = m_omx_decoder.GetInputBuffer(200); + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "COMXAudio::Decode timeout\n"); + return len; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFlags = 0; + + omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes; + memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen); + + uint64_t val = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts; + + if(m_av_clock->AudioStart()) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME; + + m_last_pts = pts; + + CLog::Log(LOGDEBUG, "ADec : setStartTime %f\n", (float)val / DVD_TIME_BASE); + m_av_clock->AudioStart(false); + } + else + { + if(pts == DVD_NOPTS_VALUE) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + m_last_pts = pts; + } + else if (m_last_pts != pts) + { + if(pts > m_last_pts) + m_last_pts = pts; + else + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;; + } + else if (m_last_pts == pts) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + } + } + + omx_buffer->nTimeStamp = ToOMXTime(val); + + demuxer_bytes -= omx_buffer->nFilledLen; + demuxer_content += omx_buffer->nFilledLen; + + if(demuxer_bytes == 0) + omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + int nRetry = 0; + while(true) + { + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err == OMX_ErrorNone) + { + break; + } + else + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + nRetry++; + } + if(nRetry == 5) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() finaly failed\n", CLASSNAME, __func__); + return 0; + } + } + + if(m_first_frame) + { + m_first_frame = false; + //m_omx_render.WaitForEvent(OMX_EventPortSettingsChanged); + + m_omx_render.DisablePort(m_omx_render.GetInputPort(), false); + if(!m_Passthrough) + { + m_omx_mixer.DisablePort(m_omx_mixer.GetOutputPort(), false); + m_omx_mixer.DisablePort(m_omx_mixer.GetInputPort(), false); + } + m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false); + + if(!m_Passthrough) + { + if(m_HWDecode) + { + OMX_INIT_STRUCTURE(m_pcm_input); + m_pcm_input.nPortIndex = m_omx_decoder.GetOutputPort(); + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 1 omx_err(0x%08x)\n", omx_err); + } + } + + if ((m_pcm_input.nChannels > m_pcm_output.nChannels) &&g_guiSettings.GetBool("audiooutput.normalizelevels")) + { + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCTURE(volume); + volume.nPortIndex = m_omx_mixer.GetInputPort(); + volume.bLinear = OMX_FALSE; + volume.sVolume.nValue = (int)(g_advancedSettings.m_ac3Gain*100.0f+0.5f); + m_omx_mixer.SetConfig(OMX_IndexConfigAudioVolume, &volume); + } + + memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels)); + m_pcm_input.nSamplingRate = m_format.m_sampleRate; + + /* setup mixer input */ + m_pcm_input.nPortIndex = m_omx_mixer.GetInputPort(); + omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 input omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_mixer.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 input omx_err(0x%08x)\n", omx_err); + } + + m_pcm_output.nSamplingRate = m_format.m_sampleRate; + + /* setup mixer output */ + m_pcm_output.nPortIndex = m_omx_mixer.GetOutputPort(); + omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 output omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_mixer.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 output omx_err(0x%08x)\n", omx_err); + } + + m_pcm_output.nSamplingRate = m_format.m_sampleRate; + + m_pcm_output.nPortIndex = m_omx_render.GetInputPort(); + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error SetParameter 1 render omx_err(0x%08x)\n", omx_err); + } + omx_err = m_omx_render.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error GetParameter 2 render omx_err(0x%08x)\n", omx_err); + } + + PrintPCM(&m_pcm_input, std::string("input")); + PrintPCM(&m_pcm_output, std::string("output")); + } + else + { + m_pcm_output.nPortIndex = m_omx_decoder.GetOutputPort(); + m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output); + PrintPCM(&m_pcm_output, std::string("output")); + + OMX_AUDIO_PARAM_PORTFORMATTYPE formatType; + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_render.GetInputPort(); + + omx_err = m_omx_render.GetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + assert(0); + } + + formatType.eEncoding = m_eEncoding; + + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXAudio::AddPackets error OMX_IndexParamAudioPortFormat omx_err(0x%08x)\n", omx_err); + assert(0); + } + + if(m_eEncoding == OMX_AUDIO_CodingDDP) + { + OMX_AUDIO_PARAM_DDPTYPE m_ddParam; + OMX_INIT_STRUCTURE(m_ddParam); + + m_ddParam.nPortIndex = m_omx_render.GetInputPort(); + + m_ddParam.nChannels = m_format.m_channelLayout.Count(); //(m_InputChannels == 6) ? 8 : m_InputChannels; + m_ddParam.nSampleRate = m_SampleRate; + m_ddParam.eBitStreamId = OMX_AUDIO_DDPBitStreamIdAC3; + m_ddParam.nBitRate = 0; + + for(unsigned int i = 0; i < OMX_MAX_CHANNELS; i++) + { + if(i >= m_ddParam.nChannels) + break; + + m_ddParam.eChannelMapping[i] = OMXChannels[i]; + } + + m_omx_render.SetParameter(OMX_IndexParamAudioDdp, &m_ddParam); + m_omx_render.GetParameter(OMX_IndexParamAudioDdp, &m_ddParam); + PrintDDP(&m_ddParam); + } + else if(m_eEncoding == OMX_AUDIO_CodingDTS) + { + m_dtsParam.nPortIndex = m_omx_render.GetInputPort(); + + m_dtsParam.nChannels = m_format.m_channelLayout.Count(); //(m_InputChannels == 6) ? 8 : m_InputChannels; + m_dtsParam.nBitRate = 0; + + for(unsigned int i = 0; i < OMX_MAX_CHANNELS; i++) + { + if(i >= m_dtsParam.nChannels) + break; + + m_dtsParam.eChannelMapping[i] = OMXChannels[i]; + } + + m_omx_render.SetParameter(OMX_IndexParamAudioDts, &m_dtsParam); + m_omx_render.GetParameter(OMX_IndexParamAudioDts, &m_dtsParam); + PrintDTS(&m_dtsParam); + } + } + + m_omx_render.EnablePort(m_omx_render.GetInputPort(), false); + if(!m_Passthrough) + { + m_omx_mixer.EnablePort(m_omx_mixer.GetOutputPort(), false); + m_omx_mixer.EnablePort(m_omx_mixer.GetInputPort(), false); + } + m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false); + } + + } + + return len; +} + +//*********************************************************************************************** +unsigned int COMXAudio::GetSpace() +{ + int free = m_omx_decoder.GetInputBufferSpace(); + return free; +} + +float COMXAudio::GetDelay() +{ + unsigned int free = m_omx_decoder.GetInputBufferSize() - m_omx_decoder.GetInputBufferSpace(); + return (float)free / (float)m_BytesPerSec; +} + +float COMXAudio::GetCacheTime() +{ + float fBufferLenFull = (float)m_BufferLen - (float)GetSpace(); + if(fBufferLenFull < 0) + fBufferLenFull = 0; + float ret = fBufferLenFull / (float)m_BytesPerSec; + return ret; +} + +float COMXAudio::GetCacheTotal() +{ + return (float)m_BufferLen / (float)m_BytesPerSec; +} + +//*********************************************************************************************** +unsigned int COMXAudio::GetChunkLen() +{ + return m_ChunkLen; +} +//*********************************************************************************************** +int COMXAudio::SetPlaySpeed(int iSpeed) +{ + return 0; +} + +void COMXAudio::RegisterAudioCallback(IAudioCallback *pCallback) +{ + m_vizBufferSamples = 0; + if(!m_Passthrough && !m_HWDecode) + { + m_pCallback = pCallback; + if (m_pCallback) + m_pCallback->OnInitialize(2, m_SampleRate, 32); + } + else + m_pCallback = NULL; +} + +void COMXAudio::UnRegisterAudioCallback() +{ + m_pCallback = NULL; + m_vizBufferSamples = 0; +} + +void COMXAudio::WaitCompletion() +{ + if(!m_Initialized || m_Pause) + return; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + struct timespec starttime, endtime; + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = 0; + omx_buffer->nTimeStamp = ToOMXTime(0LL); + + omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return; + } + + clock_gettime(CLOCK_REALTIME, &starttime); + + while(true) + { + if(m_omx_render.IsEOS()) + break; + clock_gettime(CLOCK_REALTIME, &endtime); + if((endtime.tv_sec - starttime.tv_sec) > 2) + { + CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__); + break; + } + Sleep(50); + } + + return; +} + +void COMXAudio::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) +{ + return ; +} + +bool COMXAudio::SetClock(OMXClock *clock) +{ + if(m_av_clock != NULL) + return false; + + m_av_clock = clock; + m_external_clock = true; + return true; +} + +void COMXAudio::SetCodingType(AEDataFormat dataFormat) +{ + switch(dataFormat) + { + case AE_FMT_DTS: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingDTS\n"); + m_eEncoding = OMX_AUDIO_CodingDTS; + break; + case AE_FMT_AC3: + case AE_FMT_EAC3: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingDDP\n"); + m_eEncoding = OMX_AUDIO_CodingDDP; + break; + default: + CLog::Log(LOGDEBUG, "COMXAudio::SetCodingType OMX_AUDIO_CodingPCM\n"); + m_eEncoding = OMX_AUDIO_CodingPCM; + break; + } +} + +bool COMXAudio::CanHWDecode(CodecID codec) +{ + bool ret = false; + switch(codec) + { + case CODEC_ID_DTS: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDTS\n"); + ret = true; + break; + case CODEC_ID_AC3: + case CODEC_ID_EAC3: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingDDP\n"); + ret = true; + break; + default: + CLog::Log(LOGDEBUG, "COMXAudio::CanHWDecode OMX_AUDIO_CodingPCM\n"); + ret = false; + break; + } + + return ret; +} + +void COMXAudio::PrintChannels(OMX_AUDIO_CHANNELTYPE eChannelMapping[]) +{ + for(int i = 0; i < OMX_AUDIO_MAXCHANNELS; i++) + { + switch(eChannelMapping[i]) + { + case OMX_AUDIO_ChannelLF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLF\n"); + break; + case OMX_AUDIO_ChannelRF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRF\n"); + break; + case OMX_AUDIO_ChannelCF: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelCF\n"); + break; + case OMX_AUDIO_ChannelLS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLS\n"); + break; + case OMX_AUDIO_ChannelRS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRS\n"); + break; + case OMX_AUDIO_ChannelLFE: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLFE\n"); + break; + case OMX_AUDIO_ChannelCS: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelCS\n"); + break; + case OMX_AUDIO_ChannelLR: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelLR\n"); + break; + case OMX_AUDIO_ChannelRR: + CLog::Log(LOGDEBUG, "OMX_AUDIO_ChannelRR\n"); + break; + case OMX_AUDIO_ChannelNone: + case OMX_AUDIO_ChannelKhronosExtensions: + case OMX_AUDIO_ChannelVendorStartUnused: + case OMX_AUDIO_ChannelMax: + default: + break; + } + } +} + +void COMXAudio::PrintPCM(OMX_AUDIO_PARAM_PCMMODETYPE *pcm, std::string direction) +{ + CLog::Log(LOGDEBUG, "pcm->direction : %s\n", direction.c_str()); + CLog::Log(LOGDEBUG, "pcm->nPortIndex : %d\n", (int)pcm->nPortIndex); + CLog::Log(LOGDEBUG, "pcm->eNumData : %d\n", pcm->eNumData); + CLog::Log(LOGDEBUG, "pcm->eEndian : %d\n", pcm->eEndian); + CLog::Log(LOGDEBUG, "pcm->bInterleaved : %d\n", (int)pcm->bInterleaved); + CLog::Log(LOGDEBUG, "pcm->nBitPerSample : %d\n", (int)pcm->nBitPerSample); + CLog::Log(LOGDEBUG, "pcm->ePCMMode : %d\n", pcm->ePCMMode); + CLog::Log(LOGDEBUG, "pcm->nChannels : %d\n", (int)pcm->nChannels); + CLog::Log(LOGDEBUG, "pcm->nSamplingRate : %d\n", (int)pcm->nSamplingRate); + + PrintChannels(pcm->eChannelMapping); +} + +void COMXAudio::PrintDDP(OMX_AUDIO_PARAM_DDPTYPE *ddparm) +{ + CLog::Log(LOGDEBUG, "ddparm->nPortIndex : %d\n", (int)ddparm->nPortIndex); + CLog::Log(LOGDEBUG, "ddparm->nChannels : %d\n", (int)ddparm->nChannels); + CLog::Log(LOGDEBUG, "ddparm->nBitRate : %d\n", (int)ddparm->nBitRate); + CLog::Log(LOGDEBUG, "ddparm->nSampleRate : %d\n", (int)ddparm->nSampleRate); + CLog::Log(LOGDEBUG, "ddparm->eBitStreamId : %d\n", (int)ddparm->eBitStreamId); + CLog::Log(LOGDEBUG, "ddparm->eBitStreamMode : %d\n", (int)ddparm->eBitStreamMode); + CLog::Log(LOGDEBUG, "ddparm->eDolbySurroundMode : %d\n", (int)ddparm->eDolbySurroundMode); + + PrintChannels(ddparm->eChannelMapping); +} + +void COMXAudio::PrintDTS(OMX_AUDIO_PARAM_DTSTYPE *dtsparam) +{ + CLog::Log(LOGDEBUG, "dtsparam->nPortIndex : %d\n", (int)dtsparam->nPortIndex); + CLog::Log(LOGDEBUG, "dtsparam->nChannels : %d\n", (int)dtsparam->nChannels); + CLog::Log(LOGDEBUG, "dtsparam->nBitRate : %d\n", (int)dtsparam->nBitRate); + CLog::Log(LOGDEBUG, "dtsparam->nSampleRate : %d\n", (int)dtsparam->nSampleRate); + CLog::Log(LOGDEBUG, "dtsparam->nFormat : 0x%08x\n", (int)dtsparam->nFormat); + CLog::Log(LOGDEBUG, "dtsparam->nDtsType : %d\n", (int)dtsparam->nDtsType); + CLog::Log(LOGDEBUG, "dtsparam->nDtsFrameSizeBytes : %d\n", (int)dtsparam->nDtsFrameSizeBytes); + + PrintChannels(dtsparam->eChannelMapping); +} + +/* ========================== SYNC FUNCTIONS ========================== */ +unsigned int COMXAudio::SyncDTS(BYTE* pData, unsigned int iSize) +{ + OMX_INIT_STRUCTURE(m_dtsParam); + + unsigned int skip; + unsigned int srCode; + unsigned int dtsBlocks; + bool littleEndian; + + for(skip = 0; iSize - skip > 8; ++skip, ++pData) + { + if (pData[0] == 0x7F && pData[1] == 0xFE && pData[2] == 0x80 && pData[3] == 0x01) + { + /* 16bit le */ + littleEndian = true; + dtsBlocks = ((pData[4] >> 2) & 0x7f) + 1; + m_dtsParam.nFormat = 0x1 | 0x2; + } + else if (pData[0] == 0x1F && pData[1] == 0xFF && pData[2] == 0xE8 && pData[3] == 0x00 && pData[4] == 0x07 && (pData[5] & 0xF0) == 0xF0) + { + /* 14bit le */ + littleEndian = true; + dtsBlocks = (((pData[4] & 0x7) << 4) | (pData[7] & 0x3C) >> 2) + 1; + m_dtsParam.nFormat = 0x1 | 0x0; + } + else if (pData[1] == 0x7F && pData[0] == 0xFE && pData[3] == 0x80 && pData[2] == 0x01) + { + /* 16bit be */ + littleEndian = false; + dtsBlocks = ((pData[5] >> 2) & 0x7f) + 1; + m_dtsParam.nFormat = 0x0 | 0x2; + } + else if (pData[1] == 0x1F && pData[0] == 0xFF && pData[3] == 0xE8 && pData[2] == 0x00 && pData[5] == 0x07 && (pData[4] & 0xF0) == 0xF0) + { + /* 14bit be */ + littleEndian = false; + dtsBlocks = (((pData[5] & 0x7) << 4) | (pData[6] & 0x3C) >> 2) + 1; + m_dtsParam.nFormat = 0x0 | 0x0; + } + else + { + continue; + } + + if (littleEndian) + { + /* if it is not a termination frame, check the next 6 bits are set */ + if ((pData[4] & 0x80) == 0x80 && (pData[4] & 0x7C) != 0x7C) + continue; + + /* get the frame size */ + m_dtsParam.nDtsFrameSizeBytes = ((((pData[5] & 0x3) << 8 | pData[6]) << 4) | ((pData[7] & 0xF0) >> 4)) + 1; + srCode = (pData[8] & 0x3C) >> 2; + } + else + { + /* if it is not a termination frame, check the next 6 bits are set */ + if ((pData[5] & 0x80) == 0x80 && (pData[5] & 0x7C) != 0x7C) + continue; + + /* get the frame size */ + m_dtsParam.nDtsFrameSizeBytes = ((((pData[4] & 0x3) << 8 | pData[7]) << 4) | ((pData[6] & 0xF0) >> 4)) + 1; + srCode = (pData[9] & 0x3C) >> 2; + } + + /* make sure the framesize is sane */ + if (m_dtsParam.nDtsFrameSizeBytes < 96 || m_dtsParam.nDtsFrameSizeBytes > 16384) + continue; + + m_dtsParam.nSampleRate = DTSFSCod[srCode]; + + switch(dtsBlocks << 5) + { + case 512 : + m_dtsParam.nDtsType = 1; + break; + case 1024: + m_dtsParam.nDtsType = 2; + break; + case 2048: + m_dtsParam.nDtsType = 3; + break; + default: + m_dtsParam.nDtsType = 0; + break; + } + + //m_dtsParam.nFormat = 1; + m_dtsParam.nDtsType = 1; + + m_LostSync = false; + + return skip; + } + + m_LostSync = true; + return iSize; +} + +unsigned int COMXAudio::SyncAC3(BYTE* pData, unsigned int iSize) +{ + unsigned int skip = 0; + //unsigned int fSize = 0; + + for(skip = 0; iSize - skip > 6; ++skip, ++pData) + { + /* search for an ac3 sync word */ + if(pData[0] != 0x0b || pData[1] != 0x77) + continue; + + uint8_t fscod = pData[4] >> 6; + uint8_t frmsizecod = pData[4] & 0x3F; + uint8_t bsid = pData[5] >> 3; + + /* sanity checks on the header */ + if ( + fscod == 3 || + frmsizecod > 37 || + bsid > 0x11 + ) continue; + + /* get the details we need to check crc1 and framesize */ + uint16_t bitrate = AC3Bitrates[frmsizecod >> 1]; + unsigned int framesize = 0; + switch(fscod) + { + case 0: framesize = bitrate * 2; break; + case 1: framesize = (320 * bitrate / 147 + (frmsizecod & 1 ? 1 : 0)); break; + case 2: framesize = bitrate * 4; break; + } + + //fSize = framesize * 2; + m_SampleRate = AC3FSCod[fscod]; + + /* dont do extensive testing if we have not lost sync */ + if (!m_LostSync && skip == 0) + return 0; + + unsigned int crc_size; + /* if we have enough data, validate the entire packet, else try to validate crc2 (5/8 of the packet) */ + if (framesize <= iSize - skip) + crc_size = framesize - 1; + else crc_size = (framesize >> 1) + (framesize >> 3) - 1; + + if (crc_size <= iSize - skip) + if(m_dllAvUtil.av_crc(m_dllAvUtil.av_crc_get_table(AV_CRC_16_ANSI), 0, &pData[2], crc_size * 2)) + continue; + + /* if we get here, we can sync */ + m_LostSync = false; + return skip; + } + + /* if we get here, the entire packet is invalid and we have lost sync */ + m_LostSync = true; + return iSize; +} + +void COMXAudio::CheckOutputBufferSize(void **buffer, int *oldSize, int newSize) +{ + if (newSize > *oldSize) + { + if (*buffer) + _aligned_free(*buffer); + *buffer = _aligned_malloc(newSize, 16); + *oldSize = newSize; + } + memset(*buffer, 0x0, *oldSize); +} + diff --git a/xbmc/cores/omxplayer/OMXAudio.h b/xbmc/cores/omxplayer/OMXAudio.h new file mode 100644 index 0000000000000..14d6056503930 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXAudio.h @@ -0,0 +1,152 @@ +/* +* XBMC Media Center +* Copyright (c) 2002 d7o3g4q and RUNTiME +* Portions Copyright (c) by the authors of ffmpeg and xvid +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +////////////////////////////////////////////////////////////////////// + +#ifndef __OPENMAXAUDIORENDER_H__ +#define __OPENMAXAUDIORENDER_H__ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "cores/AudioEngine/AEAudioFormat.h" +#include "cores/AudioEngine/Utils/AEUtil.h" +#include "cores/AudioEngine/Utils/AERemap.h" +#include "cores/IAudioCallback.h" +#include "linux/PlatformDefs.h" +#include "DVDStreamInfo.h" + +#include "OMXClock.h" +#include "OMXCore.h" +#include "DllAvCodec.h" +#include "DllAvUtil.h" + +#define AUDIO_BUFFER_SECONDS 2 +#define VIS_PACKET_SIZE 512 + +#define OMX_IS_RAW(x) \ +( \ + (x) == AE_FMT_AC3 || \ + (x) == AE_FMT_DTS \ +) + +class COMXAudio +{ +public: + void UnRegisterAudioCallback(); + void RegisterAudioCallback(IAudioCallback* pCallback); + unsigned int GetChunkLen(); + float GetDelay(); + float GetCacheTime(); + float GetCacheTotal(); + COMXAudio(); + bool Initialize(AEAudioFormat format, std::string& device, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode); + bool Initialize(AEAudioFormat format, std::string& device); + ~COMXAudio(); + + unsigned int AddPackets(const void* data, unsigned int len); + unsigned int AddPackets(const void* data, unsigned int len, double dts, double pts); + unsigned int GetSpace(); + bool Deinitialize(); + bool Pause(); + bool Stop(); + bool Resume(); + + long GetCurrentVolume() const; + void Mute(bool bMute); + bool SetCurrentVolume(float fVolume); + void SetDynamicRangeCompression(long drc) { m_drc = drc; } + int SetPlaySpeed(int iSpeed); + void WaitCompletion(); + void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers); + + void Flush(); + + void Process(); + + bool SetClock(OMXClock *clock); + void SetCodingType(AEDataFormat dataFormat); + static bool CanHWDecode(CodecID codec); + + void PrintChannels(OMX_AUDIO_CHANNELTYPE eChannelMapping[]); + void PrintPCM(OMX_AUDIO_PARAM_PCMMODETYPE *pcm, std::string direction); + void PrintDDP(OMX_AUDIO_PARAM_DDPTYPE *ddparm); + void PrintDTS(OMX_AUDIO_PARAM_DTSTYPE *dtsparam); + unsigned int SyncDTS(BYTE* pData, unsigned int iSize); + unsigned int SyncAC3(BYTE* pData, unsigned int iSize); + +private: + IAudioCallback* m_pCallback; + bool m_Initialized; + bool m_Pause; + bool m_CanPause; + float m_CurrentVolume; + long m_drc; + bool m_Passthrough; + bool m_HWDecode; + unsigned int m_BytesPerSec; + unsigned int m_BufferLen; + unsigned int m_ChunkLen; + unsigned int m_OutputChannels; + unsigned int m_BitsPerSample; + COMXCoreComponent *m_omx_clock; + OMXClock *m_av_clock; + bool m_external_clock; + bool m_first_frame; + bool m_LostSync; + int m_SampleRate; + OMX_AUDIO_CODINGTYPE m_eEncoding; + uint8_t *m_extradata; + int m_extrasize; + // stuff for visualisation + unsigned int m_vizBufferSamples; + double m_last_pts; + int m_vizBufferSize; + uint8_t *m_vizBuffer; + int m_vizRemapBufferSize; + uint8_t *m_vizRemapBuffer; + CAERemap m_vizRemap; + + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_output; + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input; + OMX_AUDIO_PARAM_DTSTYPE m_dtsParam; + WAVEFORMATEXTENSIBLE m_wave_header; + AEAudioFormat m_format; +protected: + COMXCoreComponent m_omx_render; + COMXCoreComponent m_omx_mixer; + COMXCoreComponent m_omx_decoder; + COMXCoreTunel m_omx_tunnel_clock; + COMXCoreTunel m_omx_tunnel_mixer; + COMXCoreTunel m_omx_tunnel_decoder; + DllAvUtil m_dllAvUtil; + + OMX_AUDIO_CHANNELTYPE m_input_channels[OMX_AUDIO_MAXCHANNELS]; + OMX_AUDIO_CHANNELTYPE m_output_channels[OMX_AUDIO_MAXCHANNELS]; + + CAEChannelInfo m_channelLayout; + + CAEChannelInfo GetChannelLayout(AEAudioFormat format); + + void CheckOutputBufferSize(void **buffer, int *oldSize, int newSize); +}; +#endif + diff --git a/xbmc/cores/omxplayer/OMXAudioCodec.h b/xbmc/cores/omxplayer/OMXAudioCodec.h new file mode 100644 index 0000000000000..76d7049829294 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXAudioCodec.h @@ -0,0 +1,119 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#include +#include "DllAvCodec.h" + +struct AVStream; + +class COMXStreamInfo; + +class COMXAudioCodec +{ +public: + + COMXAudioCodec() {} + virtual ~COMXAudioCodec() {} + + /* + * Open the decoder, returns true on success + */ + virtual bool Open(COMXStreamInfo &hints) = 0; + + /* + * Dispose, Free all resources + */ + virtual void Dispose() = 0; + + /* + * returns bytes used or -1 on error + * + */ + virtual int Decode(BYTE* pData, int iSize) = 0; + + /* + * returns nr of bytes used or -1 on error + * the data is valid until the next Decode call + */ + virtual int GetData(BYTE** dst) = 0; + + /* + * resets the decoder + */ + virtual void Reset() = 0; + + /* + * returns the nr of channels for the decoded audio stream + */ + virtual int GetChannels() = 0; + + /* + * returns the channel mapping + */ + virtual enum PCMChannels* GetChannelMap() = 0; + + /* + * returns the samplerate for the decoded audio stream + */ + virtual int GetSampleRate() = 0; + + /* + * returns the bitspersample for the decoded audio stream (eg 16 bits) + */ + virtual int GetBitsPerSample() = 0; + + /* + * returns the framesize for bitstreams + */ + virtual int GetFrameSize() = 0; + + /* + * returns the syncword for bitstreams + */ + virtual uint32_t GetSyncWord() = 0; + + /* + * should return the average input bit rate + */ + virtual int GetBitRate() { return 0; } + + /* + * returns if the codec requests to use passtrough + */ + virtual bool NeedPassthrough() { return false; } + + /* + * should return codecs name + */ + virtual const char* GetName() = 0; + + /* + * should return amount of data decoded has buffered in preparation for next audio frame + */ + virtual int GetBufferSize() { return 0; } +}; diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp new file mode 100644 index 0000000000000..b9f7ecb7e2b16 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "OMXAudioCodecOMX.h" +#ifdef _LINUX +#include "XMemUtils.h" +#endif +#include "utils/log.h" + +#include "cores/AudioEngine/Utils/AEUtil.h" + +#define MAX_AUDIO_FRAME_SIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE*2) + +template +static inline void _Upmix(AudioDataType *input, + unsigned int channelsInput, AudioDataType *output, + unsigned int channelsOutput, unsigned int frames) +{ + unsigned int unused = channelsOutput - channelsInput; + AudioDataType *_input = input; + AudioDataType *_output = output; + + for (unsigned int i = 0; i < frames; i++) + { + // get input channels + for(unsigned int j = 0; j < channelsInput; j++) + *_output++ = *_input++; + // set unused channels + for(unsigned int j = 0; j < unused; j++) + *_output++ = 0; + } +} + +void COMXAudioCodecOMX::Upmix(void *input, + unsigned int channelsInput, void *output, + unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat) +{ + // input channels must be less than output channels + if (channelsInput >= channelsOutput) + return; + + switch (CAEUtil::DataFormatToBits(dataFormat)) + { + case 8: _Upmix ( (unsigned char *) input, channelsInput, (unsigned char *) output, channelsOutput, frames ); break; + case 16: _Upmix ( (short *) input, channelsInput, (short *) output, channelsOutput, frames ); break; + case 32: _Upmix ( (float *) input, channelsInput, (float *) output, channelsOutput, frames ); break; + default: _Upmix ( (int *) input, channelsInput, (int *) output, channelsOutput, frames ); break; + } +} + +COMXAudioCodecOMX::COMXAudioCodecOMX() +{ + m_iBufferSize2 = 0; + m_pBuffer2 = (BYTE*)_aligned_malloc(MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16); + memset(m_pBuffer2, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + m_iBufferUpmixSize = 0; + m_pBufferUpmix = (BYTE*)_aligned_malloc(MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16); + memset(m_pBufferUpmix, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + m_iBuffered = 0; + m_pCodecContext = NULL; + m_pConvert = NULL; + m_bOpenedCodec = false; + + m_channels = 0; + m_layout = 0; + m_pFrame1 = NULL; + m_iSampleFormat = AV_SAMPLE_FMT_NONE; +} + +COMXAudioCodecOMX::~COMXAudioCodecOMX() +{ + _aligned_free(m_pBuffer2); + _aligned_free(m_pBufferUpmix); + Dispose(); +} + +bool COMXAudioCodecOMX::Open(CDVDStreamInfo &hints) +{ + AVCodec* pCodec; + m_bOpenedCodec = false; + + if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load()) + return false; + + m_dllAvCodec.avcodec_register_all(); + + pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec); + if (!pCodec) + { + CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to find codec %d", hints.codec); + return false; + } + + m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec); + m_pCodecContext->debug_mv = 0; + m_pCodecContext->debug = 0; + m_pCodecContext->workaround_bugs = 1; + + if (pCodec->capabilities & CODEC_CAP_TRUNCATED) + m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; + + m_channels = 0; + m_pCodecContext->channels = hints.channels; + m_pCodecContext->sample_rate = hints.samplerate; + m_pCodecContext->block_align = hints.blockalign; + m_pCodecContext->bit_rate = hints.bitrate; + m_pCodecContext->bits_per_coded_sample = hints.bitspersample; + + if(m_pCodecContext->bits_per_coded_sample == 0) + m_pCodecContext->bits_per_coded_sample = 16; + + if( hints.extradata && hints.extrasize > 0 ) + { + m_pCodecContext->extradata_size = hints.extrasize; + m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE); + memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize); + } + + if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0) + { + CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to open codec"); + Dispose(); + return false; + } + + m_pFrame1 = m_dllAvCodec.avcodec_alloc_frame(); + m_bOpenedCodec = true; + m_iSampleFormat = AV_SAMPLE_FMT_NONE; + return true; +} + +void COMXAudioCodecOMX::Dispose() +{ + if (m_pFrame1) m_dllAvUtil.av_free(m_pFrame1); + m_pFrame1 = NULL; + + if (m_pConvert) + { + m_dllAvCodec.av_audio_convert_free(m_pConvert); + m_pConvert = NULL; + } + + if (m_pCodecContext) + { + if (m_bOpenedCodec) m_dllAvCodec.avcodec_close(m_pCodecContext); + m_bOpenedCodec = false; + m_dllAvUtil.av_free(m_pCodecContext); + m_pCodecContext = NULL; + } + + m_dllAvCodec.Unload(); + m_dllAvUtil.Unload(); + + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + m_iBuffered = 0; +} + +int COMXAudioCodecOMX::Decode(BYTE* pData, int iSize) +{ + int iBytesUsed, got_frame; + if (!m_pCodecContext) return -1; + + m_iBufferSize1 = AVCODEC_MAX_AUDIO_FRAME_SIZE; + m_iBufferSize2 = 0; + + AVPacket avpkt; + m_dllAvCodec.av_init_packet(&avpkt); + avpkt.data = pData; + avpkt.size = iSize; + iBytesUsed = m_dllAvCodec.avcodec_decode_audio4( m_pCodecContext + , m_pFrame1 + , &got_frame + , &avpkt); + if (iBytesUsed < 0 || !got_frame) + { + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + return iBytesUsed; + } + m_iBufferSize1 = m_dllAvUtil.av_samples_get_buffer_size(NULL, m_pCodecContext->channels, m_pFrame1->nb_samples, m_pCodecContext->sample_fmt, 1); + + /* some codecs will attempt to consume more data than what we gave */ + if (iBytesUsed > iSize) + { + CLog::Log(LOGWARNING, "COMXAudioCodecOMX::Decode - decoder attempted to consume more data than given"); + iBytesUsed = iSize; + } + + if(m_iBufferSize1 == 0 && iBytesUsed >= 0) + m_iBuffered += iBytesUsed; + else + m_iBuffered = 0; + + if(m_pCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 && m_iBufferSize1 > 0) + { + if(m_pConvert && m_pCodecContext->sample_fmt != m_iSampleFormat) + { + m_dllAvCodec.av_audio_convert_free(m_pConvert); + m_pConvert = NULL; + } + + if(!m_pConvert) + { + m_iSampleFormat = m_pCodecContext->sample_fmt; + m_pConvert = m_dllAvCodec.av_audio_convert_alloc(AV_SAMPLE_FMT_S16, 1, m_pCodecContext->sample_fmt, 1, NULL, 0); + } + + if(!m_pConvert) + { + CLog::Log(LOGERROR, "COMXAudioCodecOMX::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", m_pCodecContext->sample_fmt); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + return iBytesUsed; + } + + const void *ibuf[6] = { m_pFrame1->data[0] }; + void *obuf[6] = { m_pBuffer2 }; + int istr[6] = { m_dllAvUtil.av_get_bytes_per_sample(m_pCodecContext->sample_fmt) }; + int ostr[6] = { (int) (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3) }; + int len = m_iBufferSize1 / istr[0]; + if(m_dllAvCodec.av_audio_convert(m_pConvert, obuf, ostr, ibuf, istr, len) < 0) + { + CLog::Log(LOGERROR, "COMXAudioCodecOMX::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", (int)m_pCodecContext->sample_fmt); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + return iBytesUsed; + } + + m_iBufferSize1 = 0; + m_iBufferSize2 = len * ostr[0]; + } + + return iBytesUsed; +} + +int COMXAudioCodecOMX::GetData(BYTE** dst) +{ + unsigned int size = 0; + BYTE *src = NULL; + + if(m_iBufferSize1) + { + *dst = m_pFrame1->data[0]; + src = m_pFrame1->data[0]; + size = m_iBufferSize1; + } + if(m_iBufferSize2) + { + *dst = m_pBuffer2; + src = m_pBuffer2; + size = m_iBufferSize2; + } + + if(m_pCodecContext->channels > 4 && size) + { + unsigned int m_frameSize = (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3) * m_pCodecContext->channels; + unsigned int frames = size / m_frameSize; + + memset(m_pBufferUpmix, 0, MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + + Upmix(src, m_pCodecContext->channels, m_pBufferUpmix, 8, frames, AE_FMT_S16LE); + + m_iBufferUpmixSize = frames * 8 * (CAEUtil::DataFormatToBits(AE_FMT_S16LE) >> 3); + + *dst = m_pBufferUpmix; + size = m_iBufferUpmixSize; + } + + return size; +} + +void COMXAudioCodecOMX::Reset() +{ + if (m_pCodecContext) m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext); + m_iBufferSize1 = 0; + m_iBufferSize2 = 0; + m_iBuffered = 0; +} + +int COMXAudioCodecOMX::GetChannels() +{ + return (m_pCodecContext->channels > 4) ? 8 : m_pCodecContext->channels; +} + +int COMXAudioCodecOMX::GetSampleRate() +{ + if (m_pCodecContext) return m_pCodecContext->sample_rate; + return 0; +} + +int COMXAudioCodecOMX::GetBitsPerSample() +{ + return 16; +} + +int COMXAudioCodecOMX::GetBitRate() +{ + if (m_pCodecContext) return m_pCodecContext->bit_rate; + return 0; +} + +static unsigned count_bits(int64_t value) +{ + unsigned bits = 0; + for(;value;++bits) + value &= value - 1; + return bits; +} + +void COMXAudioCodecOMX::BuildChannelMap() +{ + if (m_channels == m_pCodecContext->channels && m_layout == m_pCodecContext->channel_layout) + return; //nothing to do here + + m_channels = m_pCodecContext->channels; + m_layout = m_pCodecContext->channel_layout; + + int64_t layout; + + int bits = count_bits(m_pCodecContext->channel_layout); + if (bits == m_pCodecContext->channels) + layout = m_pCodecContext->channel_layout; + else + { + CLog::Log(LOGINFO, "COMXAudioCodecOMX::GetChannelMap - FFmpeg reported %d channels, but the layout contains %d ignoring", m_pCodecContext->channels, bits); + layout = m_dllAvUtil.av_get_default_channel_layout(m_pCodecContext->channels); + } + + m_channelLayout.Reset(); + + if (layout & AV_CH_FRONT_LEFT ) m_channelLayout += AE_CH_FL ; + if (layout & AV_CH_FRONT_RIGHT ) m_channelLayout += AE_CH_FR ; + if (layout & AV_CH_FRONT_CENTER ) m_channelLayout += AE_CH_FC ; + if (layout & AV_CH_LOW_FREQUENCY ) m_channelLayout += AE_CH_LFE ; + if (layout & AV_CH_BACK_LEFT ) m_channelLayout += AE_CH_BL ; + if (layout & AV_CH_BACK_RIGHT ) m_channelLayout += AE_CH_BR ; + if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelLayout += AE_CH_FLOC; + if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelLayout += AE_CH_FROC; + if (layout & AV_CH_BACK_CENTER ) m_channelLayout += AE_CH_BC ; + if (layout & AV_CH_SIDE_LEFT ) m_channelLayout += AE_CH_SL ; + if (layout & AV_CH_SIDE_RIGHT ) m_channelLayout += AE_CH_SR ; + if (layout & AV_CH_TOP_CENTER ) m_channelLayout += AE_CH_TC ; + if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelLayout += AE_CH_TFL ; + if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelLayout += AE_CH_TFC ; + if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelLayout += AE_CH_TFR ; + if (layout & AV_CH_TOP_BACK_LEFT ) m_channelLayout += AE_CH_BL ; + if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ; + if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ; + + //terminate the channel map + if(m_pCodecContext->channels > 4) + { + for(int i = m_pCodecContext->channels; i < 8; i++) + m_channelLayout += AE_CH_RAW; + } +} + +CAEChannelInfo COMXAudioCodecOMX::GetChannelMap() +{ + BuildChannelMap(); + return m_channelLayout; +} diff --git a/xbmc/cores/omxplayer/OMXAudioCodecOMX.h b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h new file mode 100644 index 0000000000000..e740e5b47cece --- /dev/null +++ b/xbmc/cores/omxplayer/OMXAudioCodecOMX.h @@ -0,0 +1,77 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "cores/AudioEngine/AEAudioFormat.h" +#include "DllAvCodec.h" +#include "DllAvFormat.h" +#include "DllAvUtil.h" + +#include "DVDStreamInfo.h" +#include "linux/PlatformDefs.h" + +class COMXAudioCodecOMX +{ +public: + void Upmix(void *input, unsigned int channelsInput, void *output, + unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat); + COMXAudioCodecOMX(); + virtual ~COMXAudioCodecOMX(); + bool Open(CDVDStreamInfo &hints); + void Dispose(); + int Decode(BYTE* pData, int iSize); + int GetData(BYTE** dst); + void Reset(); + int GetChannels(); + virtual CAEChannelInfo GetChannelMap(); + int GetSampleRate(); + int GetBitsPerSample(); + const char* GetName() { return "FFmpeg"; } + int GetBufferSize() { return m_iBuffered; } + int GetBitRate(); + +protected: + AVCodecContext* m_pCodecContext; + AVAudioConvert* m_pConvert;; + enum AVSampleFormat m_iSampleFormat; + CAEChannelInfo m_channelLayout; + + AVFrame* m_pFrame1; + int m_iBufferSize1; + + BYTE *m_pBuffer2; + int m_iBufferSize2; + + BYTE *m_pBufferUpmix; + int m_iBufferUpmixSize; + + bool m_bOpenedCodec; + int m_iBuffered; + + int m_channels; + uint64_t m_layout; + + DllAvCodec m_dllAvCodec; + DllAvUtil m_dllAvUtil; + + void BuildChannelMap(); +}; diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp new file mode 100644 index 0000000000000..bb4b91a473a34 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXImage.cpp @@ -0,0 +1,1096 @@ +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include "OMXImage.h" + +#include "utils/log.h" +#include "linux/XMemUtils.h" + +#include "BitstreamConverter.h" + +#include +#include +#ifndef STANDALONE +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "settings/AdvancedSettings.h" +#endif + +#ifdef CLASSNAME +#undef CLASSNAME +#endif +#define CLASSNAME "COMXImage" + +#define CONTENTURI_MAXLEN 256 + +#define EXIF_TAG_ORIENTATION 0x0112 + +static CCriticalSection g_OMXSection; + +COMXImage::COMXImage() +{ + m_is_open = false; + m_image_size = 0; + m_image_buffer = NULL; + m_progressive = false; + m_alpha = false; + m_orientation = 0; + m_width = 0; + m_height = 0; + + m_is_open = false; + m_decoded_buffer = NULL; + m_encoded_buffer = NULL; + m_decoder_open = false; + m_encoder_open = false; + + OMX_INIT_STRUCTURE(m_decoded_format); + OMX_INIT_STRUCTURE(m_encoded_format); + memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE)); +} + +COMXImage::~COMXImage() +{ + Close(); +} + +void COMXImage::Close() +{ + OMX_INIT_STRUCTURE(m_decoded_format); + OMX_INIT_STRUCTURE(m_encoded_format); + memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE)); + + if(m_image_buffer) + free(m_image_buffer); + + m_image_buffer = NULL; + m_image_size = 0; + m_width = 0; + m_height = 0; + m_is_open = false; + m_progressive = false; + m_orientation = 0; + m_decoded_buffer = NULL; + m_encoded_buffer = NULL; + + if(m_decoder_open) + { + m_omx_decoder.FlushInput(); + m_omx_decoder.FreeInputBuffers(true); + m_omx_resize.FlushOutput(); + m_omx_resize.FreeOutputBuffers(true); + + m_omx_tunnel_decode.Flush(); + m_omx_tunnel_decode.Flush(); + m_omx_tunnel_decode.Deestablish(); + m_omx_decoder.Deinitialize(); + m_omx_resize.Deinitialize(); + m_decoder_open = false; + } + + if(m_encoder_open) + { + m_omx_encoder.Deinitialize(); + m_encoder_open = false; + } + + m_pFile.Close(); +} + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_TEM = 0x01, +} JPEG_MARKER; + +OMX_IMAGE_CODINGTYPE COMXImage::GetCodingType() +{ + memset(&m_omx_image, 0x0, sizeof(OMX_IMAGE_PORTDEFINITIONTYPE)); + m_width = 0; + m_height = 0; + m_progressive = false; + m_orientation = 0; + + m_omx_image.eCompressionFormat = OMX_IMAGE_CodingMax; + + if(!m_image_size) + return OMX_IMAGE_CodingMax; + + bits_reader_t br; + CBitstreamConverter::bits_reader_set( &br, m_image_buffer, m_image_size ); + + /* JPEG Header */ + if(CBitstreamConverter::read_bits(&br, 16) == 0xFFD8) + { + m_omx_image.eCompressionFormat = OMX_IMAGE_CodingJPEG; + + CBitstreamConverter::read_bits(&br, 8); + unsigned char marker = CBitstreamConverter::read_bits(&br, 8); + unsigned short block_size = 0; + bool nMarker = false; + + while(!br.oflow) { + + switch(marker) + { + case M_TEM: + case M_DRI: + CBitstreamConverter::skip_bits(&br, 16); + continue; + case M_SOI: + case M_EOI: + continue; + + case M_SOS: + case M_DQT: + case M_DNL: + case M_DHP: + case M_EXP: + + case M_DHT: + + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + + case M_SOF5: + case M_SOF6: + case M_SOF7: + + case M_JPG: + case M_SOF9: + case M_SOF10: + case M_SOF11: + + case M_SOF13: + case M_SOF14: + case M_SOF15: + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + block_size = CBitstreamConverter::read_bits(&br, 16); + nMarker = true; + break; + + default: + nMarker = false; + break; + } + + if(!nMarker) + { + break; + } + + if(marker >= M_SOF0 && marker <= M_SOF15) + { + if(marker == M_SOF2 || marker == M_SOF6 || marker == M_SOF10 || marker == M_SOF14) + { + m_progressive = true; + } + CBitstreamConverter::skip_bits(&br, 8); + m_omx_image.nFrameHeight = CBitstreamConverter::read_bits(&br, 16); + m_omx_image.nFrameWidth = CBitstreamConverter::read_bits(&br, 16); + + CBitstreamConverter::skip_bits(&br, 8 * (block_size - 9)); + } + else if(marker == M_APP1) + { + int readBits = 2; + bool bMotorolla = false; + bool bError = false; + + // Exif header + if(CBitstreamConverter::read_bits(&br, 32) == 0x45786966) + { + CBitstreamConverter::skip_bits(&br, 8 * 2); + readBits += 2; + + char o1 = CBitstreamConverter::read_bits(&br, 8); + char o2 = CBitstreamConverter::read_bits(&br, 8); + readBits += 2; + + /* Discover byte order */ + if(o1 == 'M' && o2 == 'M') + bMotorolla = true; + else if(o1 == 'I' && o2 == 'I') + bMotorolla = false; + else + bError = true; + + CBitstreamConverter::skip_bits(&br, 8 * 2); + readBits += 2; + + if(!bError) + { + unsigned int offset, a, b, numberOfTags, tagNumber; + + // Get first IFD offset (offset to IFD0) + if(bMotorolla) + { + CBitstreamConverter::skip_bits(&br, 8 * 2); + readBits += 2; + + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + readBits += 2; + offset = (a << 8) + b; + } + else + { + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + readBits += 2; + offset = (b << 8) + a; + + CBitstreamConverter::skip_bits(&br, 8 * 2); + readBits += 2; + } + + offset -= 8; + if(offset > 0) + { + CBitstreamConverter::skip_bits(&br, 8 * offset); + readBits += offset; + } + + // Get the number of directory entries contained in this IFD + if(bMotorolla) + { + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + numberOfTags = (a << 8) + b; + } + else + { + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + numberOfTags = (b << 8) + a; + } + readBits += 2; + + while(numberOfTags && !br.oflow) + { + // Get Tag number + if(bMotorolla) + { + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + tagNumber = (a << 8) + b; + readBits += 2; + } + else + { + a = CBitstreamConverter::read_bits(&br, 8); + b = CBitstreamConverter::read_bits(&br, 8); + tagNumber = (b << 8) + a; + readBits += 2; + } + + //found orientation tag + if(tagNumber == EXIF_TAG_ORIENTATION) + { + if(bMotorolla) + { + CBitstreamConverter::skip_bits(&br, 8 * 7); + readBits += 7; + m_orientation = CBitstreamConverter::read_bits(&br, 8); + readBits += 1; + CBitstreamConverter::skip_bits(&br, 8 * 2); + readBits += 2; + } + else + { + CBitstreamConverter::skip_bits(&br, 8 * 6); + readBits += 6; + m_orientation = CBitstreamConverter::read_bits(&br, 8); + readBits += 1; + CBitstreamConverter::skip_bits(&br, 8 * 3); + readBits += 3; + } + break; + } + else + { + CBitstreamConverter::skip_bits(&br, 8 * 10); + readBits += 10; + } + numberOfTags--; + } + } + } + readBits += 4; + CBitstreamConverter::skip_bits(&br, 8 * (block_size - readBits)); + } + else + { + CBitstreamConverter::skip_bits(&br, 8 * (block_size - 2)); + } + + CBitstreamConverter::read_bits(&br, 8); + marker = CBitstreamConverter::read_bits(&br, 8); + + } + + } + + CBitstreamConverter::bits_reader_set( &br, m_image_buffer, m_image_size ); + + /* PNG Header */ + if(CBitstreamConverter::read_bits(&br, 32) == 0x89504E47) + { + m_omx_image.eCompressionFormat = OMX_IMAGE_CodingPNG; + CBitstreamConverter::skip_bits(&br, 32 * 2); + if(CBitstreamConverter::read_bits(&br, 32) == 0x49484452) + { + m_omx_image.nFrameWidth = CBitstreamConverter::read_bits(&br, 32); + m_omx_image.nFrameHeight = CBitstreamConverter::read_bits(&br, 32); + (void)CBitstreamConverter::read_bits(&br, 8); // bit depth + unsigned int coding_type = CBitstreamConverter::read_bits(&br, 8); + m_alpha = coding_type==4 || coding_type==6; + } + } + + if(m_orientation > 8) + m_orientation = 0; + + m_width = m_omx_image.nFrameWidth; + m_height = m_omx_image.nFrameHeight; + + return m_omx_image.eCompressionFormat; +} + +void COMXImage::SetHardwareSizeLimits() +{ + // ensure not too big for hardware + while (m_width > 2048 || m_height > 2048) + m_width >>= 1, m_height >>= 1; + // ensure not too small + while (m_width <= 32 || m_height <= 32) + m_width <<= 1, m_height <<= 1; + // surely not going to happen? + if (m_width > 2048 || m_height > 2048) + m_width = 256, m_height = 256; + + m_width = (m_width + 15) & ~15; + m_height = (m_height + 15) & ~15; +} + +bool COMXImage::ReadFile(const CStdString& inputFile) +{ + if(!m_pFile.Open(inputFile, 0)) + { + CLog::Log(LOGERROR, "%s::%s %s not found\n", CLASSNAME, __func__, inputFile.c_str()); + return false; + } + + if(m_image_buffer) + free(m_image_buffer); + m_image_buffer = NULL; + + m_image_size = m_pFile.GetLength(); + + if(!m_image_size) + return false; + + m_image_buffer = (uint8_t *)malloc(m_image_size); + if(!m_image_buffer) + return false; + + m_pFile.Read(m_image_buffer, m_image_size); + + GetCodingType(); + + if(m_width < 1 || m_height < 1) + return false; + + SetHardwareSizeLimits(); + + m_is_open = true; + + return true; +} + +bool COMXImage::Decode(unsigned width, unsigned height) +{ + std::string componentName = ""; + bool m_firstFrame = true; + unsigned int demuxer_bytes = 0; + const uint8_t *demuxer_content = NULL; + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *omx_buffer = NULL; + + OMX_INIT_STRUCTURE(m_decoded_format); + + CSingleLock lock(g_OMXSection); + + if(!m_image_buffer) + { + CLog::Log(LOGERROR, "%s::%s no input buffer\n", CLASSNAME, __func__); + return false; + } + + if(GetCompressionFormat() == OMX_IMAGE_CodingMax) + { + CLog::Log(LOGERROR, "%s::%s error unsupported image format\n", CLASSNAME, __func__); + return false; + } + + if(IsProgressive()) + { + CLog::Log(LOGWARNING, "%s::%s progressive images not supported by decoder\n", CLASSNAME, __func__); + return false; + } + + if(!m_is_open) + { + CLog::Log(LOGERROR, "%s::%s error not opened\n", CLASSNAME, __func__); + return false; + } + + componentName = "OMX.broadcom.image_decode"; + if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamImageInit)) + { + CLog::Log(LOGERROR, "%s::%s error m_omx_decoder.Initialize\n", CLASSNAME, __func__); + return false; + } + + componentName = "OMX.broadcom.resize"; + if(!m_omx_resize.Initialize((const std::string)componentName, OMX_IndexParamImageInit)) + { + CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize\n", CLASSNAME, __func__); + return false; + } + + m_decoder_open = true; + + m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_resize, m_omx_resize.GetInputPort()); + + omx_err = m_omx_tunnel_decode.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__); + return false; + } + + if(width == 0 || height == 0) + { +#ifndef STANDALONE + height = g_advancedSettings.m_imageRes; + if (g_advancedSettings.m_fanartRes > g_advancedSettings.m_imageRes) + { // a separate fanart resolution is specified - check if the image is exactly equal to this res + if (m_width == (unsigned int)g_advancedSettings.m_fanartRes * 16/9 && + m_height == (unsigned int)g_advancedSettings.m_fanartRes) + { // special case for fanart res + height = g_advancedSettings.m_fanartRes; + } + } + width = height * 16/9; + if(!width || !height) + { + //width = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iScreenWidth; + //height = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iScreenHeight; + width = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iWidth; + height = g_settings.m_ResInfo[g_guiSettings.m_LookAndFeelResolution].iHeight; + } +#else + width = 2048; + height = 2048; +#endif + } + + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_INIT_STRUCTURE(port_def); + port_def.nPortIndex = m_omx_decoder.GetInputPort(); + + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_decoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + port_def.format.image.eCompressionFormat = GetCompressionFormat(); + port_def.format.image.eColorFormat = OMX_COLOR_FormatUnused; + port_def.format.image.nFrameWidth = 0; + port_def.format.image.nFrameHeight = 0; + port_def.format.image.nStride = 0; + port_def.format.image.nSliceHeight = 0; + port_def.format.image.bFlagErrorConcealment = OMX_FALSE; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_decoder.AllocInputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_decoder.AllocInputBuffers result(0x%x)", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + OMX_INIT_STRUCTURE(port_def); + port_def.nPortIndex = m_omx_resize.GetOutputPort(); + + omx_err = m_omx_resize.GetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; + port_def.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888; + if((((width + 15)&~15) > width) || (((height + 15)&~15) > height)) + { + port_def.format.image.nFrameWidth = (width + 15)&~15; + port_def.format.image.nFrameHeight = (height + 15)&~15; + } + else + { + port_def.format.image.nFrameWidth = width; + port_def.format.image.nFrameHeight = height; + } + port_def.format.image.nStride = 0; + port_def.format.image.nSliceHeight = 0; + port_def.format.image.bFlagErrorConcealment = OMX_FALSE; + + omx_err = m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_resize.AllocOutputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize.AllocOutputBuffers result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_resize.SetStateForComponent(OMX_StateExecuting); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + demuxer_bytes = GetImageSize(); + demuxer_content = GetImageBuffer(); + if(!demuxer_bytes || !demuxer_content) + return false; + + m_firstFrame = true; + + while(demuxer_bytes > 0) + { + omx_buffer = m_omx_decoder.GetInputBuffer(1000); + if(omx_buffer == NULL) + return false; + + omx_buffer->nOffset = omx_buffer->nFlags = 0; + + omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes; + memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen); + + demuxer_content += omx_buffer->nFilledLen; + demuxer_bytes -= omx_buffer->nFilledLen; + + if(demuxer_bytes == 0) + omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + break; + } + if(m_firstFrame) + { + m_firstFrame = false; + + m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false); + m_omx_resize.DisablePort(m_omx_resize.GetInputPort(), false); + + OMX_PARAM_PORTDEFINITIONTYPE port_image; + OMX_INIT_STRUCTURE(port_image); + + port_image.nPortIndex = m_omx_decoder.GetOutputPort(); + m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_image); + + port_image.nPortIndex = m_omx_resize.GetInputPort(); + m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_image); + + m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false); + omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged); + if(omx_err == OMX_ErrorStreamCorrupt) + { + CLog::Log(LOGERROR, "%s::%s image not unsupported\n", CLASSNAME, __func__); + return false; + } + + m_omx_resize.EnablePort(m_omx_resize.GetInputPort(), false); + omx_err = m_omx_resize.WaitForEvent(OMX_EventPortSettingsChanged); + if(omx_err == OMX_ErrorStreamCorrupt) + { + CLog::Log(LOGERROR, "%s::%s image not unsupported\n", CLASSNAME, __func__); + return false; + } + } + } + + omx_err = m_omx_decoder.WaitForEvent(OMX_EventBufferFlag, 1000); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_decoder.WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + m_decoded_buffer = m_omx_resize.GetOutputBuffer(); + + if(!m_decoded_buffer) + { + CLog::Log(LOGERROR, "%s::%s no output buffer\n", CLASSNAME, __func__); + return false; + } + + omx_err = m_omx_resize.FillThisBuffer(m_decoded_buffer); + + omx_err = m_omx_resize.WaitForEvent(OMX_EventBufferFlag, 1000); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + m_decoded_format.nPortIndex = m_omx_resize.GetOutputPort(); + omx_err = m_omx_resize.GetParameter(OMX_IndexParamPortDefinition, &m_decoded_format); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_resize.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + m_omx_tunnel_decode.Deestablish(); + + SwapBlueRed(m_decoded_buffer->pBuffer, GetDecodedHeight(), GetDecodedWidth() * 4); + + return true; +} + +bool COMXImage::Encode(unsigned char *buffer, int size, unsigned width, unsigned height) +{ + std::string componentName = ""; + unsigned int demuxer_bytes = 0; + const uint8_t *demuxer_content = NULL; + uint8_t *internalBuffer = NULL; + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *omx_buffer = NULL; + OMX_INIT_STRUCTURE(m_encoded_format); + + CSingleLock lock(g_OMXSection); + + if (!buffer || !size) + { + CLog::Log(LOGERROR, "%s::%s error no buffer\n", CLASSNAME, __func__); + return false; + } + + componentName = "OMX.broadcom.image_encode"; + if(!m_omx_encoder.Initialize((const std::string)componentName, OMX_IndexParamImageInit)) + { + CLog::Log(LOGERROR, "%s::%s error m_omx_encoder.Initialize\n", CLASSNAME, __func__); + return false; + } + + m_encoder_open = true; + + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_INIT_STRUCTURE(port_def); + port_def.nPortIndex = m_omx_encoder.GetInputPort(); + + omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; + port_def.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888; + port_def.format.image.nFrameWidth = width; + port_def.format.image.nFrameHeight = height; + port_def.format.image.nStride = width * 4; + port_def.format.image.nSliceHeight = height; + port_def.format.image.bFlagErrorConcealment = OMX_FALSE; + + omx_err = m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + OMX_INIT_STRUCTURE(port_def); + port_def.nPortIndex = m_omx_encoder.GetOutputPort(); + + omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingJPEG; + port_def.format.image.eColorFormat = OMX_COLOR_FormatUnused; + port_def.format.image.nFrameWidth = width; + port_def.format.image.nFrameHeight = height; + port_def.format.image.nStride = 0; + port_def.format.image.nSliceHeight = 0; + port_def.format.image.bFlagErrorConcealment = OMX_FALSE; + + omx_err = m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + OMX_IMAGE_PARAM_QFACTORTYPE qfactor; + OMX_INIT_STRUCTURE(qfactor); + qfactor.nPortIndex = m_omx_encoder.GetOutputPort(); + qfactor.nQFactor = 16; + + omx_err = m_omx_encoder.SetParameter(OMX_IndexParamQFactor, &qfactor); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter OMX_IndexParamQFactor result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_encoder.AllocInputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.AllocInputBuffers result(0x%x)", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_encoder.AllocOutputBuffers(); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.AllocOutputBuffers result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + omx_err = m_omx_encoder.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + internalBuffer = (uint8_t *)malloc(size); + memcpy(internalBuffer, buffer, size); + demuxer_bytes = size; + demuxer_content = internalBuffer; + SwapBlueRed(internalBuffer, height, width * 4); + + if(!demuxer_bytes || !demuxer_content) + return false; + + while(demuxer_bytes > 0) + { + omx_buffer = m_omx_encoder.GetInputBuffer(1000); + if(omx_buffer == NULL) + { + if(internalBuffer) free(internalBuffer); + return false; + } + + omx_buffer->nOffset = omx_buffer->nFlags = 0; + + omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes; + memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen); + + demuxer_content += omx_buffer->nFilledLen; + demuxer_bytes -= omx_buffer->nFilledLen; + + if(demuxer_bytes == 0) + omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; + + omx_err = m_omx_encoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + break; + } + } + + if(internalBuffer) free(internalBuffer); + + m_encoded_buffer = m_omx_encoder.GetOutputBuffer(); + + if(!m_encoded_buffer) + { + CLog::Log(LOGERROR, "%s::%s no output buffer\n", CLASSNAME, __func__); + return false; + } + + omx_err = m_omx_encoder.FillThisBuffer(m_encoded_buffer); + if(omx_err != OMX_ErrorNone) + return false; + + omx_err = m_omx_encoder.WaitForEvent(OMX_EventBufferFlag, 1000); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder WaitForEvent result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + m_encoded_format.nPortIndex = m_omx_encoder.GetOutputPort(); + omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &m_encoded_format); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + + return true; +} + +unsigned char *COMXImage::GetDecodedData() +{ + if(!m_decoded_buffer) + return NULL; + + return (unsigned char *)m_decoded_buffer->pBuffer; +} + +unsigned int COMXImage::GetDecodedSize() +{ + if(!m_decoded_buffer) + return 0; + return (unsigned int)m_decoded_buffer->nFilledLen; +} + +unsigned char *COMXImage::GetEncodedData() +{ + if(!m_encoded_buffer) + return NULL; + + return (unsigned char *)m_encoded_buffer->pBuffer; +} + +unsigned int COMXImage::GetEncodedSize() +{ + if(!m_encoded_buffer) + return 0; + return (unsigned int)m_encoded_buffer->nFilledLen; +} + +bool COMXImage::SwapBlueRed(unsigned char *pixels, unsigned int height, unsigned int pitch, + unsigned int elements, unsigned int offset) +{ + if (!pixels) return false; + unsigned char *dst = pixels; + for (unsigned int y = 0; y < height; y++) + { + dst = pixels + (y * pitch); + for (unsigned int x = 0; x < pitch; x+=elements) + std::swap(dst[x+offset], dst[x+2+offset]); + } + return true; +} + +bool COMXImage::CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile, + int minx, int miny, bool rotateExif) +{ + if (!ReadFile(sourceFile)) + return false; + + return CreateThumbnailFromMemory(m_image_buffer, m_image_size, destFile, minx, miny); +} + +bool COMXImage::CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize, const CStdString& destFile, + unsigned int minx, unsigned int miny) +{ + if(!bufSize || !buffer) + return false; + + if(!m_is_open) + { + m_image_size = bufSize; + m_image_buffer = (uint8_t *)malloc(m_image_size); + if(!m_image_buffer) + return false; + + memcpy(m_image_buffer, buffer, m_image_size); + + GetCodingType(); + + SetHardwareSizeLimits(); + + m_is_open = true; + } + + if(!Decode(minx, miny)) + return false; + + return CreateThumbnailFromSurface(GetDecodedData(), GetDecodedWidth(), GetDecodedHeight(), + XB_FMT_A8R8G8B8, GetDecodedWidth() * 4, destFile); +} + +bool COMXImage::CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height, + unsigned int format, unsigned int pitch, const CStdString& destFile) +{ + if(format != XB_FMT_A8R8G8B8 || !buffer) + return false; + + // the omx encoder needs alligned sizes + if(width%16 || height%16) + { + unsigned int new_width = (width + 15)&~15; + unsigned int new_height = (height + 15)&~15; + unsigned int new_pitch = new_width * 4; + + unsigned int size = new_height * new_pitch; + unsigned char *dstBuffer = (unsigned char *)malloc(size); + unsigned char *dst = dstBuffer; + unsigned char *src = buffer; + + if(!dstBuffer) + return false; + + memset(dst, 0x0, size); + + for(unsigned int y = 0; y < height; y++) + { + memcpy(dst, src, pitch); + src += pitch; + dst += new_pitch; + } + if(!Encode(dstBuffer, size, new_width, new_height)) + { + free(dstBuffer); + return false; + } + free(dstBuffer); + } + else + { + if(!Encode(buffer, height * pitch, width, height)) + return false; + } + + XFILE::CFile file; + if (file.OpenForWrite(destFile, true)) + { + CLog::Log(LOGDEBUG, "%s::%s : %s width %d height %d\n", CLASSNAME, __func__, destFile.c_str(), width, height); + + file.Write(GetEncodedData(), GetEncodedSize()); + file.Close(); + return true; + } + + return false; +} diff --git a/xbmc/cores/omxplayer/OMXImage.h b/xbmc/cores/omxplayer/OMXImage.h new file mode 100644 index 0000000000000..4a536c67ea912 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXImage.h @@ -0,0 +1,108 @@ +#pragma once +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_OMXLIB) + +#include "OMXCore.h" + +#include + +#include "OMXClock.h" +#if defined(STANDALONE) +#define XB_FMT_A8R8G8B8 1 +#include "File.h" +#else +#include "filesystem/File.h" +#include "guilib/XBTF.h" +#endif + +using namespace XFILE; +using namespace std; + +class COMXImage +{ +public: + COMXImage(); + virtual ~COMXImage(); + + // Required overrides + void Close(void); + void SetHardwareSizeLimits(); + bool ReadFile(const CStdString& inputFile); + bool IsProgressive() { return m_progressive; }; + bool IsAlpha() { return m_alpha; }; + int GetOrientation() { return m_orientation; }; + unsigned int GetOriginalWidth() { return m_omx_image.nFrameWidth; }; + unsigned int GetOriginalHeight() { return m_omx_image.nFrameHeight; }; + unsigned int GetWidth() { return m_width; }; + unsigned int GetHeight() { return m_height; }; + OMX_IMAGE_CODINGTYPE GetCodingType(); + const uint8_t *GetImageBuffer() { return (const uint8_t *)m_image_buffer; }; + unsigned long GetImageSize() { return m_image_size; }; + OMX_IMAGE_CODINGTYPE GetCompressionFormat() { return m_omx_image.eCompressionFormat; }; + bool Decode(unsigned width, unsigned height); + bool Encode(unsigned char *buffer, int size, unsigned width, unsigned height); + int GetDecodedWidth() { return (int)m_decoded_format.format.image.nFrameWidth; }; + int GetDecodedHeight() { return (int)m_decoded_format.format.image.nFrameHeight; }; + int GetDecodedStride() { return (int)m_decoded_format.format.image.nStride; }; + unsigned char *GetDecodedData(); + unsigned int GetDecodedSize(); + int GetEncodedWidth() { return (int)m_encoded_format.format.image.nFrameWidth; }; + int GetEncodedHeight() { return (int)m_encoded_format.format.image.nFrameHeight; }; + int GetEncodedStride() { return (int)m_encoded_format.format.image.nStride; }; + unsigned char *GetEncodedData(); + unsigned int GetEncodedSize(); + bool SwapBlueRed(unsigned char *pixels, unsigned int height, unsigned int pitch, + unsigned int elements = 4, unsigned int offset=0); + bool CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile, + int minx, int miny, bool rotateExif); + bool CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize, + const CStdString& destFile, unsigned int minx, unsigned int miny); + bool CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height, + unsigned int format, unsigned int pitch, const CStdString& destFile); +protected: + uint8_t *m_image_buffer; + bool m_is_open; + unsigned long m_image_size; + unsigned int m_width; + unsigned int m_height; + bool m_progressive; + bool m_alpha; + int m_orientation; + XFILE::CFile m_pFile; + OMX_IMAGE_PORTDEFINITIONTYPE m_omx_image; + + // Components + COMXCoreComponent m_omx_decoder; + COMXCoreComponent m_omx_encoder; + COMXCoreComponent m_omx_resize; + COMXCoreTunel m_omx_tunnel_decode; + OMX_BUFFERHEADERTYPE *m_decoded_buffer; + OMX_BUFFERHEADERTYPE *m_encoded_buffer; + OMX_PARAM_PORTDEFINITIONTYPE m_decoded_format; + OMX_PARAM_PORTDEFINITIONTYPE m_encoded_format; + + bool m_decoder_open; + bool m_encoder_open; +}; + +#endif diff --git a/xbmc/cores/omxplayer/OMXPlayer.cpp b/xbmc/cores/omxplayer/OMXPlayer.cpp new file mode 100644 index 0000000000000..d1ab09dcbeff3 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayer.cpp @@ -0,0 +1,3822 @@ +/* + * Copyright (C) 2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#if defined (HAS_OMXPLAYER) + +#include +#include + +#include "OMXPlayerAudio.h" +#include "OMXPlayerVideo.h" +#include "OMXPlayer.h" +#include "Application.h" +#include "ApplicationMessenger.h" +#include "GUIInfoManager.h" +#include "cores/VideoRenderers/RenderManager.h" +#include "cores/VideoRenderers/RenderFlags.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIWindowManager.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "threads/SingleLock.h" +#include "windowing/WindowingFactory.h" + +#include "utils/log.h" +#include "utils/TimeUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/StreamDetails.h" +#include "xbmc/playlists/PlayListM3U.h" + +#include "utils/LangCodeExpander.h" +#include "guilib/LocalizeStrings.h" + +#include "BitstreamConverter.h" +#include "storage/MediaManager.h" +#include "GUIUserMessages.h" +#include "utils/StreamUtils.h" + +#include "DVDInputStreams/DVDFactoryInputStream.h" +#include "DVDInputStreams/DVDInputStreamNavigator.h" +#include "DVDInputStreams/DVDInputStreamTV.h" + +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "DVDDemuxers/DVDDemuxVobsub.h" +#include "DVDDemuxers/DVDFactoryDemuxer.h" +#include "DVDDemuxers/DVDDemuxFFmpeg.h" + +#include "DVDFileInfo.h" + +#include "Util.h" +#include "LangInfo.h" + +#include "utils/JobManager.h" +//#include "cores/AudioEngine/AEFactory.h" +//#include "cores/AudioEngine/Utils/AEUtil.h" +#include "xbmc/ThumbLoader.h" + +using namespace XFILE; + +// **************************************************************** +void COMXSelectionStreams::Clear(StreamType type, StreamSource source) +{ + CSingleLock lock(m_section); + for(int i=m_Streams.size()-1;i>=0;i--) + { + if(type && m_Streams[i].type != type) + continue; + + if(source && m_Streams[i].source != source) + continue; + + m_Streams.erase(m_Streams.begin() + i); + } +} + +void COMXPlayer::GetAudioStreamLanguage(int iStream, CStdString &strLanguage) +{ + strLanguage = ""; + OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream); + if(s.language.length() > 0) + strLanguage = s.language; +} + +OMXSelectionStream& COMXSelectionStreams::Get(StreamType type, int index) +{ + CSingleLock lock(m_section); + int count = -1; + for(int i=0;i<(int)m_Streams.size();i++) + { + if(m_Streams[i].type != type) + continue; + count++; + if(count == index) + return m_Streams[i]; + } + CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__); + return m_invalid; +} + +std::vector COMXSelectionStreams::Get(StreamType type) +{ + std::vector streams; + int count = Count(type); + for(int index = 0; index < count; ++index){ + streams.push_back(Get(type, index)); + } + return streams; +} + +#define PREDICATE_RETURN(lh, rh) \ + do { \ + if((lh) != (rh)) \ + return (lh) > (rh); \ + } while(0) + +static bool PredicateAudioPriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh) +{ + PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_AudioStream + , rh.type_index == g_settings.m_currentVideoSettings.m_AudioStream); + + if(!g_guiSettings.GetString("locale.audiolanguage").Equals("original")) + { + CStdString audio_language = g_langInfo.GetAudioLanguage(); + PREDICATE_RETURN(audio_language.Equals(lh.language.c_str()) + , audio_language.Equals(rh.language.c_str())); + } + + PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT + , rh.flags & CDemuxStream::FLAG_DEFAULT); + + PREDICATE_RETURN(lh.channels + , rh.channels); + + PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec) + , StreamUtils::GetCodecPriority(rh.codec)); + return false; +} + +static bool PredicateSubtitlePriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh) +{ + if(!g_settings.m_currentVideoSettings.m_SubtitleOn) + { + PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED + , rh.flags & CDemuxStream::FLAG_FORCED); + } + + PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream + , rh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream); + + CStdString subtitle_language = g_langInfo.GetSubtitleLanguage(); + if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original")) + { + PREDICATE_RETURN((lh.source == STREAM_SOURCE_DEMUX_SUB || lh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(lh.language.c_str()) + , (rh.source == STREAM_SOURCE_DEMUX_SUB || rh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(rh.language.c_str())); + } + + PREDICATE_RETURN(lh.source == STREAM_SOURCE_DEMUX_SUB + , rh.source == STREAM_SOURCE_DEMUX_SUB); + + PREDICATE_RETURN(lh.source == STREAM_SOURCE_TEXT + , rh.source == STREAM_SOURCE_TEXT); + + if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original")) + { + PREDICATE_RETURN(subtitle_language.Equals(lh.language.c_str()) + , subtitle_language.Equals(rh.language.c_str())); + } + + PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT + , rh.flags & CDemuxStream::FLAG_DEFAULT); + + return false; +} + +static bool PredicateVideoPriority(const OMXSelectionStream& lh, const OMXSelectionStream& rh) +{ + PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT + , rh.flags & CDemuxStream::FLAG_DEFAULT); + return false; +} + +bool COMXSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, OMXSelectionStream& out) +{ + CSingleLock lock(m_section); + for(int i=0;i<(int)m_Streams.size();i++) + { + if(m_Streams[i].type != type) + continue; + if((m_Streams[i].flags & flag) != flag) + continue; + out = m_Streams[i]; + return true; + } + return false; +} + +int COMXSelectionStreams::IndexOf(StreamType type, int source, int id) const +{ + CSingleLock lock(m_section); + int count = -1; + for(int i=0;i<(int)m_Streams.size();i++) + { + if(type && m_Streams[i].type != type) + continue; + count++; + if(source && m_Streams[i].source != source) + continue; + if(id < 0) + continue; + if(m_Streams[i].id == id) + return count; + } + if(id < 0) + return count; + else + return -1; +} + +int COMXSelectionStreams::IndexOf(StreamType type, COMXPlayer& p) const +{ + if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + int id = -1; + if(type == STREAM_AUDIO) + id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream(); + else if(type == STREAM_VIDEO) + id = p.m_CurrentVideo.id; + else if(type == STREAM_SUBTITLE) + id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream(); + + return IndexOf(type, STREAM_SOURCE_NAV, id); + } + + if(type == STREAM_AUDIO) + return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id); + else if(type == STREAM_VIDEO) + return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id); + else if(type == STREAM_SUBTITLE) + return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id); + else if(type == STREAM_TELETEXT) + return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id); + + return -1; +} + +int COMXSelectionStreams::Source(StreamSource source, std::string filename) +{ + CSingleLock lock(m_section); + int index = source - 1; + for(int i=0;i<(int)m_Streams.size();i++) + { + OMXSelectionStream &s = m_Streams[i]; + if(STREAM_SOURCE_MASK(s.source) != source) + continue; + // if it already exists, return same + if(s.filename == filename) + return s.source; + if(index < s.source) + index = s.source; + } + // return next index + return index + 1; +} + +void COMXSelectionStreams::Update(OMXSelectionStream& s) +{ + CSingleLock lock(m_section); + int index = IndexOf(s.type, s.source, s.id); + if(index >= 0) + { + OMXSelectionStream& o = Get(s.type, index); + s.type_index = o.type_index; + o = s; + } + else + { + s.type_index = Count(s.type); + m_Streams.push_back(s); + } +} + +void COMXSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer) +{ + if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input; + string filename = nav->GetFileName(); + int source = Source(STREAM_SOURCE_NAV, filename); + + int count; + count = nav->GetAudioStreamCount(); + for(int i=0;iGetAudioStreamLanguage(i); + s.flags = CDemuxStream::FLAG_NONE; + s.filename = filename; + s.channels = 0; + Update(s); + } + + count = nav->GetSubTitleStreamCount(); + for(int i=0;iGetSubtitleStreamLanguage(i); + s.flags = CDemuxStream::FLAG_NONE; + s.filename = filename; + s.channels = 0; + Update(s); + } + } + else if(demuxer) + { + string filename = demuxer->GetFileName(); + int count = demuxer->GetNrOfStreams(); + int source; + if(input) /* hack to know this is sub decoder */ + source = Source(STREAM_SOURCE_DEMUX, filename); + else + source = Source(STREAM_SOURCE_DEMUX_SUB, filename); + + + for(int i=0;iGetStream(i); + /* make sure stream is marked with right source */ + stream->source = source; + + OMXSelectionStream s; + s.source = source; + s.type = stream->type; + s.id = stream->iId; + s.language = stream->language; + s.flags = stream->flags; + s.filename = demuxer->GetFileName(); + stream->GetStreamName(s.name); + CStdString codec; + demuxer->GetStreamCodecName(stream->iId, codec); + s.codec = codec; + s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below. + if(stream->type == STREAM_AUDIO) + { + std::string type; + ((CDemuxStreamAudio*)stream)->GetStreamType(type); + if(type.length() > 0) + { + if(s.name.length() > 0) + s.name += " - "; + s.name += type; + } + s.channels = ((CDemuxStreamAudio*)stream)->iChannels; + } + Update(s); + } + } +} + +// **************************************************************** +COMXPlayer::COMXPlayer(IPlayerCallback &callback) + : IPlayer(callback), + CThread("COMXPlayer"), + m_ready(true), + m_CurrentAudio(STREAM_AUDIO, DVDPLAYER_AUDIO), + m_CurrentVideo(STREAM_VIDEO, DVDPLAYER_VIDEO), + m_CurrentSubtitle(STREAM_SUBTITLE, DVDPLAYER_SUBTITLE), + m_CurrentTeletext(STREAM_TELETEXT, DVDPLAYER_TELETEXT), + m_player_video(&m_av_clock, &m_overlayContainer, m_messenger), + m_player_audio(&m_av_clock, m_messenger), + m_player_subtitle(&m_overlayContainer), + m_messenger("player") +{ + m_bAbortRequest = false; + m_pDemuxer = NULL; + m_pSubtitleDemuxer = NULL; + m_pInputStream = NULL; + m_UpdateApplication = 0; + m_caching = CACHESTATE_DONE; + m_playSpeed = DVD_PLAYSPEED_NORMAL; + + m_State.Clear(); + m_dvd.Clear(); + m_EdlAutoSkipMarkers.Clear(); + + memset(&m_SpeedState, 0, sizeof(m_SpeedState)); +} + +COMXPlayer::~COMXPlayer() +{ + CloseFile(); +} + +bool COMXPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) +{ + try + { + CLog::Log(LOGNOTICE, "COMXPlayer: Opening: %s", file.GetPath().c_str()); + // if playing a file close it first + // this has to be changed so we won't have to close it. + CloseFile(); + + m_playSpeed = DVD_PLAYSPEED_NORMAL; + SetPlaySpeed(DVD_PLAYSPEED_NORMAL); + + m_PlayerOptions = options; + m_bAbortRequest = false; + + m_UpdateApplication = 0; + m_offset_pts = 0; + m_current_volume = 0; + m_change_volume = true; + + m_item = file; + m_mimetype = file.GetMimeType(); + m_filename = file.GetPath(); + + m_State.Clear(); + + m_ready.Reset(); + + g_renderManager.PreInit(); + + Create(); + + if(!m_ready.WaitMSec(100)) + { + CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY); + if(dialog) + { + dialog->Show(); + while(!m_ready.WaitMSec(1)) + g_windowManager.ProcessRenderLoop(false); + dialog->Close(); + } + } + + // Playback might have been stopped due to some error + if (m_bStop || m_bAbortRequest) + return false; + + return true; + } + catch(...) + { + CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__); + return false; + } +} + +bool COMXPlayer::CloseFile() +{ + CLog::Log(LOGDEBUG, "COMXPlayer::CloseFile"); + + // unpause the player + SetPlaySpeed(DVD_PLAYSPEED_NORMAL); + + // set the abort request so that other threads can finish up + m_bAbortRequest = true; + + // tell demuxer to abort + if(m_pDemuxer) + m_pDemuxer->Abort(); + + if(m_pSubtitleDemuxer) + m_pSubtitleDemuxer->Abort(); + + CLog::Log(LOGDEBUG, "COMXPlayer: waiting for threads to exit"); + // wait for the main thread to finish up + // since this main thread cleans up all other resources and threads + // we are done after the StopThread call + StopThread(); + + CLog::Log(LOGDEBUG, "COMXPlayer: finished waiting"); + + m_Edl.Clear(); + m_EdlAutoSkipMarkers.Clear(); + + g_renderManager.UnInit(); + return true; +} + +bool COMXPlayer::IsPlaying() const +{ + return !m_bStop; +} + +void COMXPlayer::OnStartup() +{ + m_CurrentVideo.Clear(); + m_CurrentAudio.Clear(); + m_CurrentSubtitle.Clear(); + + m_messenger.Init(); + + CUtil::ClearTempFonts(); +} + +bool COMXPlayer::OpenInputStream() +{ + if(m_pInputStream) + SAFE_DELETE(m_pInputStream); + + CLog::Log(LOGNOTICE, "Creating InputStream"); + + // correct the filename if needed + CStdString filename(m_filename); + if (filename.Find("dvd://") == 0 + || filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0) + { + m_filename = g_mediaManager.TranslateDevicePath(""); + } +retry: + // before creating the input stream, if this is an HLS playlist then get the + // most appropriate bitrate based on our network settings + if (filename.Left(7) == "http://" && filename.Right(5) == ".m3u8") + { + // get the available bandwidth (as per user settings) + int maxrate = g_guiSettings.GetInt("network.bandwidth"); + if(maxrate <= 0) + maxrate = INT_MAX; + + // determine the most appropriate stream + m_filename = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(m_filename, (size_t)maxrate); + } + + m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype); + if(m_pInputStream == NULL) + { + CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str()); + return false; + } + else + m_pInputStream->SetFileItem(m_item); + + if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype)) + { + if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - failed to open [%s] as DVD ISO, trying Bluray", m_filename.c_str()); + m_mimetype = "bluray/iso"; + filename = m_filename; + filename = filename + "/BDMV/index.bdmv"; + int title = (int)m_item.GetProperty("BlurayStartingTitle").asInteger(); + if( title ) + filename.AppendFormat("?title=%d",title); + + m_filename = filename; + goto retry; + } + CLog::Log(LOGERROR, "COMXPlayer::OpenInputStream - error opening [%s]", m_filename.c_str()); + return false; + } + + if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) + || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) ) + { + CLog::Log(LOGINFO, "COMXPlayer::OpenInputStream - DVD/BD not supported"); + return false; + } + + // find any available external subtitles for non dvd files + if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) + && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) + && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)) + { + // find any available external subtitles + std::vector filenames; + CUtil::ScanForExternalSubtitles( m_filename, filenames ); + + // find any upnp subtitles + CStdString key("upnp:subtitle:1"); + for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s)) + filenames.push_back(m_item.GetProperty(key).asString()); + + for(unsigned int i=0;i 0) + { + m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream); + if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE) + { + CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__); + continue; + } + break; + } + + if(!m_pDemuxer) + { + CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__); + return false; + } + + } + catch(...) + { + CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__); + return false; + } + + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX); + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV); + m_SelectionStreams.Update(m_pInputStream, m_pDemuxer); + + int64_t len = m_pInputStream->GetLength(); + int64_t tim = m_pDemuxer->GetStreamLength(); + if(len > 0 && tim > 0) + m_pInputStream->SetReadRate(len * 1000 / tim); + + return true; +} + +void COMXPlayer::OpenDefaultStreams() +{ + // bypass for DVDs. The DVD Navigator has already dictated which streams to open. + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + return; + + OMXSelectionStreams streams; + bool valid; + + // open video stream + streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority); + valid = false; + for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it) + { + if(OpenVideoStream(it->id, it->source)) + valid = true;; + } + if(!valid) + CloseVideoStream(true); + + // open audio stream + if(m_PlayerOptions.video_only) + streams.clear(); + else + streams = m_SelectionStreams.Get(STREAM_AUDIO, PredicateAudioPriority); + valid = false; + + for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it) + { + if(OpenAudioStream(it->id, it->source)) + valid = true; + } + if(!valid) + CloseAudioStream(true); + + // enable subtitles + m_player_video.EnableSubtitle(g_settings.m_currentVideoSettings.m_SubtitleOn); + + // open subtitle stream + streams = m_SelectionStreams.Get(STREAM_SUBTITLE, PredicateSubtitlePriority); + valid = false; + for(OMXSelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it) + { + if(OpenSubtitleStream(it->id, it->source)) + { + valid = true; + if(it->flags & CDemuxStream::FLAG_FORCED) + m_player_video.EnableSubtitle(true); + } + } + if(!valid) + CloseSubtitleStream(true); + + // open teletext stream + /* + streams = m_SelectionStreams.Get(STREAM_TELETEXT); + valid = false; + for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it) + { + if(OpenTeletextStream(it->id, it->source)) + valid = true; + } + if(!valid) + CloseTeletextStream(true); + */ + + m_av_clock.OMXStop(); + m_av_clock.OMXReset(); +} + +bool COMXPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream) +{ + + // check if we should read from subtitle demuxer + if(m_player_subtitle.AcceptsData() && m_pSubtitleDemuxer) + { + if(m_pSubtitleDemuxer) + packet = m_pSubtitleDemuxer->Read(); + + if(packet) + { + UpdateCorrection(packet, m_offset_pts); + if(packet->iStreamId < 0) + return true; + + stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId); + if (!stream) + { + CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__); + return false; + } + if(stream->source == STREAM_SOURCE_NONE) + { + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB); + m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer); + } + return true; + } + } + + // read a data frame from stream. + if(m_pDemuxer) + packet = m_pDemuxer->Read(); + + if(packet) + { + UpdateCorrection(packet, m_offset_pts); + // this groupId stuff is getting a bit messy, need to find a better way + // currently it is used to determine if a menu overlay is associated with a picture + // for dvd's we use as a group id, the current cell and the current title + // to be a bit more precise we alse count the number of disc's in case of a pts wrap back in the same cell / title + packet->iGroupId = m_pInputStream->GetCurrentGroupId(); + + if(packet->iStreamId < 0) + return true; + + if(m_pDemuxer) + { + stream = m_pDemuxer->GetStream(packet->iStreamId); + if (!stream) + { + CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__); + return false; + } + if(stream->source == STREAM_SOURCE_NONE) + { + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX); + m_SelectionStreams.Update(m_pInputStream, m_pDemuxer); + } + } + return true; + } + return false; +} + +bool COMXPlayer::IsValidStream(COMXCurrentStream& stream) +{ + if(stream.id<0) + return true; // we consider non selected as valid + + int source = STREAM_SOURCE_MASK(stream.source); + if(source == STREAM_SOURCE_TEXT) + return true; + if(source == STREAM_SOURCE_DEMUX_SUB) + { + CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id); + if(st == NULL || st->disabled) + return false; + if(st->type != stream.type) + return false; + return true; + } + if(source == STREAM_SOURCE_DEMUX) + { + CDemuxStream* st = m_pDemuxer->GetStream(stream.id); + if(st == NULL || st->disabled) + return false; + if(st->type != stream.type) + return false; + + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream) + return false; + if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream) + return false; + } + + return true; + } + + return false; +} + +bool COMXPlayer::IsBetterStream(COMXCurrentStream& current, CDemuxStream* stream) +{ + // Do not reopen non-video streams if we're in video-only mode + if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO) + return false; + + if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) + || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) ) + { + int source_type; + + source_type = STREAM_SOURCE_MASK(current.source); + if(source_type != STREAM_SOURCE_DEMUX + && source_type != STREAM_SOURCE_NONE) + return false; + + source_type = STREAM_SOURCE_MASK(stream->source); + if(source_type != STREAM_SOURCE_DEMUX + || stream->type != current.type + || stream->iId == current.id) + return false; + + if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream) + return true; + if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream) + return true; + if(current.type == STREAM_VIDEO && current.id < 0) + return true; + } + else + { + if(stream->source == current.source + && stream->iId == current.id) + return false; + + if(stream->disabled) + return false; + + if(stream->type != current.type) + return false; + + if(current.type == STREAM_SUBTITLE) + return false; + + if(current.type == STREAM_TELETEXT) + return false; + + if(current.id < 0) + return true; + } + return false; +} + +bool COMXPlayer::WaitForPausedThumbJobs(int timeout_ms) +{ + // use m_bStop and Sleep so we can get canceled. + while (!m_bStop && (timeout_ms > 0)) + { + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + Sleep(100); + timeout_ms -= 100; + } + else + return true; + } + + return false; +} + +void COMXPlayer::Process() +{ + //bool bAEStopped = false; + + if(!m_av_clock.OMXInitialize(false, false)) + { + m_bAbortRequest = true; + return; + } + if(g_guiSettings.GetBool("videoplayer.adjustrefreshrate")) + m_av_clock.HDMIClockSync(); + + m_av_clock.OMXStateExecute(); + m_av_clock.OMXStart(); + + //CLog::Log(LOGDEBUG, "COMXPlayer: Thread started"); + + try + { + m_stats = false; + + if(!OpenInputStream()) + { + m_bAbortRequest = true; + return; + } + + if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CLog::Log(LOGNOTICE, "OMXPlayer: playing a dvd with menu's"); + m_PlayerOptions.starttime = 0; + + if(m_PlayerOptions.state.size() > 0) + ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(m_PlayerOptions.state); + else + ((CDVDInputStreamNavigator*)m_pInputStream)->EnableSubtitleStream(g_settings.m_currentVideoSettings.m_SubtitleOn); + + g_settings.m_currentVideoSettings.m_SubtitleCached = true; + } + + if(!OpenDemuxStream()) + { + m_bAbortRequest = true; + return; + } + + m_player_video.EnableFullscreen(true); + + OpenDefaultStreams(); + + // look for any EDL files + m_Edl.Clear(); + m_EdlAutoSkipMarkers.Clear(); + float fFramesPerSecond; + if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0) + { + fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale; + m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height); + } + + /* + * Check to see if the demuxer should start at something other than time 0. This will be the case + * if there was a start time specified as part of the "Start from where last stopped" (aka + * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0. + */ + CEdl::Cut cut; + int starttime = 0; + if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0) + { + if (m_PlayerOptions.startpercent > 0 && m_pDemuxer) + { + int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) ); + starttime = m_Edl.RestoreCutTime(playerStartTime); + } + else + { + starttime = m_Edl.RestoreCutTime((int64_t)m_PlayerOptions.starttime * 1000); // s to ms + } + CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime); + } + else if(m_Edl.InCut(0, &cut) + && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK)) + { + starttime = cut.end; + CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime); + if(cut.action == CEdl::COMM_BREAK) + { + /* + * Setup auto skip markers as if the commercial break had been skipped using standard + * detection. + */ + m_EdlAutoSkipMarkers.commbreak_start = cut.start; + m_EdlAutoSkipMarkers.commbreak_end = cut.end; + m_EdlAutoSkipMarkers.seek_to_start = true; + } + } + if(starttime > 0) + { + double startpts = DVD_NOPTS_VALUE; + if(m_pDemuxer) + { + if (m_pDemuxer->SeekTime(starttime, false, &startpts)) + CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime); + else + CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime); + } + + if(m_pSubtitleDemuxer) + { + if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts)) + CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime); + else + CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime); + } + } + + // make sure application know our info + UpdateApplication(0); + UpdatePlayState(0); + + if (m_PlayerOptions.identify == false) + m_callback.OnPlayBackStarted(); + + // we are done initializing now, set the readyevent + m_ready.Set(); + + SetCaching(CACHESTATE_FLUSH); + + // shutdown AE + /* + CAEFactory::Shutdown(); + bAEStopped = true; + */ + + // stop thumb jobs + CJobManager::GetInstance().Pause(kJobTypeMediaFlags); + + /* + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + if (!WaitForPausedThumbJobs(20000)) + { + CJobManager::GetInstance().UnPause(kJobTypeMediaFlags); + CLog::Log(LOGINFO, "COMXPlayer::Process:thumbgen jobs still running !!!"); + } + } + */ + + while (!m_bAbortRequest) + { + // handle messages send to this thread, like seek or demuxer reset requests + HandleMessages(); + + if(m_bAbortRequest) + break; + + // should we open a new input stream? + if(!m_pInputStream) + { + if (OpenInputStream() == false) + { + m_bAbortRequest = true; + break; + } + } + + // should we open a new demuxer? + if(!m_pDemuxer) + { + if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE) + break; + + if (m_pInputStream->IsEOF()) + break; + + if (OpenDemuxStream() == false) + { + m_bAbortRequest = true; + break; + } + + OpenDefaultStreams(); + UpdateApplication(0); + UpdatePlayState(0); + } + + // handle eventual seeks due to playspeed + HandlePlaySpeed(); + + // update player state + UpdatePlayState(200); + + // update application with our state + UpdateApplication(1000); + + // if the queues are full, no need to read more + if ((!m_player_audio.AcceptsData() && m_CurrentAudio.id >= 0) + || (!m_player_video.AcceptsData() && m_CurrentVideo.id >= 0)) + { + Sleep(10); + continue; + } + + // always yield to players if they have data + if((m_player_audio.HasData() || m_CurrentAudio.id < 0) + && (m_player_video.HasData() || m_CurrentVideo.id < 0)) + Sleep(0); + + DemuxPacket* pPacket = NULL; + CDemuxStream *pStream = NULL; + ReadPacket(pPacket, pStream); + if (pPacket && !pStream) + { + /* probably a empty packet, just free it and move on */ + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + continue; + } + + if (!pPacket) + { + // when paused, demuxer could be be returning empty + if (m_playSpeed == DVD_PLAYSPEED_PAUSE) + continue; + + // check for a still frame state + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* pStream = static_cast(m_pInputStream); + + // stills will be skipped + if(m_dvd.state == DVDSTATE_STILL) + { + if (m_dvd.iDVDStillTime > 0) + { + if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime) + { + m_dvd.iDVDStillTime = 0; + m_dvd.iDVDStillStartTime = 0; + m_dvd.state = DVDSTATE_NORMAL; + pStream->SkipStill(); + continue; + } + } + } + } + + // if there is another stream available, reopen demuxer + CDVDInputStream::ENextStream next = m_pInputStream->NextStream(); + if(next == CDVDInputStream::NEXTSTREAM_OPEN) + { + SAFE_DELETE(m_pDemuxer); + m_CurrentAudio.stream = NULL; + m_CurrentVideo.stream = NULL; + m_CurrentSubtitle.stream = NULL; + continue; + } + + // input stream asked us to just retry + if(next == CDVDInputStream::NEXTSTREAM_RETRY) + { + Sleep(100); + continue; + } + + // make sure we tell all players to finish it's data + if(m_CurrentAudio.inited) + m_player_audio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF)); + if(m_CurrentVideo.inited) + m_player_video.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF)); + if(m_CurrentSubtitle.inited) + m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF)); + m_CurrentAudio.inited = false; + m_CurrentVideo.inited = false; + m_CurrentSubtitle.inited = false; + m_CurrentAudio.started = false; + m_CurrentVideo.started = false; + m_CurrentSubtitle.started = false; + + // if we are caching, start playing it again + SetCaching(CACHESTATE_DONE); + + // while players are still playing, keep going to allow seekbacks + if(m_player_video.HasData() + || m_player_audio.HasData()) + { + Sleep(100); + continue; + } + + if (!m_pInputStream->IsEOF()) + CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__); + + break; + } + + // check so that none of our streams has become invalid + if (!IsValidStream(m_CurrentAudio) && m_player_audio.IsStalled()) CloseAudioStream(true); + if (!IsValidStream(m_CurrentVideo) && m_player_video.IsStalled()) CloseVideoStream(true); + if (!IsValidStream(m_CurrentSubtitle) && m_player_subtitle.IsStalled()) CloseSubtitleStream(true); + + // see if we can find something better to play + if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source); + if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source); + if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source); + + if(m_change_volume) + { + m_player_audio.SetCurrentVolume(m_current_volume); + m_change_volume = false; + } + + ProcessPacket(pStream, pPacket); + + // check if in a cut or commercial break that should be automatically skipped + CheckAutoSceneSkip(); + } + } + catch(...) + { + CLog::Log(LOGERROR, "COMXPlayer::Process: Exception thrown"); + } + + /* + if(bAEStopped) + { + // start AE + CAEFactory::LoadEngine(); + CAEFactory::StartEngine(); + + CAEFactory::SetMute (g_settings.m_bMute); + CAEFactory::SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode")); + } + */ + + // let thumbgen jobs resume. + CJobManager::GetInstance().UnPause(kJobTypeMediaFlags); +} + +void COMXPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket) +{ + /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/ + OMXStreamLock lock(this); + + try + { + if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO) + ProcessAudioData(pStream, pPacket); + else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO) + ProcessVideoData(pStream, pPacket); + else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE) + ProcessSubData(pStream, pPacket); + else + { + pStream->SetDiscard(AVDISCARD_ALL); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it + } + } + catch(...) + { + CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__); + } + +} + +void COMXPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket) +{ + if (m_CurrentAudio.stream != (void*)pStream) + { + /* check so that dmuxer hints or extra data hasn't changed */ + /* if they have, reopen stream */ + + if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true)) + OpenAudioStream( pPacket->iStreamId, pStream->source ); + + m_CurrentAudio.stream = (void*)pStream; + } + + // check if we are too slow and need to recache + CheckStartCaching(m_CurrentAudio); + + CheckContinuity(m_CurrentAudio, pPacket); + UpdateTimestamps(m_CurrentAudio, pPacket); + bool drop = false; + if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO)) + drop = true; + + /* + * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped. + * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the + * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside + * of any EDL section while EDL mute is still active. + */ + CEdl::Cut cut; + if (CheckSceneSkip(m_CurrentAudio)) + drop = true; + else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute + && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered + { + m_player_audio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true)); + m_EdlAutoSkipMarkers.mute = true; + } + else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) // Outside of any EDL + && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet + { + m_player_audio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false)); + m_EdlAutoSkipMarkers.mute = false; + } + + m_player_audio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop)); +} + +void COMXPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket) +{ + if (m_CurrentVideo.stream != (void*)pStream) + { + /* check so that dmuxer hints or extra data hasn't changed */ + /* if they have reopen stream */ + + if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true)) + OpenVideoStream(pPacket->iStreamId, pStream->source); + + m_CurrentVideo.stream = (void*)pStream; + } + + // check if we are too slow and need to recache + CheckStartCaching(m_CurrentVideo); + + if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes + { + CheckContinuity(m_CurrentVideo, pPacket); + UpdateTimestamps(m_CurrentVideo, pPacket); + } + + bool drop = false; + if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO)) + drop = true; + + if (CheckSceneSkip(m_CurrentVideo)) + drop = true; + + m_player_video.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop)); +} + +void COMXPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket) +{ + if (m_CurrentSubtitle.stream != (void*)pStream) + { + /* check so that dmuxer hints or extra data hasn't changed */ + /* if they have reopen stream */ + + if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true)) + OpenSubtitleStream(pPacket->iStreamId, pStream->source); + + m_CurrentSubtitle.stream = (void*)pStream; + } + + UpdateTimestamps(m_CurrentSubtitle, pPacket); + + bool drop = false; + if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE)) + drop = true; + + if (CheckSceneSkip(m_CurrentSubtitle)) + drop = true; + + m_player_subtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop)); + + if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL); +} + +bool COMXPlayer::GetCachingTimes(double& level, double& delay, double& offset) +{ + if(!m_pInputStream || !m_pDemuxer) + return false; + + XFILE::SCacheStatus status; + if (!m_pInputStream->GetCacheStatus(&status)) + return false; + + int64_t cached = status.forward; + unsigned currate = status.currate; + unsigned maxrate = status.maxrate; + bool full = status.full; + + int64_t length = m_pInputStream->GetLength(); + int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR); + + if(cached < 0 || length <= 0 || remain < 0) + return false; + + double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length; + double queued = 1000.0 * GetQueueTime() / play_sbp; + + delay = 0.0; + level = 0.0; + offset = (double)(cached + queued) / length; + + if (currate == 0) + return true; + + double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */ + double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */ + double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */ + double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */ + + delay = cache_left - play_left; + + if (full && (currate < maxrate) ) + level = -1.0; /* buffer is full & our read rate is too low */ + else + level = (cached + queued) / (cache_need + queued); + + return true; +} + +void COMXPlayer::HandlePlaySpeed() +{ + ECacheState caching = m_caching; + + if(IsInMenu() && caching != CACHESTATE_DONE) + caching = CACHESTATE_DONE; + + if(caching == CACHESTATE_FULL) + { + double level, delay, offset; + if(GetCachingTimes(level, delay, offset)) + { + if(level < 0.0) + caching = CACHESTATE_INIT; + if(level >= 1.0) + caching = CACHESTATE_INIT; + } + else + { + if ((!m_player_audio.AcceptsData() && m_CurrentAudio.id >= 0) + || (!m_player_video.AcceptsData() && m_CurrentVideo.id >= 0)) + caching = CACHESTATE_INIT; + } + } + + if(caching == CACHESTATE_INIT) + { + // if all enabled streams have been inited we are done + if((m_CurrentVideo.id < 0 || m_CurrentVideo.started) + && (m_CurrentAudio.id < 0 || m_CurrentAudio.started)) + caching = CACHESTATE_PLAY; + + // handle situation that we get no data on one stream + if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0) + { + if ((!m_player_audio.AcceptsData() && !m_CurrentVideo.started) + || (!m_player_video.AcceptsData() && !m_CurrentAudio.started)) + { + caching = CACHESTATE_DONE; + } + } + } + + if(caching == CACHESTATE_PLAY) + { + // if all enabled streams have started playing we are done + if((m_CurrentVideo.id < 0 || !m_player_video.IsStalled()) + && (m_CurrentAudio.id < 0 || !m_player_audio.IsStalled())) + caching = CACHESTATE_DONE; + } + + if(m_caching != caching) + SetCaching(caching); + + if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE) + { + if (IsInMenu()) + { + // this can't be done in menu + SetPlaySpeed(DVD_PLAYSPEED_NORMAL); + + } + else if (m_CurrentVideo.id >= 0 + && m_CurrentVideo.inited == true + && m_SpeedState.lastpts != m_player_video.GetCurrentPTS() + && m_SpeedState.lasttime != GetTime()) + { + m_SpeedState.lastpts = m_player_video.GetCurrentPTS(); + m_SpeedState.lasttime = GetTime(); + // check how much off clock video is when ff/rw:ing + // a problem here is that seeking isn't very accurate + // and since the clock will be resynced after seek + // we might actually not really be playing at the wanted + // speed. we'd need to have some way to not resync the clock + // after a seek to remember timing. still need to handle + // discontinuities somehow + + // when seeking, give the player a headstart to make sure + // the time it takes to seek doesn't make a difference. + double error; + error = m_av_clock.GetClock() - m_SpeedState.lastpts; + error *= m_playSpeed / abs(m_playSpeed); + + if(error > DVD_MSEC_TO_TIME(1000)) + { + CLog::Log(LOGDEBUG, "COMXPlayer::Process - Seeking to catch up"); + int64_t iTime = (int64_t)DVD_TIME_TO_MSEC(m_av_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL); + m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true)); + } + } + } +} + +bool COMXPlayer::CheckStartCaching(COMXCurrentStream& current) +{ + if(m_caching != CACHESTATE_DONE + || m_playSpeed != DVD_PLAYSPEED_NORMAL) + return false; + + if(IsInMenu()) + return false; + + if((current.type == STREAM_AUDIO && m_player_audio.IsStalled()) + || (current.type == STREAM_VIDEO && m_player_video.IsStalled())) + { + // don't start caching if it's only a single stream that has run dry + if(m_player_audio.GetLevel() > 50 + || m_player_video.GetLevel() > 50) + return false; + + if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP) + || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + SetCaching(CACHESTATE_INIT); + else + { + if(current.inited) + SetCaching(CACHESTATE_FULL); + else + SetCaching(CACHESTATE_INIT); + } + return true; + } + return false; +} + +bool COMXPlayer::CheckPlayerInit(COMXCurrentStream& current, unsigned int source) +{ + if(current.inited) + return false; + + if(current.startpts != DVD_NOPTS_VALUE) + { + if(current.dts == DVD_NOPTS_VALUE) + { + CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts); + return true; + } + + if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20)) + { + CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__); + if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE) + m_CurrentAudio.startpts = current.dts; + if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE) + m_CurrentVideo.startpts = current.dts; + if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE) + m_CurrentSubtitle.startpts = current.dts; + if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE) + m_CurrentTeletext.startpts = current.dts; + } + + if(current.dts < current.startpts) + { + CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts); + return true; + } + } + + //If this is the first packet after a discontinuity, send it as a resync + if (current.dts != DVD_NOPTS_VALUE) + { + current.inited = true; + current.startpts = current.dts; + + bool setclock = false; + if(m_playSpeed == DVD_PLAYSPEED_NORMAL) + { + if( source == DVDPLAYER_AUDIO) + setclock = !m_CurrentVideo.inited; + else if(source == DVDPLAYER_VIDEO) + setclock = !m_CurrentAudio.inited; + } + else + { + if(source == DVDPLAYER_VIDEO) + setclock = true; + } + + double starttime = current.startpts; + if(m_CurrentAudio.inited + && m_CurrentAudio.startpts != DVD_NOPTS_VALUE + && m_CurrentAudio.startpts < starttime) + starttime = m_CurrentAudio.startpts; + if(m_CurrentVideo.inited + && m_CurrentVideo.startpts != DVD_NOPTS_VALUE + && m_CurrentVideo.startpts < starttime) + starttime = m_CurrentVideo.startpts; + + starttime = current.startpts - starttime; + if(starttime > 0 && setclock) + { + if(starttime > DVD_SEC_TO_TIME(2)) + CLog::Log(LOGWARNING, "COMXPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime); + else + SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source); + } + + SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source); + } + return false; +} + +void COMXPlayer::UpdateCorrection(DemuxPacket* pkt, double correction) +{ + if(pkt->dts != DVD_NOPTS_VALUE) pkt->dts -= correction; + if(pkt->pts != DVD_NOPTS_VALUE) pkt->pts -= correction; +} + +void COMXPlayer::UpdateTimestamps(COMXCurrentStream& current, DemuxPacket* pPacket) +{ + double dts = current.dts; + /* update stored values */ + if(pPacket->dts != DVD_NOPTS_VALUE) + dts = pPacket->dts; + else if(pPacket->pts != DVD_NOPTS_VALUE) + dts = pPacket->pts; + + /* calculate some average duration */ + if(pPacket->duration != DVD_NOPTS_VALUE) + current.dur = pPacket->duration; + else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE) + current.dur = 0.1 * (current.dur * 9 + (dts - current.dts)); + + current.dts = dts; +} + +void COMXPlayer::UpdateLimits(double& minimum, double& maximum, double dts) +{ + if(dts == DVD_NOPTS_VALUE) + return; + if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts; + if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts; +} + +void COMXPlayer::CheckContinuity(COMXCurrentStream& current, DemuxPacket* pPacket) +{ + if (m_playSpeed < DVD_PLAYSPEED_PAUSE) + return; + + if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE) + return; + + double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE; + UpdateLimits(mindts, maxdts, m_CurrentAudio.dts); + UpdateLimits(mindts, maxdts, m_CurrentVideo.dts); + UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end()); + UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end()); + + /* if we don't have max and min, we can't do anything more */ + if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE ) + return; + + double correction = 0.0; + if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000)) + { + CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f" + , current.type, current.dts, pPacket->dts, pPacket->dts - maxdts); + correction = pPacket->dts - maxdts; + } + + /* if it's large scale jump, correct for it */ + if(pPacket->dts + DVD_MSEC_TO_TIME(100) < current.dts_end()) + { + CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - resync backward :%d, prev:%f, curr:%f, diff:%f" + , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts); + correction = pPacket->dts - current.dts_end(); + } + else if(pPacket->dts < current.dts) + { + CLog::Log(LOGDEBUG, "COMXPlayer::CheckContinuity - wrapback :%d, prev:%f, curr:%f, diff:%f" + , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts); + } + + if(correction != 0.0) + { + /* disable detection on next packet on other stream to avoid ping pong-ing */ + if(m_CurrentAudio.player != current.player) m_CurrentAudio.dts = DVD_NOPTS_VALUE; + if(m_CurrentVideo.player != current.player) m_CurrentVideo.dts = DVD_NOPTS_VALUE; + + m_offset_pts += correction; + UpdateCorrection(pPacket, correction); + } +} + +bool COMXPlayer::CheckSceneSkip(COMXCurrentStream& current) +{ + if(!m_Edl.HasCut()) + return false; + + if(current.dts == DVD_NOPTS_VALUE) + return false; + + if(current.inited == false) + return false; + + CEdl::Cut cut; + return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &cut) && cut.action == CEdl::CUT; +} + +void COMXPlayer::CheckAutoSceneSkip() +{ + if(!m_Edl.HasCut()) + return; + + /* + * Check that there is an audio and video stream. + */ + if(m_CurrentAudio.id < 0 + || m_CurrentVideo.id < 0) + return; + + /* + * If there is a startpts defined for either the audio or video stream then dvdplayer is still + * still decoding frames to get to the previously requested seek point. + */ + if(m_CurrentAudio.inited == false + || m_CurrentVideo.inited == false) + return; + + if(m_CurrentAudio.dts == DVD_NOPTS_VALUE + || m_CurrentVideo.dts == DVD_NOPTS_VALUE) + return; + + const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts) + m_offset_pts); + + CEdl::Cut cut; + if(!m_Edl.InCut(clock, &cut)) + return; + + if(cut.action == CEdl::CUT + && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again + { + CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.", + __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), + CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str()); + /* + * Seeking either goes to the start or the end of the cut depending on the play direction. + */ + int64_t seek = GetPlaySpeed() >= 0 ? cut.end : cut.start; + /* + * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards. + */ + m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true)); + /* + * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping + * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the + * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek. + */ + m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start; + } + else if(cut.action == CEdl::COMM_BREAK + && GetPlaySpeed() >= 0 + && cut.start > m_EdlAutoSkipMarkers.commbreak_end) + { + CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)", + __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(), + CEdl::MillisecondsToTimeString(clock).c_str()); + /* + * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards. + */ + m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true)); + /* + * Each commercial break is only skipped once so poorly detected commercial breaks can be + * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back + * to the start of the commercial break if incorrectly flagged. + */ + m_EdlAutoSkipMarkers.commbreak_start = cut.start; + m_EdlAutoSkipMarkers.commbreak_end = cut.end; + m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start + } +} + +void COMXPlayer::SynchronizeDemuxer(unsigned int timeout) +{ + if(IsCurrentThread()) + return; + if(!m_messenger.IsInited()) + return; + + CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0); + m_messenger.Put(message->Acquire()); + message->Wait(&m_bStop, 0); + message->Release(); +} + +void COMXPlayer::SynchronizePlayers(unsigned int sources) +{ + /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */ + const int timeout = 10*1000; // in milliseconds + + CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources); + if (m_CurrentAudio.id >= 0) + m_player_audio.SendMessage(message->Acquire()); + + if (m_CurrentVideo.id >= 0) + m_player_video.SendMessage(message->Acquire()); +/* TODO - we have to rewrite the sync class, to not require + all other players waiting for subtitle, should only + be the oposite way + if (m_CurrentSubtitle.id >= 0) + m_player_subtitle.SendMessage(message->Acquire()); +*/ + message->Release(); +} + +void COMXPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target) +{ + if(target == DVDPLAYER_AUDIO) + m_player_audio.SendMessage(pMsg); + if(target == DVDPLAYER_VIDEO) + m_player_video.SendMessage(pMsg); + if(target == DVDPLAYER_SUBTITLE) + m_player_subtitle.SendMessage(pMsg); +} + +void COMXPlayer::OnExit() +{ + try + { + CLog::Log(LOGNOTICE, "COMXPlayer::OnExit()"); + + m_av_clock.OMXStop(); + m_av_clock.OMXStateIdle(); + + // set event to inform openfile something went wrong in case openfile is still waiting for this event + SetCaching(CACHESTATE_DONE); + + // close each stream + if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "OMXPlayer: eof, waiting for queues to empty"); + if (m_CurrentAudio.id >= 0) + { + CLog::Log(LOGNOTICE, "OMXPlayer: closing audio stream"); + CloseAudioStream(!m_bAbortRequest); + } + if (m_CurrentVideo.id >= 0) + { + CLog::Log(LOGNOTICE, "OMXPlayer: closing video stream"); + CloseVideoStream(!m_bAbortRequest); + } + if (m_CurrentSubtitle.id >= 0) + { + CLog::Log(LOGNOTICE, "OMXPlayer: closing subtitle stream"); + CloseSubtitleStream(!m_bAbortRequest); + } + /* + if (m_CurrentTeletext.id >= 0) + { + CLog::Log(LOGNOTICE, "OMXPlayer: closing teletext stream"); + CloseTeletextStream(!m_bAbortRequest); + } + */ + // destroy the demuxer + if (m_pDemuxer) + { + CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting demuxer"); + delete m_pDemuxer; + } + m_pDemuxer = NULL; + + if (m_pSubtitleDemuxer) + { + CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting subtitle demuxer"); + delete m_pSubtitleDemuxer; + } + m_pSubtitleDemuxer = NULL; + + // destroy the inputstream + if (m_pInputStream) + { + CLog::Log(LOGNOTICE, "COMXPlayer::OnExit() deleting input stream"); + delete m_pInputStream; + } + m_pInputStream = NULL; + + // clean up all selection streams + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE); + + m_messenger.End(); + + m_av_clock.OMXDeinitialize(); + + } + catch (...) + { + CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__); + m_pInputStream = NULL; + m_pDemuxer = NULL; + } + + m_bStop = true; + // if we didn't stop playing, advance to the next item in xbmc's playlist + if(m_PlayerOptions.identify == false) + { + if (m_bAbortRequest) + m_callback.OnPlayBackStopped(); + else + m_callback.OnPlayBackEnded(); + } + + // set event to inform openfile something went wrong in case openfile is still waiting for this event + m_ready.Set(); +} + +void COMXPlayer::HandleMessages() +{ + CDVDMsg* pMsg; + OMXStreamLock lock(this); + + while (m_messenger.Get(&pMsg, 0) == MSGQ_OK) + { + + try + { + if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 + && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0) + { + CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg)); + + if(!msg.GetTrickPlay()) + { + g_infoManager.SetDisplayAfterSeek(100000); + if(msg.GetFlush()) + SetCaching(CACHESTATE_FLUSH); + } + + double start = DVD_NOPTS_VALUE; + + int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime(); + CLog::Log(LOGDEBUG, "demuxer seek to: %d", time); + if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start)) + { + CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time); + if(m_pSubtitleDemuxer) + { + if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward())) + CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time); + } + FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate()); + } + else + CLog::Log(LOGWARNING, "error while seeking"); + + // set flag to indicate we have finished a seeking request + if(!msg.GetTrickPlay()) + { + g_infoManager.m_performingSeek = false; + g_infoManager.SetDisplayAfterSeek(); + } + + // dvd's will issue a HOP_CHANNEL that we need to skip + if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + m_dvd.state = DVDSTATE_SEEK; + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 + && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0) + { + g_infoManager.SetDisplayAfterSeek(100000); + SetCaching(CACHESTATE_FLUSH); + + CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg)); + double start = DVD_NOPTS_VALUE; + + // This should always be the case. + if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start)) + { + FlushBuffers(false, start, true); + m_callback.OnPlayBackSeekChapter(msg.GetChapter()); + } + + g_infoManager.SetDisplayAfterSeek(); + } + else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET)) + { + m_CurrentAudio.stream = NULL; + m_CurrentVideo.stream = NULL; + m_CurrentSubtitle.stream = NULL; + + // we need to reset the demuxer, probably because the streams have changed + if(m_pDemuxer) + m_pDemuxer->Reset(); + if(m_pSubtitleDemuxer) + m_pSubtitleDemuxer->Reset(); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM)) + { + CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg; + + OMXSelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId()); + if(st.source != STREAM_SOURCE_NONE) + { + if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream; + if(pStream->SetActiveAudioStream(st.id)) + { + m_dvd.iSelectedAudioStream = -1; + CloseAudioStream(false); + CloseVideoStream(false); + m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true)); + } + } + else + { + CloseAudioStream(false); + OpenAudioStream(st.id, st.source); + m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true)); + } + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM)) + { + CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg; + + OMXSelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId()); + if(st.source != STREAM_SOURCE_NONE) + { + if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream; + if(pStream->SetActiveSubtitleStream(st.id)) + { + m_dvd.iSelectedSPUStream = -1; + CloseSubtitleStream(false); + } + } + else + { + CloseSubtitleStream(false); + OpenSubtitleStream(st.id, st.source); + } + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE)) + { + CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg; + + m_player_video.EnableSubtitle(pValue->m_value); + + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + static_cast(m_pInputStream)->EnableSubtitleStream(pValue->m_value); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE)) + { + g_infoManager.SetDisplayAfterSeek(100000); + SetCaching(CACHESTATE_FLUSH); + + CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg; + + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + std::string s = pMsgPlayerSetState->GetState(); + ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(s); + m_dvd.state = DVDSTATE_NORMAL; + m_dvd.iDVDStillStartTime = 0; + m_dvd.iDVDStillTime = 0; + } + + g_infoManager.SetDisplayAfterSeek(); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD)) + { + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + static_cast(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) + { + FlushBuffers(false); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) + { + int speed = static_cast(pMsg)->m_value; + + // correct our current clock, as it would start going wrong otherwise + if(m_State.timestamp > 0) + { + double offset; + offset = m_av_clock.GetAbsoluteClock() - m_State.timestamp; + offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL; + if(offset > 1000) offset = 1000; + if(offset < -1000) offset = -1000; + m_State.time += DVD_TIME_TO_MSEC(offset); + m_State.timestamp = m_av_clock.GetAbsoluteClock(); + } + + if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed) + m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL); + + // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE + // audioplayer, stops outputing audio to audiorendere, but still tries to + // sleep an correct amount for each packet + // videoplayer just plays faster after the clock speed has been increased + // 1. disable audio + // 2. skip frames and adjust their pts or the clock + m_playSpeed = speed; + m_caching = CACHESTATE_DONE; + m_av_clock.SetSpeed(speed); + m_player_audio.SetSpeed(speed); + m_player_video.SetSpeed(speed); + m_av_clock.OMXSetSpeed(m_playSpeed); + + // TODO - we really shouldn't pause demuxer + // until our buffers are somewhat filled + if(m_pDemuxer) + m_pDemuxer->SetSpeed(speed); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || + pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) || + (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)) + { + CDVDInputStream::IChannel* input = dynamic_cast(m_pInputStream); + if(input) + { + g_infoManager.SetDisplayAfterSeek(100000); + + bool result; + if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT)) + result = input->SelectChannel(static_cast(pMsg)->m_value); + else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT)) + result = input->NextChannel(); + else + result = input->PrevChannel(); + + if(result) + { + FlushBuffers(false); + SAFE_DELETE(m_pDemuxer); + } + + g_infoManager.SetDisplayAfterSeek(); + } + } + else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION)) + OnAction(((CDVDMsgType*)pMsg)->m_value); + else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) + { + int player = ((CDVDMsgInt*)pMsg)->m_value; + if(player == DVDPLAYER_AUDIO) + m_CurrentAudio.started = true; + if(player == DVDPLAYER_VIDEO) + m_CurrentVideo.started = true; + CLog::Log(LOGDEBUG, "COMXPlayer::HandleMessages - player started %d", player); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__); + } + + pMsg->Release(); + } +} + +void COMXPlayer::SetCaching(ECacheState state) +{ + if(state == CACHESTATE_FLUSH) + { + double level, delay, offset; + if(GetCachingTimes(level, delay, offset)) + state = CACHESTATE_FULL; + else + state = CACHESTATE_INIT; + } + + if(m_caching == state) + return; + + CLog::Log(LOGDEBUG, "COMXPlayer::SetCaching - caching state %d", state); + if(state == CACHESTATE_FULL + || state == CACHESTATE_INIT) + { + m_av_clock.SetSpeed(DVD_PLAYSPEED_PAUSE); + m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_PAUSE); + m_player_audio.SetSpeed(DVD_PLAYSPEED_PAUSE); + m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); + m_player_video.SetSpeed(DVD_PLAYSPEED_PAUSE); + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); + } + + if(state == CACHESTATE_PLAY + ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY)) + { + m_av_clock.SetSpeed(m_playSpeed); + m_av_clock.OMXSetSpeed(m_playSpeed); + m_player_audio.SetSpeed(m_playSpeed); + m_player_video.SetSpeed(m_playSpeed); + } + m_caching = state; +} + +void COMXPlayer::SetPlaySpeed(int speed) +{ + /* only pause and normal playspeeds are allowed */ + if(speed < 0 || speed > DVD_PLAYSPEED_NORMAL) + return; + + m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed)); + m_player_audio.SetSpeed(speed); + m_player_video.SetSpeed(speed); + SynchronizeDemuxer(100); +} + +void COMXPlayer::Pause() +{ + if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching == CACHESTATE_FULL) + { + SetCaching(CACHESTATE_DONE); + return; + } + + if (m_playSpeed == DVD_PLAYSPEED_PAUSE) + { + SetPlaySpeed(DVD_PLAYSPEED_NORMAL); + m_callback.OnPlayBackResumed(); + } + else + { + SetPlaySpeed(DVD_PLAYSPEED_PAUSE); + m_callback.OnPlayBackPaused(); + } +} + +bool COMXPlayer::IsPaused() const +{ + return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching == CACHESTATE_FULL; +} + +bool COMXPlayer::HasVideo() const +{ + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) return true; + + return m_SelectionStreams.Count(STREAM_VIDEO) > 0 ? true : false; +} + +bool COMXPlayer::HasAudio() const +{ + return m_SelectionStreams.Count(STREAM_AUDIO) > 0 ? true : false; +} + +bool COMXPlayer::IsPassthrough() const +{ + return m_player_audio.Passthrough(); +} + +bool COMXPlayer::CanSeek() +{ + return GetTotalTime() > 0; +} + +void COMXPlayer::Seek(bool bPlus, bool bLargeStep) +{ + if(((bPlus && GetChapter() < GetChapterCount()) + || (!bPlus && GetChapter() > 1)) && bLargeStep) + { + if(bPlus) + SeekChapter(GetChapter() + 1); + else + SeekChapter(GetChapter() - 1); + return; + } + + int64_t seek; + if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2000*g_advancedSettings.m_videoTimeSeekForwardBig) + { + if (bLargeStep) + seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig; + else + seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward; + seek *= 1000; + seek += GetTime(); + } + else + { + float percent; + if (bLargeStep) + percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig; + else + percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward; + seek = (int64_t)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100); + } + + bool restore = true; + + if (m_Edl.HasCut()) + { + /* + * Alter the standard seek position based on whether any commercial breaks have been + * automatically skipped. + */ + const int clock = DVD_TIME_TO_MSEC(m_av_clock.GetClock()); + /* + * If a large backwards seek occurs within 10 seconds of the end of the last automated + * commercial skip, then seek back to the start of the commercial break under the assumption + * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to + * fumble around finding the remote. Only happens once per commercial break. + * + * Small skip does not trigger this in case the start of the commercial break was in fact fine + * but it skipped too far into the program. In that case small skip backwards behaves as normal. + */ + if (!bPlus && bLargeStep + && m_EdlAutoSkipMarkers.seek_to_start + && clock >= m_EdlAutoSkipMarkers.commbreak_end + && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec) + { + CLog::Log(LOGDEBUG, "%s - Seeking back to start of commercial break [%s - %s] as large backwards skip activated within 10 seconds of the automatic commercial skip (only done once per break).", + __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(), + CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str()); + seek = m_EdlAutoSkipMarkers.commbreak_start; + restore = false; + m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once. + } + /* + * If big skip forward within the last "reverted" commercial break, seek to the end of the + * commercial break under the assumption that the break was incorrectly flagged and playback has + * now reached the actual start of the commercial break. Assume that the end is flagged more + * correctly than the landing point for a standard big skip (ends seem to be flagged more + * accurately than the start). + */ + else if (bPlus && bLargeStep + && clock >= m_EdlAutoSkipMarkers.commbreak_start + && clock <= m_EdlAutoSkipMarkers.commbreak_end) + { + CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.", + __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(), + CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str()); + seek = m_EdlAutoSkipMarkers.commbreak_end; + restore = false; + } + } + + int64_t time = GetTime(); + if(g_application.CurrentFileItem().IsStack() + && (seek > GetTotalTimeInMsec() || seek < 0)) + { + g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime()); + // warning, don't access any dvdplayer variables here as + // the dvdplayer object may have been destroyed + return; + } + + m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore)); + SynchronizeDemuxer(100); + if (seek < 0) seek = 0; + m_callback.OnPlayBackSeek((int)seek, (int)(seek - time)); +} + +bool COMXPlayer::SeekScene(bool bPlus) +{ + if (!m_Edl.HasSceneMarker()) + return false; + + /* + * There is a 5 second grace period applied when seeking for scenes backwards. If there is no + * grace period applied it is impossible to go backwards past a scene marker. + */ + int64_t clock = GetTime(); + if (!bPlus && clock > 5 * 1000) // 5 seconds + clock -= 5 * 1000; + + int64_t iScenemarker; + if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker)) + { + /* + * Seeking is flushed and inaccurate, just like Seek() + */ + m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false)); + SynchronizeDemuxer(100); + return true; + } + return false; +} + +void COMXPlayer::GetAudioInfo(CStdString &strAudioInfo) +{ + { CSingleLock lock(m_StateSection); + strAudioInfo.Format("D(%s)", m_State.demux_audio.c_str()); + } + strAudioInfo.AppendFormat(" P(%s)", m_player_audio.GetPlayerInfo().c_str()); +} + +void COMXPlayer::GetVideoInfo(CStdString &strVideoInfo) +{ + { CSingleLock lock(m_StateSection); + strVideoInfo.Format("D(%s)", m_State.demux_video.c_str()); + } + strVideoInfo.AppendFormat(" P(%s)", m_player_video.GetPlayerInfo().c_str()); +} + +void COMXPlayer::GetGeneralInfo(CStdString& strGeneralInfo) +{ + if (!m_bStop) + { + double dDelay = 0; + + double apts = m_player_audio.GetCurrentPTS(); + double vpts = m_player_video.GetCurrentPTS(); + double dDiff = 0; + + if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE ) + dDiff = (apts - vpts) / DVD_TIME_BASE; + + CStdString strEDL; + strEDL.AppendFormat(", edl:%s", m_Edl.GetInfo().c_str()); + + CStdString strBuf; + CSingleLock lock(m_StateSection); + if(m_State.cache_bytes >= 0) + { + strBuf.AppendFormat(" cache:%s %2.0f%%" + , StringUtils::SizeToString(m_State.cache_bytes).c_str() + , m_State.cache_level * 100); + if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL) + strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay)); + } + + strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s, omx vb:%8d ad:% 6.3f )" + , dDelay + , dDiff + , strEDL.c_str() + , (int)(CThread::GetRelativeUsage()*100) + , (int)(m_player_audio.GetRelativeUsage()*100) + , (int)(m_player_video.GetRelativeUsage()*100) + , strBuf.c_str() + , m_player_video.GetFreeSpace() + , m_player_audio.GetDelay()); + + } +} + +void COMXPlayer::SeekPercentage(float fPercent) +{ + int64_t iTotalTime = GetTotalTimeInMsec(); + + if (!iTotalTime) + return; + + SeekTime((int64_t)(iTotalTime * fPercent / 100)); +} + +float COMXPlayer::GetPercentage() +{ + int64_t iTotalTime = GetTotalTimeInMsec(); + + if (!iTotalTime) + return 0.0f; + + return GetTime() * 100 / (float)iTotalTime; +} + +float COMXPlayer::GetCachePercentage() +{ + CSingleLock lock(m_StateSection); + return m_State.cache_offset * 100; // NOTE: Percentage returned is relative +} + +void COMXPlayer::SetAVDelay(float fValue) +{ + m_player_video.SetDelay(fValue * DVD_TIME_BASE); +} + +float COMXPlayer::GetAVDelay() +{ + return m_player_video.GetDelay() / (float)DVD_TIME_BASE; +} + +void COMXPlayer::SetSubTitleDelay(float fValue) +{ + m_player_video.SetSubtitleDelay(-fValue * DVD_TIME_BASE); +} + +float COMXPlayer::GetSubTitleDelay() +{ + return -m_player_video.GetSubtitleDelay() / DVD_TIME_BASE; +} + +int COMXPlayer::GetSubtitleCount() +{ + OMXStreamLock lock(this); + m_SelectionStreams.Update(m_pInputStream, m_pDemuxer); + return m_SelectionStreams.Count(STREAM_SUBTITLE); +} + +int COMXPlayer::GetSubtitle() +{ + return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this); +} + +void COMXPlayer::GetSubtitleName(int iStream, CStdString &strStreamName) +{ + strStreamName = ""; + OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream); + if(s.name.length() > 0) + strStreamName = s.name; + else + strStreamName = g_localizeStrings.Get(13205); // Unknown + + if(s.type == STREAM_NONE) + strStreamName += "(Invalid)"; +} + +void COMXPlayer::GetSubtitleLanguage(int iStream, CStdString &strStreamLang) +{ + OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream); + if (!g_LangCodeExpander.Lookup(strStreamLang, s.language)) + strStreamLang = g_localizeStrings.Get(13205); // Unknown +} + +void COMXPlayer::SetSubtitle(int iStream) +{ + m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream)); +} + +bool COMXPlayer::GetSubtitleVisible() +{ + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream; + if(pStream->IsInMenu()) + return g_settings.m_currentVideoSettings.m_SubtitleOn; + else + return pStream->IsSubtitleStreamEnabled(); + } + + return m_player_video.IsSubtitleEnabled(); +} + +void COMXPlayer::SetSubtitleVisible(bool bVisible) +{ + g_settings.m_currentVideoSettings.m_SubtitleOn = bVisible; + m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible)); +} + +int COMXPlayer::GetAudioStreamCount() +{ + OMXStreamLock lock(this); + m_SelectionStreams.Update(m_pInputStream, m_pDemuxer); + return m_SelectionStreams.Count(STREAM_AUDIO); +} + +int COMXPlayer::GetAudioStream() +{ + return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this); +} + +void COMXPlayer::GetAudioStreamName(int iStream, CStdString &strStreamName) +{ + strStreamName = ""; + OMXSelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream); + if(s.name.length() > 0) + strStreamName += s.name; + else + strStreamName += "Unknown"; + + if(s.type == STREAM_NONE) + strStreamName += " (Invalid)"; +} + +void COMXPlayer::SetAudioStream(int iStream) +{ + m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream)); + SynchronizeDemuxer(100); +} + +void COMXPlayer::SeekTime(int64_t iTime) +{ + int seekOffset = (int)(iTime - GetTime()); + m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true)); + SynchronizeDemuxer(100); + m_callback.OnPlayBackSeek((int)iTime, seekOffset); +} + +// return the time in milliseconds +int64_t COMXPlayer::GetTime() +{ + CSingleLock lock(m_StateSection); + double offset = 0; + if(m_State.timestamp > 0) + { + offset = m_av_clock.GetAbsoluteClock() - m_State.timestamp; + offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL; + if(offset > 1000) offset = 1000; + if(offset < -1000) offset = -1000; + } + //printf("COMXPlayer::GetTime %Lf offset %Lf %Lf\n", m_State.time, offset, m_av_clock.GetClock()); + return llrint(m_State.time + DVD_TIME_TO_MSEC(offset)); +} + +// return length in msec +int64_t COMXPlayer::GetTotalTimeInMsec() +{ + CSingleLock lock(m_StateSection); + return llrint(m_State.time_total); +} + +// return length in seconds.. this should be changed to return in milleseconds throughout xbmc +int64_t COMXPlayer::GetTotalTime() +{ + return GetTotalTimeInMsec(); +} + +void COMXPlayer::ToFFRW(int iSpeed) +{ + // can't rewind in menu as seeking isn't possible + // forward is fine + if (iSpeed < 0 && IsInMenu()) return; + + /* only pause and normal playspeeds are allowed */ + if(iSpeed > 1 || iSpeed < 0) + return; + + SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL); +} + +bool COMXPlayer::OpenAudioStream(int iStream, int source) +{ + CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source); + + if (!m_pDemuxer) + return false; + + CDemuxStream* pStream = m_pDemuxer->GetStream(iStream); + if (!pStream || pStream->disabled) + return false; + + if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 ) + { + // up until now we wheren't playing audio, but we did play video + // this will change what is used to sync the dvdclock. + // since the new audio data doesn't have to have any relation + // to the current video data in the packet que, we have to + // wait for it to empty + + // this happens if a new cell has audio data, but previous didn't + // and both have video data + + SynchronizePlayers(SYNCSOURCE_AUDIO); + } + + CDVDStreamInfo hint(*pStream, true); + + if(m_CurrentAudio.id < 0 + || m_CurrentAudio.hint != hint) + { + if(!m_player_audio.OpenStream(hint)) + { + /* mark stream as disabled, to disallaw further attempts*/ + CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream); + pStream->disabled = true; + pStream->SetDiscard(AVDISCARD_ALL); + return false; + } + m_av_clock.SetSpeed(DVD_PLAYSPEED_NORMAL); + m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_NORMAL); + } + else + m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + + /* store information about stream */ + m_CurrentAudio.id = iStream; + m_CurrentAudio.source = source; + m_CurrentAudio.hint = hint; + m_CurrentAudio.stream = (void*)pStream; + m_CurrentAudio.started = false; + + /* we are potentially going to be waiting on this */ + m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); + + /* software decoding normaly consumes full cpu time so prio it */ + m_player_audio.SetPriority(GetPriority()+1); + + return true; +} + +bool COMXPlayer::OpenVideoStream(int iStream, int source) +{ + CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source); + + if (!m_pDemuxer) + return false; + + CDemuxStream* pStream = m_pDemuxer->GetStream(iStream); + if(!pStream || pStream->disabled) + return false; + pStream->SetDiscard(AVDISCARD_NONE); + + CDVDStreamInfo hint(*pStream, true); + + if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) ) + { + /* set aspect ratio as requested by navigator for dvd's */ + float aspect = static_cast(m_pInputStream)->GetVideoAspectRatio(); + if(aspect != 0.0) + { + hint.aspect = aspect; + hint.forced_aspect = true; + } + hint.software = true; + } + + CDVDInputStream::IMenus* pMenus = dynamic_cast(m_pInputStream); + if(pMenus && pMenus->IsInMenu()) + hint.stills = true; + + if(m_CurrentVideo.id < 0 + || m_CurrentVideo.hint != hint) + { + if(!m_player_video.OpenStream(hint)) + { + /* mark stream as disabled, to disallaw further attempts */ + CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream); + pStream->disabled = true; + pStream->SetDiscard(AVDISCARD_ALL); + return false; + } + m_av_clock.SetSpeed(DVD_PLAYSPEED_NORMAL); + m_av_clock.OMXSetSpeed(DVD_PLAYSPEED_NORMAL); + } + else + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + + unsigned flags = 0; + if(m_filename.find("3DSBS") != string::npos) + flags = CONF_FLAGS_FORMAT_SBS; + m_player_video.SetFlags(flags); + + /* store information about stream */ + m_CurrentVideo.id = iStream; + m_CurrentVideo.source = source; + m_CurrentVideo.hint = hint; + m_CurrentVideo.stream = (void*)pStream; + m_CurrentVideo.started = false; + + /* we are potentially going to be waiting on this */ + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); + + /* use same priority for video thread as demuxing thread, as */ + /* otherwise demuxer will starve if video consumes the full cpu */ + m_player_video.SetPriority(GetPriority()); + + return true; +} + +bool COMXPlayer::OpenSubtitleStream(int iStream, int source) +{ + CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source); + + CDemuxStream* pStream = NULL; + std::string filename; + CDVDStreamInfo hint; + + if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB) + { + int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream); + if(index < 0) + return false; + OMXSelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index); + + if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename) + { + CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str()); + auto_ptr demux(new CDVDDemuxVobsub()); + if(!demux->Open(st.filename, st.filename2)) + return false; + m_pSubtitleDemuxer = demux.release(); + } + + pStream = m_pSubtitleDemuxer->GetStream(iStream); + if(!pStream || pStream->disabled) + return false; + pStream->SetDiscard(AVDISCARD_NONE); + double pts = m_player_video.GetCurrentPTS(); + if(pts == DVD_NOPTS_VALUE) + pts = m_CurrentVideo.dts; + if(pts == DVD_NOPTS_VALUE) + pts = 0; + pts += m_offset_pts; + m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE)); + + hint.Assign(*pStream, true); + } + else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT) + { + int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream); + if(index < 0) + return false; + filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename; + + hint.Clear(); + hint.fpsscale = m_CurrentVideo.hint.fpsscale; + hint.fpsrate = m_CurrentVideo.hint.fpsrate; + } + else + { + if(!m_pDemuxer) + return false; + pStream = m_pDemuxer->GetStream(iStream); + if(!pStream || pStream->disabled) + return false; + pStream->SetDiscard(AVDISCARD_NONE); + + hint.Assign(*pStream, true); + + if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + filename = "dvd"; + } + + if(m_CurrentSubtitle.id < 0 + || m_CurrentSubtitle.hint != hint) + { + if(m_CurrentSubtitle.id >= 0) + { + CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream"); + CloseSubtitleStream(false); + } + + if(!m_player_subtitle.OpenStream(hint, filename)) + { + CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream); + if(pStream) + { + pStream->disabled = true; + pStream->SetDiscard(AVDISCARD_ALL); + } + return false; + } + } + else + m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + + m_CurrentSubtitle.id = iStream; + m_CurrentSubtitle.source = source; + m_CurrentSubtitle.hint = hint; + m_CurrentSubtitle.stream = (void*)pStream; + m_CurrentSubtitle.started = false; + + return true; +} + +bool COMXPlayer::CloseAudioStream(bool bWaitForBuffers) +{ + if (m_CurrentAudio.id < 0) + return false; + + CLog::Log(LOGNOTICE, "Closing audio stream"); + + if(bWaitForBuffers) + SetCaching(CACHESTATE_DONE); + + m_player_audio.CloseStream(bWaitForBuffers); + + m_CurrentAudio.Clear(); + return true; +} + +bool COMXPlayer::CloseVideoStream(bool bWaitForBuffers) +{ + if (m_CurrentVideo.id < 0) + return false; + + CLog::Log(LOGNOTICE, "Closing video stream"); + + if(bWaitForBuffers) + SetCaching(CACHESTATE_DONE); + + m_player_video.CloseStream(bWaitForBuffers); + + m_CurrentVideo.Clear(); + return true; +} + +bool COMXPlayer::CloseSubtitleStream(bool bKeepOverlays) +{ + if (m_CurrentSubtitle.id < 0) + return false; + + CLog::Log(LOGNOTICE, "Closing subtitle stream"); + + m_player_subtitle.CloseStream(!bKeepOverlays); + + m_CurrentSubtitle.Clear(); + return true; +} + +void COMXPlayer::FlushBuffers(bool queued, double pts, bool accurate) +{ + double startpts; + if(accurate) + startpts = pts; + else + startpts = DVD_NOPTS_VALUE; + + /* call with demuxer pts */ + if(startpts != DVD_NOPTS_VALUE) + startpts -= m_offset_pts; + + m_CurrentAudio.inited = false; + m_CurrentAudio.dts = DVD_NOPTS_VALUE; + m_CurrentAudio.startpts = startpts; + + m_CurrentVideo.inited = false; + m_CurrentVideo.dts = DVD_NOPTS_VALUE; + m_CurrentVideo.startpts = startpts; + + m_CurrentSubtitle.inited = false; + m_CurrentSubtitle.dts = DVD_NOPTS_VALUE; + m_CurrentSubtitle.startpts = startpts; + + m_CurrentTeletext.inited = false; + m_CurrentTeletext.dts = DVD_NOPTS_VALUE; + m_CurrentTeletext.startpts = startpts; + + if(queued) + { + m_player_audio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP)); + m_player_subtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET)); + SynchronizePlayers(SYNCSOURCE_ALL); + } + else + { + m_player_video.Flush(); + m_player_audio.Flush(); + m_player_subtitle.Flush(); + + // clear subtitle and menu overlays + m_overlayContainer.Clear(); + + if(m_playSpeed == DVD_PLAYSPEED_NORMAL + || m_playSpeed == DVD_PLAYSPEED_PAUSE) + { + // make sure players are properly flushed, should put them in stalled state + CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0); + m_player_video.SendMessage(msg->Acquire(), 1); + m_player_audio.SendMessage(msg->Acquire(), 1); + msg->Wait(&m_bStop, 0); + msg->Release(); + + // purge any pending PLAYER_STARTED messages + m_messenger.Flush(CDVDMsg::PLAYER_STARTED); + + // we should now wait for init cache + SetCaching(CACHESTATE_FLUSH); + m_CurrentAudio.started = false; + m_CurrentVideo.started = false; + m_CurrentSubtitle.started = false; + m_CurrentTeletext.started = false; + } + + if(pts != DVD_NOPTS_VALUE) + m_av_clock.Discontinuity(pts); + UpdatePlayState(0); + } +} + +// since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is +int COMXPlayer::OnDVDNavResult(void* pData, int iMessage) +{ + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)) + { + if(iMessage == 0) + m_overlayContainer.Add((CDVDOverlay*)pData); + else if(iMessage == 1) + m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); + else if(iMessage == 2) + m_dvd.iSelectedAudioStream = *(int*)pData; + else if(iMessage == 3) + m_dvd.iSelectedSPUStream = *(int*)pData; + else if(iMessage == 4) + m_player_video.EnableSubtitle(*(int*)pData ? true: false); + + return 0; + } + + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream; + + switch (iMessage) + { + case DVDNAV_STILL_FRAME: + { + //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME"); + + dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData; + // should wait the specified time here while we let the player running + // after that call dvdnav_still_skip(m_dvdnav); + + if (m_dvd.state != DVDSTATE_STILL) + { + // else notify the player we have received a still frame + + if(still_event->length < 0xff) + m_dvd.iDVDStillTime = still_event->length * 1000; + else + m_dvd.iDVDStillTime = 0; + + m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis(); + + /* adjust for the output delay in the video queue */ + DWORD time = 0; + if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 ) + { + time = (DWORD)(m_player_video.GetOutputDelay() / ( DVD_TIME_BASE / 1000 )); + if( time < 10000 && time > 0 ) + m_dvd.iDVDStillTime += time; + } + m_dvd.state = DVDSTATE_STILL; + CLog::Log(LOGDEBUG, + "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec", + still_event->length, time / 1000); + } + return NAVRESULT_HOLD; + } + break; + case DVDNAV_SPU_CLUT_CHANGE: + { + m_player_subtitle.SendMessage(new CDVDMsgSubtitleClutChange((BYTE*)pData)); + } + break; + case DVDNAV_SPU_STREAM_CHANGE: + { + dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData; + + int iStream = event->physical_wide; + bool visible = !(iStream & 0x80); + + m_player_video.EnableSubtitle(visible); + + if (iStream >= 0) + m_dvd.iSelectedSPUStream = (iStream & ~0x80); + else + m_dvd.iSelectedSPUStream = -1; + + m_CurrentSubtitle.stream = NULL; + } + break; + case DVDNAV_AUDIO_STREAM_CHANGE: + { + // This should be the correct way i think, however we don't have any streams right now + // since the demuxer hasn't started so it doesn't change. not sure how to do this. + dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData; + + // Tell system what audiostream should be opened by default + if (event->logical >= 0) + m_dvd.iSelectedAudioStream = event->physical; + else + m_dvd.iSelectedAudioStream = -1; + + m_CurrentAudio.stream = NULL; + } + break; + case DVDNAV_HIGHLIGHT: + { + //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData; + int iButton = pStream->GetCurrentButton(); + CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton); + m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL); + } + break; + case DVDNAV_VTS_CHANGE: + { + //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData; + CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE"); + + //Make sure we clear all the old overlays here, or else old forced items are left. + m_overlayContainer.Clear(); + + //Force an aspect ratio that is set in the dvdheaders if available + m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio(); + if( m_player_video.IsInited() ) + m_player_video.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect)); + + m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV); + m_SelectionStreams.Update(m_pInputStream, m_pDemuxer); + + return NAVRESULT_HOLD; + } + break; + case DVDNAV_CELL_CHANGE: + { + //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData; + CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE"); + + m_dvd.state = DVDSTATE_NORMAL; + + if( m_player_video.IsInited() ) + m_player_video.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP)); + } + break; + case DVDNAV_NAV_PACKET: + { + //pci_t* pci = (pci_t*)pData; + + // this should be possible to use to make sure we get + // seamless transitions over these boundaries + // if we remember the old vobunits boundaries + // when a packet comes out of demuxer that has + // pts values outside that boundary, it belongs + // to the new vobunit, wich has new timestamps + UpdatePlayState(0); + } + break; + case DVDNAV_HOP_CHANNEL: + { + // This event is issued whenever a non-seamless operation has been executed. + // Applications with fifos should drop the fifos content to speed up responsiveness. + CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL"); + if(m_dvd.state == DVDSTATE_SEEK) + m_dvd.state = DVDSTATE_NORMAL; + else + m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); + + return NAVRESULT_ERROR; + } + break; + case DVDNAV_STOP: + { + CLog::Log(LOGDEBUG, "DVDNAV_STOP"); + m_dvd.state = DVDSTATE_NORMAL; + } + break; + default: + {} + break; + } + } + return NAVRESULT_NOP; +} + +bool COMXPlayer::OnAction(const CAction &action) +{ +#define THREAD_ACTION(action) \ + do { \ + if (!IsCurrentThread()) { \ + m_messenger.Put(new CDVDMsgType(CDVDMsg::GENERAL_GUI_ACTION, action)); \ + return true; \ + } \ + } while(false) + + CDVDInputStream::IMenus* pMenus = dynamic_cast(m_pInputStream); + if (pMenus) + { + if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pMenus->GetTotalButtons() == 0 ) + { + switch(action.GetID()) + { + case ACTION_NEXT_ITEM: + case ACTION_MOVE_RIGHT: + case ACTION_MOVE_UP: + case ACTION_SELECT_ITEM: + { + THREAD_ACTION(action); + /* this will force us out of the stillframe */ + CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__); + m_dvd.iDVDStillStartTime = 0; + m_dvd.iDVDStillTime = 1; + } + return true; + } + } + + + switch (action.GetID()) + { +/* this code is disabled to allow switching playlist items (dvdimage "stacks") */ +#if 0 + case ACTION_PREV_ITEM: // SKIP-: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - pushed prev"); + pMenus->OnPrevious(); + g_infoManager.SetDisplayAfterSeek(); + return true; + } + break; + case ACTION_NEXT_ITEM: // SKIP+: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - pushed next"); + pMenus->OnNext(); + g_infoManager.SetDisplayAfterSeek(); + return true; + } + break; +#endif + case ACTION_SHOW_VIDEOMENU: // start button + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - go to menu"); + pMenus->OnMenu(); + // send a message to everyone that we've gone to the menu + CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0); + g_windowManager.SendMessage(msg); + return true; + } + break; + } + if (pMenus->IsInMenu()) + { + switch (action.GetID()) + { + case ACTION_NEXT_ITEM: + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide"); + pMenus->OnNext(); + g_infoManager.SetDisplayAfterSeek(); + return true; + case ACTION_PREV_ITEM: + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide"); + pMenus->OnPrevious(); + g_infoManager.SetDisplayAfterSeek(); + return true; + case ACTION_PREVIOUS_MENU: + case ACTION_NAV_BACK: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - menu back"); + pMenus->OnBack(); + } + break; + case ACTION_MOVE_LEFT: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - move left"); + pMenus->OnLeft(); + } + break; + case ACTION_MOVE_RIGHT: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - move right"); + pMenus->OnRight(); + } + break; + case ACTION_MOVE_UP: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - move up"); + pMenus->OnUp(); + } + break; + case ACTION_MOVE_DOWN: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - move down"); + pMenus->OnDown(); + } + break; + + case ACTION_MOUSE_MOVE: + case ACTION_MOUSE_LEFT_CLICK: + { + CRect rs, rd; + GetVideoRect(rs, rd); + CPoint pt(action.GetAmount(), action.GetAmount(1)); + if (!rd.PtInRect(pt)) + return false; // out of bounds + THREAD_ACTION(action); + // convert to video coords... + pt -= CPoint(rd.x1, rd.y1); + pt.x *= rs.Width() / rd.Width(); + pt.y *= rs.Height() / rd.Height(); + pt += CPoint(rs.x1, rs.y1); + if (action.GetID() == ACTION_MOUSE_LEFT_CLICK) + return pMenus->OnMouseClick(pt); + return pMenus->OnMouseMove(pt); + } + break; + case ACTION_SELECT_ITEM: + { + THREAD_ACTION(action); + CLog::Log(LOGDEBUG, " - button select"); + // show button pushed overlay + if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + m_player_subtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED); + + pMenus->ActivateButton(); + } + break; + case REMOTE_0: + case REMOTE_1: + case REMOTE_2: + case REMOTE_3: + case REMOTE_4: + case REMOTE_5: + case REMOTE_6: + case REMOTE_7: + case REMOTE_8: + case REMOTE_9: + { + THREAD_ACTION(action); + // Offset from key codes back to button number + int button = action.GetID() - REMOTE_0; + CLog::Log(LOGDEBUG, " - button pressed %d", button); + pMenus->SelectButton(button); + } + break; + default: + return false; + break; + } + return true; // message is handled + } + } + if (dynamic_cast(m_pInputStream)) + { + switch (action.GetID()) + { + case ACTION_NEXT_ITEM: + m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT)); + g_infoManager.SetDisplayAfterSeek(); + return true; + break; + + case ACTION_PREV_ITEM: + m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV)); + g_infoManager.SetDisplayAfterSeek(); + return true; + break; + + case ACTION_CHANNEL_SWITCH: + { + // Offset from key codes back to button number + int channel = action.GetAmount(); + m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel)); + g_infoManager.SetDisplayAfterSeek(); + return true; + } + break; + } + } + + switch (action.GetID()) + { + case ACTION_NEXT_ITEM: + if(GetChapterCount() > 0) + { + m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1)); + g_infoManager.SetDisplayAfterSeek(); + return true; + } + else + break; + case ACTION_PREV_ITEM: + if(GetChapterCount() > 0) + { + m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1)); + g_infoManager.SetDisplayAfterSeek(); + return true; + } + else + break; + } + + // return false to inform the caller we didn't handle the message + return false; +} + +bool COMXPlayer::IsInMenu() const +{ + CDVDInputStream::IMenus* pStream = dynamic_cast(m_pInputStream); + if (pStream) + { + if( m_dvd.state == DVDSTATE_STILL ) + return true; + else + return pStream->IsInMenu(); + } + return false; +} + +bool COMXPlayer::HasMenu() +{ + CDVDInputStream::IMenus* pStream = dynamic_cast(m_pInputStream); + if (pStream) + return true; + else + return false; +} + +bool COMXPlayer::GetCurrentSubtitle(CStdString& strSubtitle) +{ + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + return false; + + double pts = m_av_clock.OMXMediaTime(); + + m_player_subtitle.GetCurrentSubtitle(strSubtitle, pts - m_player_video.GetSubtitleDelay()); + + // In case we stalled, don't output any subs + if ((m_player_video.IsStalled() && HasVideo()) || (m_player_audio.IsStalled() && HasAudio())) + strSubtitle = m_lastSub; + else + m_lastSub = strSubtitle; + + return !strSubtitle.IsEmpty(); +} + +CStdString COMXPlayer::GetPlayerState() +{ + CSingleLock lock(m_StateSection); + return m_State.player_state; +} + +bool COMXPlayer::SetPlayerState(CStdString state) +{ + m_messenger.Put(new CDVDMsgPlayerSetState(state)); + return true; +} + +int COMXPlayer::GetChapterCount() +{ + CSingleLock lock(m_StateSection); + return m_State.chapter_count; +} + +int COMXPlayer::GetChapter() +{ + CSingleLock lock(m_StateSection); + return m_State.chapter; +} + +void COMXPlayer::GetChapterName(CStdString& strChapterName) +{ + CSingleLock lock(m_StateSection); + strChapterName = m_State.chapter_name; +} + +int COMXPlayer::SeekChapter(int iChapter) +{ + if (GetChapterCount() > 0) + { + if (iChapter < 0) + iChapter = 0; + if (iChapter > GetChapterCount()) + return 0; + + // Seek to the chapter. + m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter)); + SynchronizeDemuxer(100); + } + else + { + // Do a regular big jump. + if (GetChapter() > 0 && iChapter > GetChapter()) + Seek(true, true); + else + Seek(false, true); + } + return 0; +} + +int COMXPlayer::AddSubtitle(const CStdString& strSubPath) +{ + return AddSubtitleFile(strSubPath); +} + +int COMXPlayer::GetCacheLevel() const +{ + CSingleLock lock(m_StateSection); + return (int)(m_State.cache_level * 100); +} + +double COMXPlayer::GetQueueTime() +{ + int a = m_player_video.GetLevel(); + int v = m_player_audio.GetLevel(); + return max(a, v) * 8000.0 / 100; +} + +int COMXPlayer::GetAudioBitrate() +{ + return m_player_audio.GetAudioBitrate(); +} + +int COMXPlayer::GetVideoBitrate() +{ + return m_player_video.GetVideoBitrate(); +} + +int COMXPlayer::GetSourceBitrate() +{ + if (m_pInputStream) + return (int)m_pInputStream->GetBitstreamStats().GetBitrate(); + + return 0; +} + +int COMXPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags) +{ + std::string ext = URIUtils::GetExtension(filename); + std::string vobsubfile = subfilename; + if(ext == ".idx") + { + if (vobsubfile.empty()) + vobsubfile = URIUtils::ReplaceExtension(filename, ".sub"); + + CDVDDemuxVobsub v; + if(!v.Open(filename, vobsubfile)) + return -1; + m_SelectionStreams.Update(NULL, &v); + int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0); + m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags; + m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile; + return index; + } + if(ext == ".sub") + { + CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx")); + if (XFILE::CFile::Exists(strReplace)) + return -1; + } + OMXSelectionStream s; + s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename); + s.type = STREAM_SUBTITLE; + s.id = 0; + s.filename = filename; + s.name = URIUtils::GetFileName(filename); + s.flags = flags; + m_SelectionStreams.Update(s); + return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id); +} + +void COMXPlayer::UpdatePlayState(double timeout) +{ + if(m_State.timestamp != 0 + && m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > m_av_clock.GetAbsoluteClock()) + return; + + SPlayerState state(m_State); + + if (m_CurrentVideo.dts != DVD_NOPTS_VALUE) + state.dts = m_CurrentVideo.dts; + else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE) + state.dts = m_CurrentAudio.dts; + else + state.dts = m_av_clock.GetClock(); + + if(m_pDemuxer) + { + state.chapter = m_pDemuxer->GetChapter(); + state.chapter_count = m_pDemuxer->GetChapterCount(); + m_pDemuxer->GetChapterName(state.chapter_name); + + state.time = DVD_TIME_TO_MSEC(m_av_clock.GetClock() + m_offset_pts); + state.time_total = m_pDemuxer->GetStreamLength(); + } + + if(m_pInputStream) + { + // override from input stream if needed + + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + { + state.canrecord = static_cast(m_pInputStream)->CanRecord(); + state.recording = static_cast(m_pInputStream)->IsRecording(); + } + + CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast(m_pInputStream); + if (pDisplayTime) + { + state.time = pDisplayTime->GetTime(); + state.time_total = pDisplayTime->GetTotalTime(); + } + + if (dynamic_cast(m_pInputStream)) + { + if(m_dvd.state == DVDSTATE_STILL) + { + state.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime; + state.time_total = m_dvd.iDVDStillTime; + } + } + + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + { + if(((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime() > 0) + { + state.time -= ((CDVDInputStreamTV*)m_pInputStream)->GetStartTime(); + state.time_total = ((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime(); + } + } + } + + if (m_Edl.HasCut()) + { + state.time = m_Edl.RemoveCutTime(llrint(state.time)); + state.time_total = m_Edl.RemoveCutTime(llrint(state.time_total)); + } + + state.player_state = ""; + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + state.time_offset = DVD_MSEC_TO_TIME(state.time) - state.dts; + if(!((CDVDInputStreamNavigator*)m_pInputStream)->GetNavigatorState(state.player_state)) + state.player_state = ""; + } + else + state.time_offset = 0; + + if (m_CurrentAudio.id >= 0 && m_pDemuxer) + { + CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id); + if (pStream && pStream->type == STREAM_AUDIO) + ((CDemuxStreamAudio*)pStream)->GetStreamInfo(state.demux_audio); + } + else + state.demux_audio = ""; + + if (m_CurrentVideo.id >= 0 && m_pDemuxer) + { + CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id); + if (pStream && pStream->type == STREAM_VIDEO) + ((CDemuxStreamVideo*)pStream)->GetStreamInfo(state.demux_video); + } + else + state.demux_video = ""; + + double level, delay, offset; + if(GetCachingTimes(level, delay, offset)) + { + state.cache_delay = max(0.0, delay); + state.cache_level = max(0.0, min(1.0, level)); + state.cache_offset = offset; + } + else + { + state.cache_delay = 0.0; + state.cache_level = min(1.0, GetQueueTime() / 8000.0); + state.cache_offset = GetQueueTime() / state.time_total; + } + + XFILE::SCacheStatus status; + if(m_pInputStream && m_pInputStream->GetCacheStatus(&status)) + { + state.cache_bytes = status.forward; + if(state.time_total) + state.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / state.time_total; + } + else + state.cache_bytes = 0; + + state.timestamp = m_av_clock.GetAbsoluteClock(); + + CSingleLock lock(m_StateSection); + m_State = state; +} + +void COMXPlayer::UpdateApplication(double timeout) +{ + if(m_UpdateApplication != 0 + && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > m_av_clock.GetAbsoluteClock()) + return; + + CDVDInputStream::IChannel* pStream = dynamic_cast(m_pInputStream); + if(pStream) + { + CFileItem item(g_application.CurrentFileItem()); + if(pStream->UpdateItem(item)) + { + g_application.CurrentFileItem() = item; + g_infoManager.SetCurrentItem(item); + //CApplicationMessenger::Get().SetCurrentItem(item); + } + } + m_UpdateApplication = m_av_clock.GetAbsoluteClock(); +} + +bool COMXPlayer::CanRecord() +{ + CSingleLock lock(m_StateSection); + return m_State.canrecord; +} + +bool COMXPlayer::IsRecording() +{ + CSingleLock lock(m_StateSection); + return m_State.recording; +} + +bool COMXPlayer::Record(bool bOnOff) +{ + if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + { + m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff)); + return true; + } + return false; +} + +int COMXPlayer::GetChannels() +{ + if (m_pDemuxer && (m_CurrentAudio.id != -1)) + { + CDemuxStreamAudio* stream = static_cast(m_pDemuxer->GetStream(m_CurrentAudio.id)); + if (stream) + return stream->iChannels; + } + return -1; +} + +CStdString COMXPlayer::GetAudioCodecName() +{ + CStdString retVal; + if (m_pDemuxer && (m_CurrentAudio.id != -1)) + m_pDemuxer->GetStreamCodecName(m_CurrentAudio.id, retVal); + return retVal; +} + +CStdString COMXPlayer::GetVideoCodecName() +{ + CStdString retVal; + if (m_pDemuxer && (m_CurrentVideo.id != -1)) + m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal); + return retVal; +} + +int COMXPlayer::GetPictureWidth() +{ + if (m_pDemuxer && (m_CurrentVideo.id != -1)) + { + CDemuxStreamVideo* stream = static_cast(m_pDemuxer->GetStream(m_CurrentVideo.id)); + if (stream) + return stream->iWidth; + } + return 0; +} + +int COMXPlayer::GetPictureHeight() +{ + if (m_pDemuxer && (m_CurrentVideo.id != -1)) + { + CDemuxStreamVideo* stream = static_cast(m_pDemuxer->GetStream(m_CurrentVideo.id)); + if (stream) + return stream->iHeight; + } + return 0; +} + +bool COMXPlayer::GetStreamDetails(CStreamDetails &details) +{ + if (m_pDemuxer) + { + bool result=CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, details); + if (result && details.GetStreamCount(CStreamDetail::VIDEO) > 0) // this is more correct (dvds in particular) + { + GetVideoAspectRatio(((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect); + ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = GetTotalTime() / 1000; + } + return result; + } + else + return false; +} + +CStdString COMXPlayer::GetPlayingTitle() +{ + return ""; +} + +void COMXPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect) +{ + g_renderManager.GetVideoRect(SrcRect, DestRect); +} + +void COMXPlayer::SetVolume(float fVolume) +{ + m_current_volume = fVolume; + m_change_volume = true; +} + +void COMXPlayer::Update(bool bPauseDrawing) +{ + g_renderManager.Update(bPauseDrawing); +} + +void COMXPlayer::GetVideoAspectRatio(float &fAR) +{ + fAR = g_renderManager.GetAspectRatio(); +} + +#endif diff --git a/xbmc/cores/omxplayer/OMXPlayer.h b/xbmc/cores/omxplayer/OMXPlayer.h new file mode 100644 index 0000000000000..7896e95c6e42a --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayer.h @@ -0,0 +1,458 @@ +#pragma once +/* + * Copyright (C) 2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_CONFIG_H) && !defined(TARGET_WINDOWS) +#include "config.h" +#define DECLARE_UNUSED(a,b) a __attribute__((unused)) b; +#endif + +#include +#include + +#include "FileItem.h" +#include "cores/IPlayer.h" +#include "cores/dvdplayer/IDVDPlayer.h" +#include "dialogs/GUIDialogBusy.h" +#include "threads/Thread.h" +#include "threads/SingleLock.h" + +#include "OMXCore.h" +#include "OMXClock.h" +#include "OMXPlayerAudio.h" +#include "OMXPlayerVideo.h" +#include "DVDPlayerSubtitle.h" + +#include "utils/BitstreamStats.h" + +#include "linux/DllBCM.h" +#include "Edl.h" + +#define MAX_CHAPTERS 64 + +#define DVDPLAYER_AUDIO 1 +#define DVDPLAYER_VIDEO 2 +#define DVDPLAYER_SUBTITLE 3 +#define DVDPLAYER_TELETEXT 4 + +#define DVDSTATE_NORMAL 0x00000001 // normal dvd state +#define DVDSTATE_STILL 0x00000002 // currently displaying a still frame +#define DVDSTATE_WAIT 0x00000003 // waiting for demuxer read error +#define DVDSTATE_SEEK 0x00000004 // we are finishing a seek request + +class COMXPlayer; +class OMXPlayerVideo; +class OMXPlayerAudio; + +class COMXCurrentStream +{ +public: + int id; // demuxerid of current playing stream + int source; + double dts; // last dts from demuxer, used to find disncontinuities + double dur; // last frame expected duration + CDVDStreamInfo hint; // stream hints, used to notice stream changes + void* stream; // pointer or integer, identifying stream playing. if it changes stream changed + bool inited; + bool started; // has the player started + const StreamType type; + const int player; + // stuff to handle starting after seek + double startpts; + + COMXCurrentStream(StreamType t, int i) + : type(t) + , player(i) + { + Clear(); + } + + void Clear() + { + id = -1; + source = STREAM_SOURCE_NONE; + dts = DVD_NOPTS_VALUE; + dur = DVD_NOPTS_VALUE; + hint.Clear(); + stream = NULL; + inited = false; + started = false; + startpts = DVD_NOPTS_VALUE; + } + double dts_end() + { + if(dts == DVD_NOPTS_VALUE) + return DVD_NOPTS_VALUE; + if(dur == DVD_NOPTS_VALUE) + return dts; + return dts + dur; + } +}; + +typedef struct +{ + StreamType type; + int type_index; + std::string filename; + std::string filename2; // for vobsub subtitles, 2 files are necessary (idx/sub) + std::string language; + std::string name; + CDemuxStream::EFlags flags; + int source; + int id; + std::string codec; + int channels; +} OMXSelectionStream; + +typedef std::vector OMXSelectionStreams; + +class COMXSelectionStreams +{ + CCriticalSection m_section; + OMXSelectionStream m_invalid; +public: + COMXSelectionStreams() + { + m_invalid.id = -1; + m_invalid.source = STREAM_SOURCE_NONE; + m_invalid.type = STREAM_NONE; + } + std::vector m_Streams; + + int IndexOf (StreamType type, int source, int id) const; + int IndexOf (StreamType type, COMXPlayer& p) const; + int Count (StreamType type) const { return IndexOf(type, STREAM_SOURCE_NONE, -1) + 1; } + OMXSelectionStream& Get (StreamType type, int index); + bool Get (StreamType type, CDemuxStream::EFlags flag, OMXSelectionStream& out); + + OMXSelectionStreams Get(StreamType type); + template OMXSelectionStreams Get(StreamType type, Compare compare) + { + OMXSelectionStreams streams = Get(type); + std::stable_sort(streams.begin(), streams.end(), compare); + return streams; + } + + void Clear (StreamType type, StreamSource source); + int Source (StreamSource source, std::string filename); + + void Update (OMXSelectionStream& s); + void Update (CDVDInputStream* input, CDVDDemux* demuxer); +}; + + +class COMXPlayer : public IPlayer, public CThread, public IDVDPlayer +{ +public: + + COMXPlayer(IPlayerCallback &callback); + virtual ~COMXPlayer(); + + virtual void RegisterAudioCallback(IAudioCallback* pCallback) { m_player_audio.RegisterAudioCallback(pCallback); }; + virtual void UnRegisterAudioCallback() { m_player_audio.UnRegisterAudioCallback(); }; + + virtual bool IsValidStream(COMXCurrentStream& stream); + virtual bool IsBetterStream(COMXCurrentStream& current, CDemuxStream* stream); + virtual bool ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream); + virtual bool CloseAudioStream(bool bWaitForBuffers); + virtual bool CloseVideoStream(bool bWaitForBuffers); + virtual bool CloseSubtitleStream(bool bKeepOverlays); + virtual bool OpenAudioStream(int iStream, int source); + virtual bool OpenVideoStream(int iStream, int source); + virtual bool OpenSubtitleStream(int iStream, int source); + virtual void OpenDefaultStreams(); + virtual bool OpenDemuxStream(); + virtual bool OpenInputStream(); + virtual bool CheckPlayerInit(COMXCurrentStream& current, unsigned int source); + virtual void UpdateCorrection(DemuxPacket* pkt, double correction); + virtual void UpdateTimestamps(COMXCurrentStream& current, DemuxPacket* pPacket); + virtual void UpdateLimits(double& minimum, double& maximum, double dts); + virtual bool CheckSceneSkip(COMXCurrentStream& current); + virtual void CheckAutoSceneSkip(); + virtual void CheckContinuity(COMXCurrentStream& current, DemuxPacket* pPacket); + virtual void ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket); + virtual void ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket); + virtual void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket); + virtual void ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket); + virtual void SynchronizeDemuxer(unsigned int timeout); + virtual void SynchronizePlayers(unsigned int sources); + virtual void SendPlayerMessage(CDVDMsg* pMsg, unsigned int target); + virtual void HandleMessages(); + + virtual bool OpenFile(const CFileItem &file, const CPlayerOptions &options); + virtual bool QueueNextFile(const CFileItem &file) {return false;} + virtual void OnNothingToQueueNotify() {} + virtual bool CloseFile(); + virtual bool IsPlaying() const; + virtual void SetPlaySpeed(int speed); + int GetPlaySpeed() { return m_playSpeed; } + virtual void Pause(); + virtual bool IsPaused() const; + virtual bool HasVideo() const; + virtual bool HasAudio() const; + virtual bool IsPassthrough() const; + virtual bool CanSeek(); + virtual void Seek(bool bPlus = true, bool bLargeStep = false); + virtual bool SeekScene(bool bPlus = true); + virtual void SeekPercentage(float fPercent = 0.0f); + virtual float GetPercentage(); + virtual float GetCachePercentage(); + + virtual void SetVolume(float fVolume); + virtual void SetDynamicRangeCompression(long drc) {} + virtual void GetAudioInfo(CStdString &strAudioInfo); + virtual void GetVideoInfo(CStdString &strVideoInfo); + virtual void GetGeneralInfo(CStdString &strVideoInfo); + virtual void Update(bool bPauseDrawing); + virtual void GetVideoRect(CRect& SrcRect, CRect& DestRect); + virtual void GetVideoAspectRatio(float &fAR); + virtual void UpdateApplication(double timeout); + virtual bool CanRecord(); + virtual bool IsRecording(); + virtual bool Record(bool bOnOff); + virtual void SetAVDelay(float fValue = 0.0f); + virtual float GetAVDelay(); + + virtual void SetSubTitleDelay(float fValue = 0.0f); + virtual float GetSubTitleDelay(); + virtual int GetSubtitleCount(); + virtual int GetSubtitle(); + virtual void GetSubtitleName(int iStream, CStdString &strStreamName); + virtual void GetSubtitleLanguage(int iStream, CStdString &strStreamLang); + virtual void SetSubtitle(int iStream); + virtual bool GetSubtitleVisible(); + virtual void SetSubtitleVisible(bool bVisible); + virtual bool GetSubtitleExtension(CStdString &strSubtitleExtension) { return false; } + virtual int AddSubtitle(const CStdString& strSubPath); + + virtual int GetAudioStreamCount(); + virtual int GetAudioStream(); + virtual void GetAudioStreamName(int iStream, CStdString &strStreamName); + virtual void SetAudioStream(int iStream); + virtual void GetAudioStreamLanguage(int iStream, CStdString &strLanguage); + + virtual TextCacheStruct_t* GetTeletextCache() {return NULL;}; + virtual void LoadPage(int p, int sp, unsigned char* buffer) {}; + + virtual int GetChapterCount(); + virtual int GetChapter(); + virtual void GetChapterName(CStdString& strChapterName); + virtual int SeekChapter(int iChapter); + + virtual void SeekTime(int64_t iTime = 0); + virtual int64_t GetTotalTimeInMsec(); + virtual int64_t GetTime(); + virtual int64_t GetTotalTime(); + virtual void ToFFRW(int iSpeed = 0); + virtual int GetAudioBitrate(); + virtual int GetVideoBitrate(); + virtual int GetSourceBitrate(); + virtual int GetChannels(); + virtual CStdString GetAudioCodecName(); + virtual CStdString GetVideoCodecName(); + virtual int GetPictureWidth(); + virtual int GetPictureHeight(); + virtual bool GetStreamDetails(CStreamDetails &details); + + virtual bool IsInMenu() const; + virtual bool HasMenu(); + + virtual bool GetCurrentSubtitle(CStdString& strSubtitle); + //returns a state that is needed for resuming from a specific time + virtual CStdString GetPlayerState(); + virtual bool SetPlayerState(CStdString state); + + virtual CStdString GetPlayingTitle(); + + enum ECacheState + { CACHESTATE_DONE = 0 + , CACHESTATE_FULL // player is filling up the demux queue + , CACHESTATE_INIT // player is waiting for first packet of each stream + , CACHESTATE_PLAY // player is waiting for players to not be stalled + , CACHESTATE_FLUSH // temporary state player will choose startup between init or full + }; + + int m_playSpeed; + struct SSpeedState + { + double lastpts; // holds last display pts during ff/rw operations + double lasttime; + } m_SpeedState; + + void HandlePlaySpeed(); + bool GetCachingTimes(double& play_left, double& cache_left, double& file_offset); + bool CheckStartCaching(COMXCurrentStream& current); + void SetCaching(ECacheState state); + double GetQueueTime(); + virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL; } + virtual int GetCacheLevel() const; + + virtual int OnDVDNavResult(void* pData, int iMessage); + virtual bool OnAction(const CAction &action); +protected: + friend class COMXSelectionStreams; + + class OMXStreamLock : public CSingleLock + { + public: + inline OMXStreamLock(COMXPlayer* comxplayer) : CSingleLock(comxplayer->m_critStreamSection) {} + }; + + virtual void OnStartup(); + virtual void OnExit(); + bool WaitForPausedThumbJobs(int timeout_ms); + virtual void Process(); + + CEvent m_ready; + std::string m_filename; // holds the actual filename + CDVDInputStream *m_pInputStream; + CDVDDemux *m_pDemuxer; + CDVDDemux* m_pSubtitleDemuxer; + COMXSelectionStreams m_SelectionStreams; + std::string m_mimetype; + COMXCurrentStream m_CurrentAudio; + COMXCurrentStream m_CurrentVideo; + COMXCurrentStream m_CurrentSubtitle; + COMXCurrentStream m_CurrentTeletext; + + struct SDVDInfo + { + void Clear() + { + state = DVDSTATE_NORMAL; + iSelectedSPUStream = -1; + iSelectedAudioStream = -1; + iDVDStillTime = 0; + iDVDStillStartTime = 0; + } + + int state; // current dvdstate + unsigned int iDVDStillTime; // total time in ticks we should display the still before continuing + unsigned int iDVDStillStartTime; // time in ticks when we started the still + int iSelectedSPUStream; // mpeg stream id, or -1 if disabled + int iSelectedAudioStream; // mpeg stream id, or -1 if disabled + } m_dvd; + + struct SPlayerState + { + SPlayerState() { Clear(); } + void Clear() + { + timestamp = 0; + time = 0; + time_total = 0; + time_offset = 0; + dts = DVD_NOPTS_VALUE; + player_state = ""; + chapter = 0; + chapter_name = ""; + chapter_count = 0; + canrecord = false; + recording = false; + demux_video = ""; + demux_audio = ""; + cache_bytes = 0; + cache_level = 0.0; + cache_delay = 0.0; + cache_offset = 0.0; + } + + double timestamp; // last time of update + double time_offset; // difference between time and pts + + double time; // current playback time + double time_total; // total playback time + double dts; // last known dts + + std::string player_state; // full player state + + int chapter; // current chapter + std::string chapter_name; // name of current chapter + int chapter_count;// number of chapter + + bool canrecord; // can input stream record + bool recording; // are we currently recording + + std::string demux_video; + std::string demux_audio; + + int64_t cache_bytes; // number of bytes current's cached + double cache_level; // current estimated required cache level + double cache_delay; // time until cache is expected to reach estimated level + double cache_offset; // percentage of file ahead of current position + } m_State; + CCriticalSection m_StateSection; + + CEdl m_Edl; + + struct SEdlAutoSkipMarkers { + + void Clear() + { + cut = -1; + commbreak_start = -1; + commbreak_end = -1; + seek_to_start = false; + mute = false; + } + + int cut; // last automatically skipped EDL cut seek position + int commbreak_start; // start time of the last commercial break automatically skipped + int commbreak_end; // end time of the last commercial break automatically skipped + bool seek_to_start; // whether seeking can go back to the start of a previously skipped break + bool mute; // whether EDL mute is on + + } m_EdlAutoSkipMarkers; + + int AddSubtitleFile(const std::string& filename, const std::string& subfilename = "", CDemuxStream::EFlags flags = CDemuxStream::FLAG_NONE); + virtual void UpdatePlayState(double timeout); + + double m_UpdateApplication; + + void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect); + +private: + void FlushBuffers(bool queued, double pts = DVD_NOPTS_VALUE, bool accurate = true); + + CCriticalSection m_critStreamSection; + + bool m_paused; + bool m_bAbortRequest; + CFileItem m_item; + CPlayerOptions m_PlayerOptions; + + std::string m_lastSub; + + double m_offset_pts; + + OMXClock m_av_clock; + OMXPlayerVideo m_player_video; + OMXPlayerAudio m_player_audio; + CDVDPlayerSubtitle m_player_subtitle; + + CDVDMessageQueue m_messenger; + + float m_current_volume; + bool m_change_volume; + bool m_stats; + CDVDOverlayContainer m_overlayContainer; + ECacheState m_caching; +}; diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp new file mode 100644 index 0000000000000..9bbadb7e7f31e --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp @@ -0,0 +1,823 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include "OMXPlayerAudio.h" + +#include +#include +#include + +#include "linux/XMemUtils.h" +#include "utils/BitstreamStats.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" + +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "utils/MathUtils.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "utils/TimeUtils.h" + +#include "OMXPlayer.h" + +#include +#include + +class COMXMsgAudioCodecChange : public CDVDMsg +{ +public: + COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXAudioCodecOMX* codec) + : CDVDMsg(GENERAL_STREAMCHANGE) + , m_codec(codec) + , m_hints(hints) + {} + ~COMXMsgAudioCodecChange() + { + delete m_codec; + } + COMXAudioCodecOMX *m_codec; + CDVDStreamInfo m_hints; +}; + +OMXPlayerAudio::OMXPlayerAudio(OMXClock *av_clock, + CDVDMessageQueue& parent) +: CThread("COMXPlayerAudio") +, m_messageQueue("audio") +, m_messageParent(parent) +{ + m_av_clock = av_clock; + m_pAudioCodec = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + m_started = false; + m_stalled = false; + m_audioClock = 0; + m_buffer_empty = false; + m_nChannels = 0; + m_DecoderOpen = false; + m_freq = CurrentHostFrequency(); + m_hints_current.Clear(); + + m_av_clock->SetMasterClock(false); + + m_messageQueue.SetMaxDataSize(3 * 1024 * 1024); + m_messageQueue.SetMaxTimeSize(8.0); +} + + +OMXPlayerAudio::~OMXPlayerAudio() +{ + CloseStream(false); + + m_DllBcmHost.Unload(); +} + +bool OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints) +{ + if(!m_DllBcmHost.Load()) + return false; + + COMXAudioCodecOMX *codec = new COMXAudioCodecOMX(); + + if(!codec || !codec->Open(hints)) + { + CLog::Log(LOGERROR, "Unsupported audio codec"); + delete codec; codec = NULL; + return false; + } + + if(m_messageQueue.IsInited()) + m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, codec), 0); + else + { + OpenStream(hints, codec); + m_messageQueue.Init(); + CLog::Log(LOGNOTICE, "Creating audio thread"); + Create(); + } + + return true; +} + +void OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec) +{ + SAFE_DELETE(m_pAudioCodec); + + m_hints = hints; + m_pAudioCodec = codec; + + if(m_hints.bitspersample == 0) + m_hints.bitspersample = 16; + + m_speed = DVD_PLAYSPEED_NORMAL; + m_audioClock = 0; + m_error = 0; + m_errorbuff = 0; + m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; + m_prevskipped = false; + m_syncclock = true; + m_hw_decode = false; + m_errortime = CurrentHostCounter(); + m_silence = false; + m_started = false; + m_flush = false; + m_nChannels = 0; + m_synctype = SYNC_DISCON; + m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0; + m_use_passthrough = (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI) ? true : false ; + m_use_hw_decode = g_advancedSettings.m_omxHWAudioDecode; +} + +bool OMXPlayerAudio::CloseStream(bool bWaitForBuffers) +{ + // wait until buffers are empty + if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty(); + + m_messageQueue.Abort(); + + if(IsRunning()) + StopThread(); + + m_messageQueue.End(); + + if (m_pAudioCodec) + { + m_pAudioCodec->Dispose(); + delete m_pAudioCodec; + m_pAudioCodec = NULL; + } + + CloseDecoder(); + + m_speed = DVD_PLAYSPEED_NORMAL; + m_started = false; + + return true; +} + +void OMXPlayerAudio::OnStartup() +{ +} + +void OMXPlayerAudio::OnExit() +{ + CLog::Log(LOGNOTICE, "thread end: OMXPlayerAudio::OnExit()"); +} + + + +void OMXPlayerAudio::HandleSyncError(double duration) +{ + double clock = m_av_clock->GetClock(); + double error = m_audioClock - clock; + int64_t now; + + if( fabs(error) > DVD_MSEC_TO_TIME(100) || m_syncclock ) + { + m_av_clock->Discontinuity(clock+error); + /* + if(m_speed == DVD_PLAYSPEED_NORMAL) + CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f\n", clock, clock+error, error); + */ + + m_errorbuff = 0; + m_errorcount = 0; + m_skipdupcount = 0; + m_error = 0; + m_syncclock = false; + m_errortime = m_av_clock->CurrentHostCounter(); + + return; + } + + if (m_speed != DVD_PLAYSPEED_NORMAL) + { + m_errorbuff = 0; + m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; + m_error = 0; + m_errortime = m_av_clock->CurrentHostCounter(); + return; + } + + //check if measured error for 1 second + now = m_av_clock->CurrentHostCounter(); + if ((now - m_errortime) >= m_freq) + { + m_errortime = now; + m_error = m_errorbuff / m_errorcount; + + m_errorbuff = 0; + m_errorcount = 0; + + if (m_synctype == SYNC_DISCON) + { + double limit, error; + + if (m_av_clock->GetRefreshRate(&limit) > 0) + { + //when the videoreferenceclock is running, the discontinuity limit is one vblank period + limit *= DVD_TIME_BASE; + + //make error a multiple of limit, rounded towards zero, + //so it won't interfere with the sync methods in CXBMCRenderManager::WaitPresentTime + if (m_error > 0.0) + error = limit * floor(m_error / limit); + else + error = limit * ceil(m_error / limit); + } + else + { + limit = DVD_MSEC_TO_TIME(10); + error = m_error; + } + + /* + limit = DVD_MSEC_TO_TIME(10); + error = m_error; + */ + + if (fabs(error) > limit - 0.001) + { + m_av_clock->Discontinuity(clock+error); + /* + if(m_speed == DVD_PLAYSPEED_NORMAL) + CLog::Log(LOGDEBUG, "COMXPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f", clock, clock+error, error); + */ + } + } + /* + else if (m_synctype == SYNC_SKIPDUP && m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) + if (m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) + { + //check how many packets to skip/duplicate + m_skipdupcount = (int)(m_error / duration); + //if less than one frame off, see if it's more than two thirds of a frame, so we can get better in sync + if (m_skipdupcount == 0 && fabs(m_error) > duration / 3 * 2) + m_skipdupcount = (int)(m_error / (duration / 3 * 2)); + + if (m_skipdupcount > 0) + CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Duplicating %i packet(s) of %.2f ms duration", + m_skipdupcount, duration / DVD_TIME_BASE * 1000.0); + else if (m_skipdupcount < 0) + CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Skipping %i packet(s) of %.2f ms duration ", + m_skipdupcount * -1, duration / DVD_TIME_BASE * 1000.0); + } + */ + } +} + +bool OMXPlayerAudio::CodecChange() +{ + unsigned int old_bitrate = m_hints.bitrate; + unsigned int new_bitrate = m_hints_current.bitrate; + + if(m_pAudioCodec) + { + m_hints.channels = m_pAudioCodec->GetChannels(); + m_hints.samplerate = m_pAudioCodec->GetSampleRate(); + } + + /* only check bitrate changes on CODEC_ID_DTS, CODEC_ID_AC3, CODEC_ID_EAC3 */ + if(m_hints.codec != CODEC_ID_DTS && m_hints.codec != CODEC_ID_AC3 && m_hints.codec != CODEC_ID_EAC3) + new_bitrate = old_bitrate = 0; + + if(m_hints_current.codec != m_hints.codec || + m_hints_current.channels != m_hints.channels || + m_hints_current.samplerate != m_hints.samplerate || + m_hints_current.bitspersample != m_hints.bitspersample || + old_bitrate != new_bitrate || + !m_DecoderOpen) + { + m_hints_current = m_hints; + return true; + } + + return false; +} + +bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket) +{ + if(!pkt) + return false; + + /* last decoder reinit went wrong */ + if(!m_pAudioCodec) + return true; + + if(pkt->dts != DVD_NOPTS_VALUE) + m_audioClock = pkt->dts; + + const uint8_t *data_dec = pkt->pData; + int data_len = pkt->iSize; + + if(!OMX_IS_RAW(m_format.m_dataFormat)) + { + while(!m_bStop && data_len > 0) + { + int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len); + if( (len < 0) || (len > data_len) ) + { + m_pAudioCodec->Reset(); + break; + } + + data_dec+= len; + data_len -= len; + + uint8_t *decoded; + int decoded_size = m_pAudioCodec->GetData(&decoded); + + if(decoded_size <=0) + continue; + + int ret = 0; + + m_audioStats.AddSampleBytes(decoded_size); + + if(CodecChange()) + { + CloseDecoder(); + + m_DecoderOpen = OpenDecoder(); + if(!m_DecoderOpen) + return false; + } + + while(!m_bStop) + { + if(m_flush) + { + m_flush = false; + break; + } + + if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize) + { + Sleep(10); + continue; + } + + if(!bDropPacket) + { + // Zero out the frame data if we are supposed to silence the audio + if(m_silence) + memset(decoded, 0x0, decoded_size); + + ret = m_omxAudio.AddPackets(decoded, decoded_size, m_audioClock, m_audioClock); + + if(ret != decoded_size) + { + CLog::Log(LOGERROR, "error ret %d decoded_size %d\n", ret, decoded_size); + } + } + + int n = (m_nChannels * m_hints.bitspersample * m_hints.samplerate)>>3; + if (n > 0) + m_audioClock += ((double)decoded_size * DVD_TIME_BASE) / n; + + if(m_speed == DVD_PLAYSPEED_NORMAL) + HandleSyncError((((double)decoded_size * DVD_TIME_BASE) / n)); + break; + + } + } + } + else + { + if(CodecChange()) + { + CloseDecoder(); + + m_DecoderOpen = OpenDecoder(); + if(!m_DecoderOpen) + return false; + } + + while(!m_bStop) + { + if(m_flush) + { + m_flush = false; + break; + } + + if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize) + { + Sleep(10); + continue; + } + + if(!bDropPacket) + { + if(m_silence) + memset(pkt->pData, 0x0, pkt->iSize); + + m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock); + } + + if(m_speed == DVD_PLAYSPEED_NORMAL) + HandleSyncError(0); + + m_audioStats.AddSampleBytes(pkt->iSize); + + break; + } + } + + if(bDropPacket) + m_stalled = false; + + if(m_omxAudio.GetDelay() < 0.1) + m_stalled = true; + + // signal to our parent that we have initialized + if(m_started == false) + { + m_started = true; + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO)); + } + + if(!bDropPacket && m_speed == DVD_PLAYSPEED_NORMAL) + { + if(GetDelay() < 0.1f && !m_av_clock->OMXAudioBuffer()) + { + clock_gettime(CLOCK_REALTIME, &m_starttime); + m_av_clock->OMXAudioBufferStart(); + } + else if(GetDelay() > (AUDIO_BUFFER_SECONDS * 0.75f) && m_av_clock->OMXAudioBuffer()) + { + m_av_clock->OMXAudioBufferStop(); + } + else if(m_av_clock->OMXAudioBuffer()) + { + clock_gettime(CLOCK_REALTIME, &m_endtime); + if((m_endtime.tv_sec - m_starttime.tv_sec) > 1) + { + m_av_clock->OMXAudioBufferStop(); + } + } + } + + return true; +} + +void OMXPlayerAudio::Process() +{ + m_audioStats.Start(); + + while(!m_bStop) + { + CDVDMsg* pMsg; + int priority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; + int timeout = 1000; + + MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority); + + if (ret == MSGQ_TIMEOUT) + { + Sleep(10); + continue; + } + + if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) + { + Sleep(10); + continue; + } + + if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) + { + DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); + bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); + + if(Decode(pPacket, m_speed > DVD_PLAYSPEED_NORMAL || m_speed < 0 || bPacketDrop)) + { + if (m_stalled && (m_omxAudio.GetDelay() > (AUDIO_BUFFER_SECONDS * 0.75f))) + { + CLog::Log(LOGINFO, "COMXPlayerAudio - Switching to normal playback"); + m_stalled = false; + } + } + } + else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) + { + if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO )) + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE"); + else + m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ + } + else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) + { //player asked us to set internal clock + CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; + + if (pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) + m_audioClock = pMsgGeneralResync->m_timestamp; + + if (pMsgGeneralResync->m_clock) + { + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 1)", m_audioClock); + m_av_clock->Discontinuity(m_audioClock); + //m_av_clock->OMXUpdateClock(m_audioClock); + } + else + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 0)", m_audioClock); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) + { + if (m_pAudioCodec) + m_pAudioCodec->Reset(); + m_started = false; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) + { + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_FLUSH"); + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_omxAudio.Flush(); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + m_syncclock = true; + m_stalled = true; + m_started = false; + + if (m_pAudioCodec) + m_pAudioCodec->Reset(); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) + { + if(m_started) + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO)); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_EOF)) + { + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_EOF"); + WaitCompletion(); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) + { + if (m_speed != DVD_PLAYSPEED_PAUSE) + { + double timeout = static_cast(pMsg)->m_value; + + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_DELAY(%f)", timeout); + + timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed); + timeout += m_av_clock->GetAbsoluteClock(); + + while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout) + Sleep(1); + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) + { + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_SETSPEED"); + m_speed = static_cast(pMsg)->m_value; + if (m_speed != DVD_PLAYSPEED_NORMAL) + { + m_syncclock = true; + } + } + else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE)) + { + m_silence = static_cast(pMsg)->m_value; + if (m_silence) + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 1)", m_audioClock); + else + CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 0)", m_audioClock); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) + { + COMXMsgAudioCodecChange* msg(static_cast(pMsg)); + OpenStream(msg->m_hints, msg->m_codec); + msg->m_codec = NULL; + } + + pMsg->Release(); + } +} + +void OMXPlayerAudio::Flush() +{ + m_flush = true; + m_messageQueue.Flush(); + m_messageQueue.Put( new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1); +} + +void OMXPlayerAudio::WaitForBuffers() +{ + // make sure there are no more packets available + m_messageQueue.WaitUntilEmpty(); + + // make sure almost all has been rendered + // leave 500ms to avound buffer underruns + double delay = GetCacheTime(); + if(delay > 0.5) + Sleep((int)(1000 * (delay - 0.5))); +} + +bool OMXPlayerAudio::Passthrough() const +{ + return m_passthrough; +} + +AEDataFormat OMXPlayerAudio::GetDataFormat(CDVDStreamInfo hints) +{ + AEDataFormat dataFormat = AE_FMT_S16NE; + bool hdmi_passthrough_dts = false; + bool hdmi_passthrough_ac3 = false; + + if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eAC3, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0) + hdmi_passthrough_ac3 = true; + if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eDTS, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0) + hdmi_passthrough_dts = true; + //printf("Audio support AC3=%d, DTS=%d\n", hdmi_passthrough_ac3, hdmi_passthrough_dts); + + m_passthrough = false; + m_hw_decode = false; + + /* check our audio capabilties */ + + /* pathrought is overriding hw decode*/ + if(AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode")) && m_use_passthrough) + { + if(hints.codec == CODEC_ID_AC3 && g_guiSettings.GetBool("audiooutput.ac3passthrough") && hdmi_passthrough_ac3) + { + dataFormat = AE_FMT_AC3; + m_passthrough = true; + } + if(hints.codec == CODEC_ID_DTS && g_guiSettings.GetBool("audiooutput.dtspassthrough") && hdmi_passthrough_dts) + { + dataFormat = AE_FMT_DTS; + m_passthrough = true; + } + } + + /* hw decode */ + if(m_use_hw_decode && !m_passthrough) + { + if(hints.codec == CODEC_ID_AC3 && COMXAudio::CanHWDecode(m_hints.codec)) + { + dataFormat = AE_FMT_AC3; + m_hw_decode = true; + } + if(hints.codec == CODEC_ID_DTS && COMXAudio::CanHWDecode(m_hints.codec)) + { + dataFormat = AE_FMT_DTS; + m_hw_decode = true; + } + } + + /* software path */ + if(!m_passthrough && !m_hw_decode) + { + /* 6 channel have to be mapped to 8 for PCM */ + if(m_nChannels > 4) + m_nChannels = 8; + dataFormat = AE_FMT_S16NE; + } + + return dataFormat; +} + +bool OMXPlayerAudio::OpenDecoder() +{ + bool bAudioRenderOpen = false; + + m_nChannels = m_hints.channels; + m_passthrough = false; + m_hw_decode = false; + + m_omxAudio.SetClock(m_av_clock); + + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_av_clock->HasAudio(false); + + /* setup audi format for audio render */ + m_format.m_sampleRate = m_hints.samplerate; + m_format.m_channelLayout = m_pAudioCodec->GetChannelMap(); + /* GetDataFormat is setting up evrything */ + m_format.m_dataFormat = GetDataFormat(m_hints); + + std::string device = ""; + + if(g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI) + device = "hdmi"; + else + device = "local"; + + bAudioRenderOpen = m_omxAudio.Initialize(m_format, device, m_av_clock, m_hints, m_passthrough, m_hw_decode); + + m_codec_name = ""; + + if(!bAudioRenderOpen) + { + CLog::Log(LOGERROR, "OMXPlayerAudio : Error open audio output"); + m_av_clock->HasAudio(false); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + return false; + } + else + { + CLog::Log(LOGINFO, "Audio codec %s channels %d samplerate %d bitspersample %d\n", + m_codec_name.c_str(), m_nChannels, m_hints.samplerate, m_hints.bitspersample); + } + + m_av_clock->HasAudio(true); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + + return true; +} + +void OMXPlayerAudio::CloseDecoder() +{ + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_omxAudio.Deinitialize(); + m_av_clock->HasAudio(false); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + + m_DecoderOpen = false; +} + +double OMXPlayerAudio::GetDelay() +{ + return m_omxAudio.GetDelay(); +} + +double OMXPlayerAudio::GetCacheTime() +{ + return m_omxAudio.GetCacheTime(); +} + +void OMXPlayerAudio::WaitCompletion() +{ + m_omxAudio.WaitCompletion(); +} + +void OMXPlayerAudio::RegisterAudioCallback(IAudioCallback *pCallback) +{ + m_omxAudio.RegisterAudioCallback(pCallback); +} + +void OMXPlayerAudio::UnRegisterAudioCallback() +{ + m_omxAudio.UnRegisterAudioCallback(); +} + +void OMXPlayerAudio::SetCurrentVolume(float fVolume) +{ + m_omxAudio.SetCurrentVolume(fVolume); +} + +void OMXPlayerAudio::SetSpeed(int speed) +{ + if(m_messageQueue.IsInited()) + m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 ); + else + m_speed = speed; +} + +int OMXPlayerAudio::GetAudioBitrate() +{ + return (int)m_audioStats.GetBitrate(); +} + +std::string OMXPlayerAudio::GetPlayerInfo() +{ + std::ostringstream s; + s << "aq:" << setw(2) << min(99,m_messageQueue.GetLevel() + MathUtils::round_int(100.0/8.0*GetCacheTime())) << "%"; + s << ", Kb/s:" << fixed << setprecision(2) << (double)GetAudioBitrate() / 1024.0; + + return s.str(); +} diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.h b/xbmc/cores/omxplayer/OMXPlayerAudio.h new file mode 100644 index 0000000000000..51ac1c4b45d76 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayerAudio.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef _OMX_PLAYERAUDIO_H_ +#define _OMX_PLAYERAUDIO_H_ + +#include +#include + +#include "utils/StdString.h" + +#include "OMXClock.h" +#include "DVDStreamInfo.h" +#include "OMXAudio.h" +#include "OMXAudioCodecOMX.h" +#include "threads/Thread.h" + +#include "DVDDemuxers/DVDDemux.h" +#include "DVDMessageQueue.h" +#include "utils/BitstreamStats.h" +#include "xbmc/linux/DllBCM.h" + +using namespace std; + +class OMXPlayerAudio : public CThread +{ +protected: + CDVDMessageQueue m_messageQueue; + CDVDMessageQueue &m_messageParent; + + CDVDStreamInfo m_hints_current; + CDVDStreamInfo m_hints; + OMXClock *m_av_clock; + COMXAudio m_omxAudio; + std::string m_codec_name; + bool m_use_passthrough; + bool m_passthrough; + bool m_use_hw_decode; + bool m_hw_decode; + AEAudioFormat m_format; + CAEChannelInfo m_channelLayout; + COMXAudioCodecOMX *m_pAudioCodec; + unsigned int m_speed; + bool m_silence; + double m_audioClock; + double m_error; //last average error + + int64_t m_errortime; //timestamp of last time we measured + int64_t m_freq; + + void HandleSyncError(double duration); + double m_errorbuff; //place to store average errors + int m_errorcount;//number of errors stored + bool m_syncclock; + + double m_integral; //integral correction for resampler + int m_skipdupcount; //counter for skip/duplicate synctype + bool m_prevskipped; + + bool m_stalled; + bool m_started; + + BitstreamStats m_audioStats; + + struct timespec m_starttime, m_endtime; + bool m_buffer_empty; + bool m_flush; + //SYNC_DISCON, SYNC_SKIPDUP, SYNC_RESAMPLE + int m_synctype; + int m_nChannels; + bool m_DecoderOpen; + + DllBcmHost m_DllBcmHost; + + virtual void OnStartup(); + virtual void OnExit(); + virtual void Process(); +private: +public: + OMXPlayerAudio(OMXClock *av_clock, CDVDMessageQueue& parent); + ~OMXPlayerAudio(); + bool OpenStream(CDVDStreamInfo &hints); + void OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec); + void SendMessage(CDVDMsg* pMsg, int priority = 0) { m_messageQueue.Put(pMsg, priority); } + bool AcceptsData() const { return !m_messageQueue.IsFull(); } + bool HasData() const { return m_messageQueue.GetDataSize() > 0; } + bool IsInited() const { return m_messageQueue.IsInited(); } + int GetLevel() const { return m_messageQueue.GetLevel(); } + bool IsStalled() { return m_stalled; } + void WaitForBuffers(); + bool CloseStream(bool bWaitForBuffers); + bool CodecChange(); + bool Decode(DemuxPacket *pkt, bool bDropPacket); + void Flush(); + bool AddPacket(DemuxPacket *pkt); + AEDataFormat GetDataFormat(CDVDStreamInfo hints); + bool Passthrough() const; + bool OpenDecoder(); + void CloseDecoder(); + double GetDelay(); + double GetCacheTime(); + double GetCurrentPTS() { return m_audioClock; }; + void WaitCompletion(); + void RegisterAudioCallback(IAudioCallback* pCallback); + void UnRegisterAudioCallback(); + void SetCurrentVolume(float fVolume); + void SetSpeed(int iSpeed); + int GetAudioBitrate(); + std::string GetPlayerInfo(); +}; +#endif diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp new file mode 100644 index 0000000000000..cb975d308d741 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "OMXPlayerVideo.h" + +#include "linux/XMemUtils.h" +#include "utils/BitstreamStats.h" + +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "DVDCodecs/DVDCodecUtils.h" +#include "windowing/WindowingFactory.h" +#include "DVDOverlayRenderer.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "cores/VideoRenderers/RenderFormats.h" +#include "cores/VideoRenderers/RenderFlags.h" + +#include "OMXPlayer.h" + +class COMXMsgAudioCodecChange : public CDVDMsg +{ +public: + COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXVideo *codec) + : CDVDMsg(GENERAL_STREAMCHANGE) + , m_codec(codec) + , m_hints(hints) + {} + ~COMXMsgAudioCodecChange() + { + delete m_codec; + } + COMXVideo *m_codec; + CDVDStreamInfo m_hints; +}; + +OMXPlayerVideo::OMXPlayerVideo(OMXClock *av_clock, + CDVDOverlayContainer* pOverlayContainer, + CDVDMessageQueue& parent) +: CThread("COMXPlayerVideo") +, m_messageQueue("video") +, m_messageParent(parent) +{ + m_av_clock = av_clock; + m_pOverlayContainer = pOverlayContainer; + m_pTempOverlayPicture = NULL; + m_open = false; + m_stream_id = -1; + m_fFrameRate = 25.0f; + m_flush = false; + m_hdmi_clock_sync = false; + m_iVideoDelay = 0; + m_speed = DVD_PLAYSPEED_NORMAL; + m_stalled = false; + m_codecname = ""; + m_iSubtitleDelay = 0; + m_FlipTimeStamp = 0.0; + m_bRenderSubs = false; + m_width = 0; + m_height = 0; + m_fps = 0.0f; + m_flags = 0; + m_bAllowFullscreen = false; + m_iCurrentPts = DVD_NOPTS_VALUE; + m_fFrameRate = 25.0f; + m_iVideoDelay = 0; + m_droptime = 0.0; + m_dropbase = 0.0; + m_autosync = 1; + m_messageQueue.SetMaxDataSize(10 * 1024 * 1024); + m_messageQueue.SetMaxTimeSize(8.0); + + RESOLUTION res = g_graphicsContext.GetVideoResolution(); + m_video_width = g_settings.m_ResInfo[res].iWidth; + m_video_height = g_settings.m_ResInfo[res].iHeight; + + m_dst_rect.SetRect(0, 0, 0, 0); + +} + +OMXPlayerVideo::~OMXPlayerVideo() +{ + CloseStream(false); +} + +bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints) +{ + /* + if(IsRunning()) + CloseStream(false); + */ + + m_hints = hints; + m_Deinterlace = ( g_settings.m_currentVideoSettings.m_DeinterlaceMode == VS_DEINTERLACEMODE_OFF ) ? false : true; + m_flush = false; + m_hdmi_clock_sync = g_guiSettings.GetBool("videoplayer.adjustrefreshrate"); + m_started = false; + m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0; + m_autosync = 1; + + m_audio_count = m_av_clock->HasAudio(); + + if (!m_DllBcmHost.Load()) + return false; + + if(!OpenDecoder()) + { + return false; + } + + if(m_messageQueue.IsInited()) + m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, NULL), 0); + else + { + if(!OpenStream(hints, NULL)) + return false; + CLog::Log(LOGNOTICE, "Creating video thread"); + m_messageQueue.Init(); + Create(); + } + + /* + if(!OpenStream(hints, NULL)) + return false; + + CLog::Log(LOGNOTICE, "Creating video thread"); + m_messageQueue.Init(); + Create(); + */ + + m_open = true; + + return true; +} + +bool OMXPlayerVideo::OpenStream(CDVDStreamInfo &hints, COMXVideo *codec) +{ + return true; +} + +bool OMXPlayerVideo::CloseStream(bool bWaitForBuffers) +{ + m_flush = true; + + // wait until buffers are empty + if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty(); + + m_messageQueue.Abort(); + + if(IsRunning()) + StopThread(); + + m_messageQueue.End(); + + m_open = false; + m_stream_id = -1; + m_speed = DVD_PLAYSPEED_NORMAL; + m_started = false; + + if (m_pTempOverlayPicture) + { + CDVDCodecUtils::FreePicture(m_pTempOverlayPicture); + m_pTempOverlayPicture = NULL; + } + + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_omxVideo.Close(); + m_av_clock->HasVideo(false); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + + if(m_DllBcmHost.IsLoaded()) + m_DllBcmHost.Unload(); + + return true; +} + +void OMXPlayerVideo::OnStartup() +{ + m_iCurrentPts = DVD_NOPTS_VALUE; + m_FlipTimeStamp = m_av_clock->GetAbsoluteClock(); +} + +void OMXPlayerVideo::OnExit() +{ + CLog::Log(LOGNOTICE, "thread end: video_thread"); +} + +void OMXPlayerVideo::ProcessOverlays(int iGroupId, double pts) +{ + // remove any overlays that are out of time + if (m_started) + m_pOverlayContainer->CleanUp(pts - m_iSubtitleDelay); + + enum EOverlay + { OVERLAY_AUTO // select mode auto + , OVERLAY_GPU // render osd using gpu + , OVERLAY_BUF // render osd on buffer + } render = OVERLAY_AUTO; + + /* + if(m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU) + || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE) + || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA) ) + render = OVERLAY_BUF; + */ + + if(render == OVERLAY_BUF) + { + // rendering spu overlay types directly on video memory costs a lot of processing power. + // thus we allocate a temp picture, copy the original to it (needed because the same picture can be used more than once). + // then do all the rendering on that temp picture and finaly copy it to video memory. + // In almost all cases this is 5 or more times faster!. + + if(m_pTempOverlayPicture && ( m_pTempOverlayPicture->iWidth != m_width + || m_pTempOverlayPicture->iHeight != m_height)) + { + CDVDCodecUtils::FreePicture(m_pTempOverlayPicture); + m_pTempOverlayPicture = NULL; + } + + if(!m_pTempOverlayPicture) + m_pTempOverlayPicture = CDVDCodecUtils::AllocatePicture(m_width, m_height); + if(!m_pTempOverlayPicture) + return; + m_pTempOverlayPicture->format = RENDER_FMT_YUV420P; + } + + if(render == OVERLAY_AUTO) + render = OVERLAY_GPU; + + VecOverlays overlays; + + { + CSingleLock lock(*m_pOverlayContainer); + + VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays(); + VecOverlaysIter it = pVecOverlays->begin(); + + //Check all overlays and render those that should be rendered, based on time and forced + //Both forced and subs should check timeing, pts == 0 in the stillframe case + while (it != pVecOverlays->end()) + { + CDVDOverlay* pOverlay = *it++; + if(!pOverlay->bForced && !m_bRenderSubs) + continue; + + if(pOverlay->iGroupId != iGroupId) + continue; + + double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay; + + if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL)) || pts == 0) + { + if(pOverlay->IsOverlayType(DVDOVERLAY_TYPE_GROUP)) + overlays.insert(overlays.end(), static_cast(pOverlay)->m_overlays.begin() + , static_cast(pOverlay)->m_overlays.end()); + else + overlays.push_back(pOverlay); + + } + } + + for(it = overlays.begin(); it != overlays.end(); ++it) + { + double pts2 = (*it)->bForced ? pts : pts - m_iSubtitleDelay; + + if (render == OVERLAY_GPU) + g_renderManager.AddOverlay(*it, pts2); + + /* + printf("subtitle : DVDOVERLAY_TYPE_SPU %d DVDOVERLAY_TYPE_IMAGE %d DVDOVERLAY_TYPE_SSA %d\n", + m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU), + m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE), + m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA) ); + */ + + if (render == OVERLAY_BUF) + CDVDOverlayRenderer::Render(m_pTempOverlayPicture, *it, pts2); + } + } +} + +void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket) +{ + + if (!g_renderManager.IsConfigured() + || m_video_width != m_width + || m_video_height != m_height + || m_fps != m_fFrameRate) + { + m_width = m_video_width; + m_height = m_video_height; + m_fps = m_fFrameRate; + + unsigned flags = 0; + ERenderFormat format = RENDER_FMT_BYPASS; + + if(m_bAllowFullscreen) + { + flags |= CONF_FLAGS_FULLSCREEN; + m_bAllowFullscreen = false; // only allow on first configure + } + + if(m_flags & CONF_FLAGS_FORMAT_SBS) + { + if(g_Windowing.Support3D(m_video_width, m_video_height, D3DPRESENTFLAG_MODE3DSBS)) + { + CLog::Log(LOGNOTICE, "3DSBS movie found"); + flags |= CONF_FLAGS_FORMAT_SBS; + } + } + + CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: BYPASS", + __FUNCTION__, m_width, m_height, m_fps); + + if(!g_renderManager.Configure(m_video_width, m_video_height, + m_video_width, m_video_height, m_fps, flags, format, 0, + m_hints.orientation)) + { + CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__); + return; + } + + g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack); + } + + if (!g_renderManager.IsStarted()) { + CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); + return; + } + + // calculate the time we need to delay this picture before displaying + double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; + + iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock + iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison + iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame + iFrameDuration = (double)DVD_TIME_BASE / m_fFrameRate; //pPacket->duration; + + // correct sleep times based on speed + if(m_speed) + { + iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; + iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); + iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); + } + else + { + iClockSleep = 0; + iFrameSleep = 0; + } + + // dropping to a very low framerate is not correct (it should not happen at all) + iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); + iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); + + if( m_stalled ) + iSleepTime = iFrameSleep; + else + iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; + + // present the current pts of this frame to user, and include the actual + // presentation delay, to allow him to adjust for it + if( m_stalled ) + m_iCurrentPts = DVD_NOPTS_VALUE; + else + m_iCurrentPts = pts - max(0.0, iSleepTime); + + // timestamp when we think next picture should be displayed based on current duration + m_FlipTimeStamp = iCurrentClock; + m_FlipTimeStamp += max(0.0, iSleepTime); + m_FlipTimeStamp += iFrameDuration; + + if( m_speed < 0 ) + { + if( iClockSleep < -DVD_MSEC_TO_TIME(200)) + return; + } + + if(bDropPacket) + return; + +#if 0 + if( m_speed != DVD_PLAYSPEED_NORMAL) + { + // calculate frame dropping pattern to render at this speed + // we do that by deciding if this or next frame is closest + // to the flip timestamp + double current = fabs(m_dropbase - m_droptime); + double next = fabs(m_dropbase - (m_droptime + iFrameDuration)); + double frametime = (double)DVD_TIME_BASE / m_fFrameRate; + + m_droptime += iFrameDuration; +#ifndef PROFILE + if( next < current /*&& !(pPicture->iFlags & DVP_FLAG_NOSKIP) */) + return /*result | EOS_DROPPED*/; +#endif + + while(!m_bStop && m_dropbase < m_droptime) m_dropbase += frametime; + while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime; + } + else + { + m_droptime = 0.0f; + m_dropbase = 0.0f; + } +#else + m_droptime = 0.0f; + m_dropbase = 0.0f; +#endif + + double pts_media = m_av_clock->OMXMediaTime(); + ProcessOverlays(iGroupId, pts_media); + + while(!CThread::m_bStop && m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) ) + Sleep(1); + + g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, FS_NONE); + + //m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); +} + +void OMXPlayerVideo::Process() +{ + double pts = 0; + double frametime = (double)DVD_TIME_BASE / m_fFrameRate; + bool bRequestDrop = false; + + m_videoStats.Start(); + + while(!m_bStop) + { + CDVDMsg* pMsg; + int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; + int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; + MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority); + + if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) + { + CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); + break; + } + else if (ret == MSGQ_TIMEOUT) + { + // if we only wanted priority messages, this isn't a stall + if( iPriority ) + continue; + + //Okey, start rendering at stream fps now instead, we are likely in a stillframe + if( !m_stalled ) + { + if(m_started) + CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate); + m_stalled = true; + pts += frametime*4; + } + + pts += frametime; + + continue; + } + + if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) + { + if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO)) + { + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); + + } + else + m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ + + pMsg->Release(); + + continue; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) + { + CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; + + if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) + pts = pMsgGeneralResync->m_timestamp; + + double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); + if( delay > frametime ) delay = frametime; + else if( delay < 0 ) delay = 0; + + if(pMsgGeneralResync->m_clock) + { + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts); + m_av_clock->Discontinuity(pts - delay); + //m_av_clock->OMXUpdateClock(pts - delay); + } + else + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts); + + pMsgGeneralResync->Release(); + continue; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) + { + if (m_speed != DVD_PLAYSPEED_PAUSE) + { + double timeout = static_cast(pMsg)->m_value; + + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout); + + timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed); + timeout += m_av_clock->GetAbsoluteClock(); + + while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout) + Sleep(1); + } + } + else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) + { + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESET"); + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_omxVideo.Reset(); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + m_started = false; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (COMXPlayerVideo::Flush()) + { + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_FLUSH"); + m_stalled = true; + m_started = false; + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + m_omxVideo.Reset(); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) + { + m_speed = static_cast(pMsg)->m_value; + } + else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) + { + if(m_started) + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) + { + COMXMsgAudioCodecChange* msg(static_cast(pMsg)); + OpenStream(msg->m_hints, msg->m_codec); + msg->m_codec = NULL; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_EOF) && !m_audio_count) + { + CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_EOF"); + WaitCompletion(); + } + else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) + { + DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); + bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); + + if (m_messageQueue.GetDataSize() == 0 + || m_speed < 0) + { + bRequestDrop = false; + } + + // if player want's us to drop this packet, do so nomatter what + if(bPacketDrop) + bRequestDrop = true; + + m_omxVideo.SetDropState(bRequestDrop); + + while (!m_bStop) + { + if(m_flush) + { + m_flush = false; + break; + } + + if((int)m_omxVideo.GetFreeSpace() < pPacket->iSize) + { + Sleep(10); + continue; + } + + if (m_stalled) + { + CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe left, switching to normal playback"); + m_stalled = false; + } + + // validate picture timing, + // if both dts/pts invalid, use pts calulated from picture.iDuration + // if pts invalid use dts, else use picture.pts as passed + if (pPacket->dts == DVD_NOPTS_VALUE && pPacket->pts == DVD_NOPTS_VALUE) + pPacket->pts = pts; + else if (pPacket->pts == DVD_NOPTS_VALUE) + pPacket->pts = pPacket->dts; + + if(pPacket->pts != DVD_NOPTS_VALUE) + pPacket->pts += m_iVideoDelay; + + if(pPacket->duration == 0) + pPacket->duration = frametime; + + m_omxVideo.Decode(pPacket->pData, pPacket->iSize, pPacket->pts, pPacket->pts); + Output(pPacket->iGroupId, pPacket->pts, bRequestDrop); + + if(m_started == false) + { + m_codecname = m_omxVideo.GetDecoderName(); + m_started = true; + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); + } + + // guess next frame pts. iDuration is always valid + if (m_speed != 0) + pts += pPacket->duration * m_speed / abs(m_speed); + + break; + } + + bRequestDrop = false; + + m_videoStats.AddSampleBytes(pPacket->iSize); + } + pMsg->Release(); + + } +} + +void OMXPlayerVideo::Flush() +{ + m_flush = true; + m_messageQueue.Flush(); + m_messageQueue.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1); +} + +bool OMXPlayerVideo::OpenDecoder() +{ + if(!m_av_clock) + return false; + + if (m_hints.fpsrate && m_hints.fpsscale) + m_fFrameRate = DVD_TIME_BASE / OMXClock::NormalizeFrameduration((double)DVD_TIME_BASE * m_hints.fpsscale / m_hints.fpsrate); + else + m_fFrameRate = 25; + + if( m_fFrameRate > 100 || m_fFrameRate < 5 ) + { + CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder : Invalid framerate %d, using forced 25fps and just trust timestamps\n", (int)m_fFrameRate); + m_fFrameRate = 25; + } + + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + if(!m_omxVideo.Open(m_hints, m_av_clock, m_Deinterlace, m_hdmi_clock_sync)) + { + CLog::Log(LOGERROR, "OMXPlayerAudio : Error open video output"); + m_av_clock->HasVideo(false); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + return false; + } + else + CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder : Video codec %s width %d height %d profile %d fps %f\n", + m_omxVideo.GetDecoderName().c_str() , m_hints.width, m_hints.height, m_hints.profile, m_fFrameRate); + + m_codecname = m_omxVideo.GetDecoderName(); + + // if we are closer to ntsc version of framerate, let gpu know + int iFrameRate = (int)(m_fFrameRate + 0.5f); + bool bNtscFreq = fabs(m_fFrameRate * 1001.0f / 1000.0f - iFrameRate) < fabs(m_fFrameRate - iFrameRate); + char response[80], command[80]; + sprintf(command, "hdmi_ntsc_freqs %d", bNtscFreq); + CLog::Log(LOGINFO, "OMXPlayerVideo::OpenDecoder fps: %f %s\n", m_fFrameRate, command); + m_DllBcmHost.vc_gencmd(response, sizeof response, command); + + if(m_av_clock) + m_av_clock->SetRefreshRate(m_fFrameRate); + + m_av_clock->HasVideo(true); + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + return true; +} + +int OMXPlayerVideo::GetDecoderBufferSize() +{ + return m_omxVideo.GetInputBufferSize(); +} + +int OMXPlayerVideo::GetDecoderFreeSpace() +{ + return m_omxVideo.GetFreeSpace(); +} + +void OMXPlayerVideo::WaitCompletion() +{ + m_omxVideo.WaitCompletion(); +} + +void OMXPlayerVideo::SetSpeed(int speed) +{ + if(m_messageQueue.IsInited()) + m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 ); + else + m_speed = speed; +} + +std::string OMXPlayerVideo::GetPlayerInfo() +{ + std::ostringstream s; + s << "fr:" << fixed << setprecision(3) << m_fFrameRate; + s << ", vq:" << setw(2) << min(99,GetLevel()) << "%"; + s << ", dc:" << m_codecname; + s << ", Mb/s:" << fixed << setprecision(2) << (double)GetVideoBitrate() / (1024.0*1024.0); + + return s.str(); +} + +int OMXPlayerVideo::GetVideoBitrate() +{ + return (int)m_videoStats.GetBitrate(); +} + +double OMXPlayerVideo::GetOutputDelay() +{ + double time = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET); + if( m_fFrameRate ) + time = (time * DVD_TIME_BASE) / m_fFrameRate; + else + time = 0.0; + + if( m_speed != 0 ) + time = time * DVD_PLAYSPEED_NORMAL / abs(m_speed); + + return time; +} + +int OMXPlayerVideo::GetFreeSpace() +{ + return m_omxVideo.GetFreeSpace(); +} + +void OMXPlayerVideo::SetVideoRect(const CRect &SrcRect, const CRect &DestRect) +{ + // check if destination rect or video view mode has changed + if ((m_dst_rect != DestRect) || (m_view_mode != g_settings.m_currentVideoSettings.m_ViewMode)) + { + m_dst_rect = DestRect; + m_view_mode = g_settings.m_currentVideoSettings.m_ViewMode; + } + else + { + return; + } + + // might need to scale up m_dst_rect to display size as video decodes + // to separate video plane that is at display size. + CRect gui, display, dst_rect; + RESOLUTION res = g_graphicsContext.GetVideoResolution(); + gui.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight); + display.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight); + + dst_rect = m_dst_rect; + if (gui != display) + { + float xscale = display.Width() / gui.Width(); + float yscale = display.Height() / gui.Height(); + dst_rect.x1 *= xscale; + dst_rect.x2 *= xscale; + dst_rect.y1 *= yscale; + dst_rect.y2 *= yscale; + } + + if(!(m_flags & CONF_FLAGS_FORMAT_SBS) && !!(m_flags & CONF_FLAGS_FORMAT_TB)) + m_omxVideo.SetVideoRect(SrcRect, m_dst_rect); +} + +void OMXPlayerVideo::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect) +{ + OMXPlayerVideo *player = (OMXPlayerVideo*)ctx; + player->SetVideoRect(SrcRect, DestRect); +} + diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.h b/xbmc/cores/omxplayer/OMXPlayerVideo.h new file mode 100644 index 0000000000000..f4227d05de3ee --- /dev/null +++ b/xbmc/cores/omxplayer/OMXPlayerVideo.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef _OMX_PLAYERVIDEO_H_ +#define _OMX_PLAYERVIDEO_H_ + +#include +#include + +#include "utils/StdString.h" + +#include "OMXClock.h" +#include "DVDStreamInfo.h" +#include "OMXVideo.h" +#include "threads/Thread.h" + +#include "DVDDemuxers/DVDDemux.h" +#include "DVDStreamInfo.h" +#include "DVDCodecs/Video/DVDVideoCodec.h" +#include "DVDOverlayContainer.h" +#include "DVDMessageQueue.h" +#include "utils/BitstreamStats.h" +#include "linux/DllBCM.h" + +using namespace std; + +class OMXPlayerVideo : public CThread +{ +protected: + CDVDMessageQueue m_messageQueue; + int m_stream_id; + bool m_open; + CDVDStreamInfo m_hints; + double m_iCurrentPts; + OMXClock *m_av_clock; + COMXVideo m_omxVideo; + float m_fFrameRate; + bool m_Deinterlace; + bool m_flush; + bool m_hdmi_clock_sync; + double m_iVideoDelay; + int m_speed; + double m_FlipTimeStamp; // time stamp of last flippage. used to play at a forced framerate + int m_audio_count; + bool m_stalled; + bool m_started; + std::string m_codecname; + double m_droptime; + double m_dropbase; + unsigned int m_autosync; + double m_iSubtitleDelay; + bool m_bRenderSubs; + bool m_bAllowFullscreen; + + unsigned int m_width; + unsigned int m_height; + unsigned int m_video_width; + unsigned int m_video_height; + unsigned m_flags; + float m_fps; + + CRect m_dst_rect; + int m_view_mode; + + DllBcmHost m_DllBcmHost; + + CDVDOverlayContainer *m_pOverlayContainer; + CDVDMessageQueue &m_messageParent; + + BitstreamStats m_videoStats; + + DVDVideoPicture* m_pTempOverlayPicture; + + void ProcessOverlays(int iGroupId, double pts); + + virtual void OnStartup(); + virtual void OnExit(); + virtual void Process(); +private: +public: + OMXPlayerVideo(OMXClock *av_clock, CDVDOverlayContainer* pOverlayContainer, CDVDMessageQueue& parent); + ~OMXPlayerVideo(); + bool OpenStream(CDVDStreamInfo &hints); + bool OpenStream(CDVDStreamInfo &hints, COMXVideo *codec); + void SendMessage(CDVDMsg* pMsg, int priority = 0) { m_messageQueue.Put(pMsg, priority); } + bool AcceptsData() const { return !m_messageQueue.IsFull(); } + bool HasData() const { return m_messageQueue.GetDataSize() > 0; } + bool IsInited() const { return m_messageQueue.IsInited(); } + void WaitForBuffers() { m_messageQueue.WaitUntilEmpty(); } + int GetLevel() const { return m_messageQueue.GetLevel(); } + bool IsStalled() { return m_stalled; } + bool CloseStream(bool bWaitForBuffers); + void Output(int iGroupId, double pts, bool bDropPacket); + void Flush(); + bool OpenDecoder(); + int GetDecoderBufferSize(); + int GetDecoderFreeSpace(); + double GetCurrentPTS() { return m_iCurrentPts; }; + double GetFPS() { return m_fFrameRate; }; + void WaitCompletion(); + void SetDelay(double delay) { m_iVideoDelay = delay; } + double GetDelay() { return m_iVideoDelay; } + void SetSpeed(int iSpeed); + std::string GetPlayerInfo(); + int GetVideoBitrate(); + double GetOutputDelay(); + double GetSubtitleDelay() { return m_iSubtitleDelay; } + void SetSubtitleDelay(double delay) { m_iSubtitleDelay = delay; } + void EnableSubtitle(bool bEnable) { m_bRenderSubs = bEnable; } + bool IsSubtitleEnabled() { return m_bRenderSubs; } + void EnableFullscreen(bool bEnable) { m_bAllowFullscreen = bEnable; } + void SetFlags(unsigned flags) { m_flags = flags; }; + int GetFreeSpace(); + void SetVideoRect(const CRect &SrcRect, const CRect &DestRect); + static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect); +}; +#endif diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp new file mode 100644 index 0000000000000..c0ac035d9b175 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXVideo.cpp @@ -0,0 +1,961 @@ +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include "OMXVideo.h" + +#include "utils/log.h" +#include "linux/XMemUtils.h" +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "settings/AdvancedSettings.h" + +#include +#include + +#ifdef CLASSNAME +#undef CLASSNAME +#endif +#define CLASSNAME "COMXVideo" + +#if 0 +// TODO: These are Nvidia Tegra2 dependent, need to dynamiclly find the +// right codec matched to video format. +#define OMX_H264BASE_DECODER "OMX.Nvidia.h264.decode" +// OMX.Nvidia.h264ext.decode segfaults, not sure why. +//#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264ext.decode" +#define OMX_H264MAIN_DECODER "OMX.Nvidia.h264.decode" +#define OMX_H264HIGH_DECODER "OMX.Nvidia.h264ext.decode" +#define OMX_MPEG4_DECODER "OMX.Nvidia.mp4.decode" +#define OMX_MPEG4EXT_DECODER "OMX.Nvidia.mp4ext.decode" +#define OMX_MPEG2V_DECODER "OMX.Nvidia.mpeg2v.decode" +#define OMX_VC1_DECODER "OMX.Nvidia.vc1.decode" +#endif + +#define OMX_VIDEO_DECODER "OMX.broadcom.video_decode" +#define OMX_H264BASE_DECODER OMX_VIDEO_DECODER +#define OMX_H264MAIN_DECODER OMX_VIDEO_DECODER +#define OMX_H264HIGH_DECODER OMX_VIDEO_DECODER +#define OMX_MPEG4_DECODER OMX_VIDEO_DECODER +#define OMX_MSMPEG4V1_DECODER OMX_VIDEO_DECODER +#define OMX_MSMPEG4V2_DECODER OMX_VIDEO_DECODER +#define OMX_MSMPEG4V3_DECODER OMX_VIDEO_DECODER +#define OMX_MPEG4EXT_DECODER OMX_VIDEO_DECODER +#define OMX_MPEG2V_DECODER OMX_VIDEO_DECODER +#define OMX_VC1_DECODER OMX_VIDEO_DECODER +#define OMX_WMV3_DECODER OMX_VIDEO_DECODER +#define OMX_VP8_DECODER OMX_VIDEO_DECODER + +#define MAX_TEXT_LENGTH 1024 + +COMXVideo::COMXVideo() +{ + m_is_open = false; + m_Pause = false; + m_extradata = NULL; + m_extrasize = 0; + m_converter = NULL; + m_video_convert = false; + m_video_codec_name = ""; + m_deinterlace = false; + m_hdmi_clock_sync = false; + m_first_frame = true; +} + +COMXVideo::~COMXVideo() +{ + if (m_is_open) + Close(); +} + +bool COMXVideo::SendDecoderConfig() +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + /* send decoder config */ + if(m_extrasize > 0 && m_extradata != NULL) + { + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return false; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = m_extrasize; + if(omx_buffer->nFilledLen > omx_buffer->nAllocLen) + { + CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__); + return false; + } + + memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen); + memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen); + omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return false; + } + } + return true; +} + +bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, bool hdmi_clock_sync) +{ + if(m_is_open) + Close(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + std::string decoder_name; + + m_video_codec_name = ""; + m_codingType = OMX_VIDEO_CodingUnused; + + m_decoded_width = hints.width; + m_decoded_height = hints.height; + + m_hdmi_clock_sync = hdmi_clock_sync; + + if(!m_decoded_width || !m_decoded_height) + return false; + + m_converter = new CBitstreamConverter(); + m_video_convert = m_converter->Open(hints.codec, (uint8_t *)hints.extradata, hints.extrasize, false); + + if(m_video_convert) + { + if(m_converter->GetExtraData() != NULL && m_converter->GetExtraSize() > 0) + { + m_extrasize = m_converter->GetExtraSize(); + m_extradata = (uint8_t *)malloc(m_extrasize); + memcpy(m_extradata, m_converter->GetExtraData(), m_converter->GetExtraSize()); + } + } + else + { + if(hints.extrasize > 0 && hints.extradata != NULL) + { + m_extrasize = hints.extrasize; + m_extradata = (uint8_t *)malloc(m_extrasize); + memcpy(m_extradata, hints.extradata, hints.extrasize); + } + } + + switch (hints.codec) + { + case CODEC_ID_H264: + { + switch(hints.profile) + { + case FF_PROFILE_H264_BASELINE: + // (role name) video_decoder.avc + // H.264 Baseline profile + decoder_name = OMX_H264BASE_DECODER; + m_codingType = OMX_VIDEO_CodingAVC; + m_video_codec_name = "omx-h264"; + break; + case FF_PROFILE_H264_MAIN: + // (role name) video_decoder.avc + // H.264 Main profile + decoder_name = OMX_H264MAIN_DECODER; + m_codingType = OMX_VIDEO_CodingAVC; + m_video_codec_name = "omx-h264"; + break; + case FF_PROFILE_H264_HIGH: + // (role name) video_decoder.avc + // H.264 Main profile + decoder_name = OMX_H264HIGH_DECODER; + m_codingType = OMX_VIDEO_CodingAVC; + m_video_codec_name = "omx-h264"; + break; + case FF_PROFILE_UNKNOWN: + decoder_name = OMX_H264HIGH_DECODER; + m_codingType = OMX_VIDEO_CodingAVC; + m_video_codec_name = "omx-h264"; + break; + default: + decoder_name = OMX_H264HIGH_DECODER; + m_codingType = OMX_VIDEO_CodingAVC; + m_video_codec_name = "omx-h264"; + break; + } + } + break; + case CODEC_ID_MPEG4: + // (role name) video_decoder.mpeg4 + // MPEG-4, DivX 4/5 and Xvid compatible + decoder_name = OMX_MPEG4_DECODER; + m_codingType = OMX_VIDEO_CodingMPEG4; + m_video_codec_name = "omx-mpeg4"; + break; + case CODEC_ID_MPEG1VIDEO: + case CODEC_ID_MPEG2VIDEO: + // (role name) video_decoder.mpeg2 + // MPEG-2 + decoder_name = OMX_MPEG2V_DECODER; + m_codingType = OMX_VIDEO_CodingMPEG2; + m_video_codec_name = "omx-mpeg2"; + break; + case CODEC_ID_H263: + // (role name) video_decoder.mpeg4 + // MPEG-4, DivX 4/5 and Xvid compatible + decoder_name = OMX_MPEG4_DECODER; + m_codingType = OMX_VIDEO_CodingMPEG4; + m_video_codec_name = "omx-h263"; + break; + case CODEC_ID_VP8: + // (role name) video_decoder.vp8 + // VP8 + decoder_name = OMX_VP8_DECODER; + m_codingType = OMX_VIDEO_CodingVP8; + m_video_codec_name = "omx-vp8"; + break; + case CODEC_ID_VC1: + case CODEC_ID_WMV3: + // (role name) video_decoder.vc1 + // VC-1, WMV9 + decoder_name = OMX_VC1_DECODER; + m_codingType = OMX_VIDEO_CodingWMV; + m_video_codec_name = "omx-vc1"; + break; + default: + return false; + break; + } + + if(m_decoded_width <= 720 && m_decoded_height <=576 && deinterlace) + { + CLog::Log(LOGDEBUG, "COMXVideo::Open : enable deinterlace\n"); + m_deinterlace = true; + } + else + { + m_deinterlace = false; + } + + std::string componentName = ""; + + componentName = decoder_name; + if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamVideoInit)) + return false; + + componentName = "OMX.broadcom.video_render"; + if(!m_omx_render.Initialize((const std::string)componentName, OMX_IndexParamVideoInit)) + return false; + + componentName = "OMX.broadcom.video_scheduler"; + if(!m_omx_sched.Initialize((const std::string)componentName, OMX_IndexParamVideoInit)) + return false; + + if(m_deinterlace) + { + componentName = "OMX.broadcom.image_fx"; + if(!m_omx_image_fx.Initialize((const std::string)componentName, OMX_IndexParamImageInit)) + return false; + } + + OMX_VIDEO_PARAM_PORTFORMATTYPE formatType; + /* + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_decoder.GetInputPort(); + OMX_U32 nIndex = 1; + bool bFound = false; + + omx_err = OMX_ErrorNone; + do + { + formatType.nIndex = nIndex; + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamVideoPortFormat, &formatType); + if(formatType.eCompressionFormat == m_codingType) + { + bFound = true; + break; + } + nIndex++; + } + while(omx_err == OMX_ErrorNone); + + if(!bFound) + { + CLog::Log(LOGINFO, "COMXVideo::Open coding : %s not supported\n", m_video_codec_name.c_str()); + return false; + } + */ + + if(clock == NULL) + return false; + + m_av_clock = clock; + m_omx_clock = m_av_clock->GetOMXClock(); + + if(m_omx_clock->GetComponent() == NULL) + { + m_av_clock = NULL; + m_omx_clock = NULL; + return false; + } + + if(m_deinterlace) + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_image_fx, m_omx_image_fx.GetInputPort()); + m_omx_tunnel_image_fx.Initialize(&m_omx_image_fx, m_omx_image_fx.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort()); + } + else + { + m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort()); + } + m_omx_tunnel_sched.Initialize(&m_omx_sched, m_omx_sched.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); + + m_omx_tunnel_clock.Initialize(m_omx_clock, m_omx_clock->GetInputPort() + 1, &m_omx_sched, m_omx_sched.GetOutputPort() + 1); + + omx_err = m_omx_tunnel_clock.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_clock.Establish\n"); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open m_omx_decoder.SetStateForComponent\n"); + return false; + } + + OMX_INIT_STRUCTURE(formatType); + formatType.nPortIndex = m_omx_decoder.GetInputPort(); + formatType.eCompressionFormat = m_codingType; + + if (hints.fpsscale > 0 && hints.fpsrate > 0) + { + formatType.xFramerate = (long long)(1<<16)*hints.fpsrate / hints.fpsscale; + } + else + { + formatType.xFramerate = 25 * (1<<16); + } + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamVideoPortFormat, &formatType); + if(omx_err != OMX_ErrorNone) + return false; + + OMX_PARAM_PORTDEFINITIONTYPE portParam; + OMX_INIT_STRUCTURE(portParam); + portParam.nPortIndex = m_omx_decoder.GetInputPort(); + + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &portParam); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + portParam.nPortIndex = m_omx_decoder.GetInputPort(); + portParam.nBufferCountActual = VIDEO_BUFFERS; + + portParam.format.video.nFrameWidth = m_decoded_width; + portParam.format.video.nFrameHeight = m_decoded_height; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err); + return false; + } + + OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE concanParam; + OMX_INIT_STRUCTURE(concanParam); + if(g_advancedSettings.m_omxDecodeStartWithValidFrame) + concanParam.bStartWithValidFrame = OMX_TRUE; + else + concanParam.bStartWithValidFrame = OMX_FALSE; + + omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmVideoDecodeErrorConcealment, &concanParam); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamBrcmVideoDecodeErrorConcealment omx_err(0x%08x)\n", omx_err); + return false; + } + + if(m_hdmi_clock_sync) + { + OMX_CONFIG_LATENCYTARGETTYPE latencyTarget; + OMX_INIT_STRUCTURE(latencyTarget); + latencyTarget.nPortIndex = m_omx_render.GetInputPort(); + latencyTarget.bEnabled = OMX_TRUE; + latencyTarget.nFilter = 2; + latencyTarget.nTarget = 4000; + latencyTarget.nShift = 3; + latencyTarget.nSpeedFactor = -135; + latencyTarget.nInterFactor = 500; + latencyTarget.nAdjCap = 20; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexConfigLatencyTarget error (0%08x)\n", omx_err); + return false; + } + } + + // Alloc buffers for the omx intput port. + omx_err = m_omx_decoder.AllocInputBuffers(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open AllocOMXInputBuffers error (0%08x)\n", omx_err); + return false; + } + + omx_err = m_omx_tunnel_decoder.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_decoder.Establish\n"); + return false; + } + + omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_decoder.SetStateForComponent\n"); + return false; + } + + if(m_deinterlace) + { + OMX_CONFIG_IMAGEFILTERPARAMSTYPE image_filter; + OMX_INIT_STRUCTURE(image_filter); + + image_filter.nPortIndex = m_omx_image_fx.GetOutputPort(); + image_filter.nNumParams = 1; + image_filter.nParams[0] = 3; + image_filter.eImageFilter = OMX_ImageFilterDeInterlaceAdvanced; + + omx_err = m_omx_image_fx.SetConfig(OMX_IndexConfigCommonImageFilterParameters, &image_filter); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexConfigCommonImageFilterParameters omx_err(0x%08x)\n", omx_err); + return false; + } + + omx_err = m_omx_tunnel_image_fx.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_image_fx.Establish\n"); + return false; + } + + omx_err = m_omx_image_fx.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_image_fx.SetStateForComponent\n"); + return false; + } + + m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false); + m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false); + } + + omx_err = m_omx_tunnel_sched.Establish(false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_sched.Establish\n"); + return false; + } + + omx_err = m_omx_sched.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_sched.SetStateForComponent\n"); + return false; + } + + omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_render.SetStateForComponent\n"); + return false; + } + + if(!SendDecoderConfig()) + return false; + + m_is_open = true; + m_drop_state = false; + + OMX_CONFIG_DISPLAYREGIONTYPE configDisplay; + OMX_INIT_STRUCTURE(configDisplay); + configDisplay.nPortIndex = m_omx_render.GetInputPort(); + + configDisplay.set = OMX_DISPLAY_SET_TRANSFORM; + + switch(hints.orientation) + { + case 90: + configDisplay.transform = OMX_DISPLAY_ROT90; + break; + case 180: + configDisplay.transform = OMX_DISPLAY_ROT180; + break; + case 270: + configDisplay.transform = OMX_DISPLAY_ROT270; + break; + default: + configDisplay.transform = OMX_DISPLAY_ROT0; + break; + } + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGWARNING, "COMXVideo::Open could not set orientation : %d\n", hints.orientation); + } + + /* + configDisplay.set = OMX_DISPLAY_SET_LAYER; + configDisplay.layer = 2; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_DEST_RECT; + configDisplay.dest_rect.x_offset = 100; + configDisplay.dest_rect.y_offset = 100; + configDisplay.dest_rect.width = 640; + configDisplay.dest_rect.height = 480; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_TRANSFORM; + configDisplay.transform = OMX_DISPLAY_ROT180; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_FULLSCREEN; + configDisplay.fullscreen = OMX_FALSE; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_MODE; + configDisplay.mode = OMX_DISPLAY_MODE_FILL; //OMX_DISPLAY_MODE_LETTERBOX; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_LAYER; + configDisplay.layer = 1; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + configDisplay.set = OMX_DISPLAY_SET_ALPHA; + configDisplay.alpha = OMX_FALSE; + + omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + if(omx_err != OMX_ErrorNone) + return false; + + */ + + CLog::Log(LOGDEBUG, + "%s::%s - decoder_component(0x%p), input_port(0x%x), output_port(0x%x) deinterlace %d hdmiclocksync %d\n", + CLASSNAME, __func__, m_omx_decoder.GetComponent(), m_omx_decoder.GetInputPort(), m_omx_decoder.GetOutputPort(), + m_deinterlace, m_hdmi_clock_sync); + + m_av_clock->OMXStateExecute(false); + + m_first_frame = true; + return true; +} + +void COMXVideo::Close() +{ + if(!m_is_open) + return; + + /* + if(m_av_clock) + { + m_av_clock->Lock(); + m_av_clock->OMXStop(false); + } + */ + + m_omx_tunnel_decoder.Flush(); + if(m_deinterlace) + m_omx_tunnel_image_fx.Flush(); + m_omx_tunnel_clock.Flush(); + m_omx_tunnel_sched.Flush(); + + m_omx_tunnel_clock.Deestablish(); + m_omx_tunnel_decoder.Deestablish(); + if(m_deinterlace) + m_omx_tunnel_image_fx.Deestablish(); + m_omx_tunnel_sched.Deestablish(); + + m_omx_decoder.FlushInput(); + + m_omx_sched.Deinitialize(); + if(m_deinterlace) + m_omx_image_fx.Deinitialize(); + m_omx_decoder.Deinitialize(); + m_omx_render.Deinitialize(); + + /* + if(m_av_clock) + { + m_av_clock->OMXReset(false); + m_av_clock->UnLock(); + } + */ + + m_is_open = false; + + if(m_extradata) + free(m_extradata); + m_extradata = NULL; + m_extrasize = 0; + + if(m_converter) + delete m_converter; + m_converter = NULL; + m_video_convert = false; + m_video_codec_name = ""; + m_deinterlace = false; + m_first_frame = true; +} + +void COMXVideo::SetDropState(bool bDrop) +{ + m_drop_state = bDrop; +} + +unsigned int COMXVideo::GetFreeSpace() +{ + return m_omx_decoder.GetInputBufferSpace(); +} + +unsigned int COMXVideo::GetSize() +{ + return m_omx_decoder.GetInputBufferSize(); +} + +int COMXVideo::Decode(uint8_t *pData, int iSize, double dts, double pts) +{ + OMX_ERRORTYPE omx_err; + + if( m_drop_state ) + return true; + + if (pData || iSize > 0) + { + unsigned int demuxer_bytes = (unsigned int)iSize; + uint8_t *demuxer_content = pData; + + if(m_video_convert) + { + m_converter->Convert(pData, iSize); + demuxer_bytes = m_converter->GetConvertSize(); + demuxer_content = m_converter->GetConvertBuffer(); + if(!demuxer_bytes && demuxer_bytes < 1) + { + return false; + } + } + + while(demuxer_bytes) + { + // 500ms timeout + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(500); + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "OMXVideo::Decode timeout\n"); + return false; + } + + /* + CLog::Log(DEBUG, "COMXVideo::Video VDec : pts %lld omx_buffer 0x%08x buffer 0x%08x number %d\n", + pts, omx_buffer, omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate); + if(pts == DVD_NOPTS_VALUE) + { + CLog::Log(LOGDEBUG, "VDec : pts %f omx_buffer 0x%08x buffer 0x%08x number %d\n", + (float)pts / AV_TIME_BASE, (int)omx_buffer, (int)omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate); + } + */ + + omx_buffer->nFlags = 0; + omx_buffer->nOffset = 0; + + uint64_t val = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts; + + if(m_av_clock->VideoStart()) + { + omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME; + CLog::Log(LOGDEBUG, "VDec : setStartTime %f\n", (float)val / DVD_TIME_BASE); + m_av_clock->VideoStart(false); + } + else + { + if(pts == DVD_NOPTS_VALUE) + omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + } + + omx_buffer->nTimeStamp = ToOMXTime(val); + + omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes; + memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen); + + demuxer_bytes -= omx_buffer->nFilledLen; + demuxer_content += omx_buffer->nFilledLen; + + if(demuxer_bytes == 0) + omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + int nRetry = 0; + while(true) + { + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err == OMX_ErrorNone) + { + break; + } + else + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + nRetry++; + } + if(nRetry == 5) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() finaly failed\n", CLASSNAME, __func__); + return false; + } + } + + if(m_first_frame && m_deinterlace) + { + OMX_PARAM_PORTDEFINITIONTYPE port_image; + OMX_INIT_STRUCTURE(port_image); + port_image.nPortIndex = m_omx_decoder.GetOutputPort(); + + omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_image); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 1 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err); + + /* we assume when the sizes equal we have the first decoded frame */ + if(port_image.format.video.nFrameWidth == m_decoded_width && port_image.format.video.nFrameHeight == m_decoded_height) + { + m_first_frame = false; + + omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged); + if(omx_err == OMX_ErrorStreamCorrupt) + { + CLog::Log(LOGERROR, "%s::%s - image not unsupported\n", CLASSNAME, __func__); + return false; + } + + m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false); + m_omx_sched.DisablePort(m_omx_sched.GetInputPort(), false); + + if(m_deinterlace) + { + m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false); + m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false); + + port_image.nPortIndex = m_omx_image_fx.GetInputPort(); + omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 2 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err); + + port_image.nPortIndex = m_omx_image_fx.GetOutputPort(); + omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 3 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err); + } + + m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false); + + if(m_deinterlace) + { + m_omx_image_fx.EnablePort(m_omx_image_fx.GetOutputPort(), false); + m_omx_image_fx.EnablePort(m_omx_image_fx.GetInputPort(), false); + } + + m_omx_sched.EnablePort(m_omx_sched.GetInputPort(), false); + } + } + } + + return true; + + } + + return false; +} + +void COMXVideo::Reset(void) +{ + if(!m_is_open) + return; + + m_omx_decoder.FlushInput(); + m_omx_tunnel_decoder.Flush(); + + /* + OMX_ERRORTYPE omx_err; + OMX_CONFIG_BOOLEANTYPE configBool; + OMX_INIT_STRUCTURE(configBool); + configBool.bEnabled = OMX_TRUE; + + omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigRefreshCodec, &configBool); + if (omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "%s::%s - error reopen codec omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err); + + SendDecoderConfig(); + + m_first_frame = true; + */ +} + +/////////////////////////////////////////////////////////////////////////////////////////// +bool COMXVideo::Pause() +{ + if(m_omx_render.GetComponent() == NULL) + return false; + + if(m_Pause) return true; + m_Pause = true; + + m_omx_sched.SetStateForComponent(OMX_StatePause); + m_omx_render.SetStateForComponent(OMX_StatePause); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +bool COMXVideo::Resume() +{ + if(m_omx_render.GetComponent() == NULL) + return false; + + if(!m_Pause) return true; + m_Pause = false; + + m_omx_sched.SetStateForComponent(OMX_StateExecuting); + m_omx_render.SetStateForComponent(OMX_StateExecuting); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect) +{ + if(!m_is_open) + return; + + OMX_CONFIG_DISPLAYREGIONTYPE configDisplay; + OMX_INIT_STRUCTURE(configDisplay); + configDisplay.nPortIndex = m_omx_render.GetInputPort(); + + configDisplay.set = OMX_DISPLAY_SET_FULLSCREEN; + configDisplay.fullscreen = OMX_FALSE; + + m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + + configDisplay.set = OMX_DISPLAY_SET_DEST_RECT; + configDisplay.dest_rect.x_offset = DestRect.x1; + configDisplay.dest_rect.y_offset = DestRect.y1; + configDisplay.dest_rect.width = DestRect.Width(); + configDisplay.dest_rect.height = DestRect.Height(); + + m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay); + + CLog::Log(LOGDEBUG, "dest_rect.x_offset %d dest_rect.y_offset %d dest_rect.width %d dest_rect.height %d\n", + configDisplay.dest_rect.x_offset, configDisplay.dest_rect.y_offset, + configDisplay.dest_rect.width, configDisplay.dest_rect.height); +} + +int COMXVideo::GetInputBufferSize() +{ + return m_omx_decoder.GetInputBufferSize(); +} + +void COMXVideo::WaitCompletion() +{ + if(!m_is_open) + return; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(); + struct timespec starttime, endtime; + + if(omx_buffer == NULL) + { + CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err); + return; + } + + omx_buffer->nOffset = 0; + omx_buffer->nFilledLen = 0; + omx_buffer->nTimeStamp = ToOMXTime(0LL); + + omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN; + + omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err); + return; + } + + clock_gettime(CLOCK_REALTIME, &starttime); + + while(true) + { + if(m_omx_render.IsEOS()) + break; + clock_gettime(CLOCK_REALTIME, &endtime); + if((endtime.tv_sec - starttime.tv_sec) > 5) + { + CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__); + break; + } + Sleep(50); + } + + return; +} diff --git a/xbmc/cores/omxplayer/OMXVideo.h b/xbmc/cores/omxplayer/OMXVideo.h new file mode 100644 index 0000000000000..7bfca9f8ef1dc --- /dev/null +++ b/xbmc/cores/omxplayer/OMXVideo.h @@ -0,0 +1,98 @@ +#pragma once +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_OMXLIB) + +#include "OMXCore.h" +#include "DVDStreamInfo.h" + +#include + +#include "BitstreamConverter.h" + +#include "OMXClock.h" + +#include "guilib/Geometry.h" +#include "DVDDemuxers/DVDDemux.h" +#include + +#define VIDEO_BUFFERS 60 + +#define CLASSNAME "COMXVideo" + +class COMXVideo +{ +public: + COMXVideo(); + ~COMXVideo(); + + // Required overrides + bool SendDecoderConfig(); + bool Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace = false, bool hdmi_clock_sync = false); + void Close(void); + unsigned int GetFreeSpace(); + unsigned int GetSize(); + int Decode(uint8_t *pData, int iSize, double dts, double pts); + void Reset(void); + void SetDropState(bool bDrop); + bool Pause(); + bool Resume(); + std::string GetDecoderName() { return m_video_codec_name; }; + void SetVideoRect(const CRect& SrcRect, const CRect& DestRect); + int GetInputBufferSize(); + void WaitCompletion(); +protected: + // Video format + bool m_drop_state; + unsigned int m_decoded_width; + unsigned int m_decoded_height; + + OMX_VIDEO_CODINGTYPE m_codingType; + + COMXCoreComponent m_omx_decoder; + COMXCoreComponent m_omx_render; + COMXCoreComponent m_omx_sched; + COMXCoreComponent m_omx_image_fx; + COMXCoreComponent *m_omx_clock; + OMXClock *m_av_clock; + + COMXCoreTunel m_omx_tunnel_decoder; + COMXCoreTunel m_omx_tunnel_clock; + COMXCoreTunel m_omx_tunnel_sched; + COMXCoreTunel m_omx_tunnel_image_fx; + bool m_is_open; + + bool m_Pause; + + uint8_t *m_extradata; + int m_extrasize; + + CBitstreamConverter *m_converter; + bool m_video_convert; + std::string m_video_codec_name; + + bool m_deinterlace; + bool m_hdmi_clock_sync; + bool m_first_frame; +}; + +#endif diff --git a/xbmc/cores/omxplayer/OMXVideoCodec.h b/xbmc/cores/omxplayer/OMXVideoCodec.h new file mode 100644 index 0000000000000..6990f31df2b34 --- /dev/null +++ b/xbmc/cores/omxplayer/OMXVideoCodec.h @@ -0,0 +1,240 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#include + +// when modifying these structures, make sure you update all codecs accordingly +#define FRAME_TYPE_UNDEF 0 +#define FRAME_TYPE_I 1 +#define FRAME_TYPE_P 2 +#define FRAME_TYPE_B 3 +#define FRAME_TYPE_D 4 + +namespace DXVA { class CSurfaceContext; } +namespace VAAPI { struct CHolder; } +class CVDPAU; +class COMXCore; +class COMXCoreVideo; +struct OMXCoreVideoBuffer; +#ifdef HAVE_VIDEOTOOLBOXDECODER + class COMXVideoCodecVideoToolBox; + struct __CVBuffer; +#endif + +// should be entirely filled by all codecs +struct DVDVideoPicture +{ + double pts; // timestamp in seconds, used in the CDVDPlayer class to keep track of pts + double dts; + + union + { + struct { + BYTE* data[4]; // [4] = alpha channel, currently not used + int iLineSize[4]; // [4] = alpha channel, currently not used + }; + struct { + DXVA::CSurfaceContext* context; + }; + struct { + CVDPAU* vdpau; + }; + struct { + VAAPI::CHolder* vaapi; + }; + + struct { + COMXCore *openMax; + OMXCoreVideoBuffer *openMaxBuffer; + }; +#ifdef HAVE_VIDEOTOOLBOXDECODER + struct { + COMXVideoCodecVideoToolBox *vtb; + struct __CVBuffer *cvBufferRef; + }; +#endif + }; + + unsigned int iFlags; + + double iRepeatPicture; + double iDuration; + unsigned int iFrameType : 4; // see defines above // 1->I, 2->P, 3->B, 0->Undef + unsigned int color_matrix : 4; + unsigned int color_range : 1; // 1 indicate if we have a full range of color + unsigned int chroma_position; + unsigned int color_primaries; + unsigned int color_transfer; + unsigned int extended_format; + int iGroupId; + + int8_t* qscale_table; // Quantization parameters, primarily used by filters + int qscale_stride; + int qscale_type; + + unsigned int iWidth; + unsigned int iHeight; + unsigned int iDisplayWidth; // width of the picture without black bars + unsigned int iDisplayHeight; // height of the picture without black bars + + enum EFormat { + FMT_YUV420P = 0, + FMT_VDPAU, + FMT_NV12, + FMT_UYVY, + FMT_YUY2, + FMT_DXVA, + FMT_VAAPI, + FMT_OMXEGL, + FMT_CVBREF, + } format; +}; + +struct DVDVideoUserData +{ + BYTE* data; + int size; +}; + +#define DVP_FLAG_TOP_FIELD_FIRST 0x00000001 +#define DVP_FLAG_REPEAT_TOP_FIELD 0x00000002 //Set to indicate that the top field should be repeated +#define DVP_FLAG_ALLOCATED 0x00000004 //Set to indicate that this has allocated data +#define DVP_FLAG_INTERLACED 0x00000008 //Set to indicate that this frame is interlaced + +#define DVP_FLAG_NOSKIP 0x00000010 // indicate this picture should never be dropped +#define DVP_FLAG_DROPPED 0x00000020 // indicate that this picture has been dropped in decoder stage, will have no data + +// DVP_FLAG 0x00000100 - 0x00000f00 is in use by libmpeg2! + +#define DVP_QSCALE_UNKNOWN 0 +#define DVP_QSCALE_MPEG1 1 +#define DVP_QSCALE_MPEG2 2 +#define DVP_QSCALE_H264 3 + +class COMXStreamInfo; +class CDVDCodecOption; +class CDVDCodecOptions; + +// VC_ messages, messages can be combined +#define VC_ERROR 0x00000001 // an error occured, no other messages will be returned +#define VC_BUFFER 0x00000002 // the decoder needs more data +#define VC_PICTURE 0x00000004 // the decoder got a picture, call Decode(NULL, 0) again to parse the rest of the data +#define VC_USERDATA 0x00000008 // the decoder found some userdata, call Decode(NULL, 0) again to parse the rest of the data +#define VC_FLUSHED 0x00000010 // the decoder lost it's state, we need to restart decoding again +class COMXVideoCodec +{ +public: + + COMXVideoCodec() {} + virtual ~COMXVideoCodec() {} + + /* + * Open the decoder, returns true on success + */ + virtual bool Open(COMXStreamInfo &hints, CDVDCodecOptions &options) = 0; + + /* + * Dispose, Free all resources + */ + virtual void Dispose() = 0; + + /* + * returns one or a combination of VC_ messages + * pData and iSize can be NULL, this means we should flush the rest of the data. + */ + virtual int Decode(BYTE* pData, int iSize, double dts, double pts) = 0; + + /* + * Reset the decoder. + * Should be the same as calling Dispose and Open after each other + */ + virtual void Reset() = 0; + + /* + * returns true if successfull + * the data is valid until the next Decode call + */ + virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture) = 0; + + + /* + * returns true if successfull + * the data is cleared to zero + */ + virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture) + { + memset(pDvdVideoPicture, 0, sizeof(DVDVideoPicture)); + return true; + } + + /* + * returns true if successfull + * the data is valid until the next Decode call + * userdata can be anything, for now we use it for closed captioning + */ + virtual bool GetUserData(DVDVideoUserData* pDvdVideoUserData) + { + pDvdVideoUserData->data = NULL; + pDvdVideoUserData->size = 0; + return false; + } + + /* + * will be called by video player indicating if a frame will eventually be dropped + * codec can then skip actually decoding the data, just consume the data set picture headers + */ + virtual void SetDropState(bool bDrop) = 0; + + + enum EFilterFlags { + FILTER_NONE = 0x0, + FILTER_DEINTERLACE_YADIF = 0x1, /* use first deinterlace mode */ + FILTER_DEINTERLACE_ANY = 0xf, /* use any deinterlace mode */ + FILTER_DEINTERLACE_FLAGGED = 0x10, /* only deinterlace flagged frames */ + FILTER_DEINTERLACE_HALFED = 0x20, /* do half rate deinterlacing */ + }; + + /* + * set the type of filters that should be applied at decoding stage if possible + */ + virtual unsigned int SetFilters(unsigned int filters) { return 0u; } + + /* + * + * should return codecs name + */ + virtual const char* GetName() = 0; + + /* + * + * How many packets should player remember, so codec + * can recover should something cause it to flush + * outside of players control + */ + virtual unsigned GetConvergeCount() + { + return 0; + } +}; diff --git a/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml new file mode 100644 index 0000000000000..bea4c5a2c86ab --- /dev/null +++ b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml @@ -0,0 +1,10 @@ + + + + diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h index 486c09f0e1ca9..c14a4c7e4c522 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h +++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h @@ -28,6 +28,9 @@ #if defined(HAS_AMLPLAYER) #include "cores/amlplayer/AMLPlayer.h" #endif +#if defined(HAS_OMXPLAYER) +#include "cores/omxplayer/OMXPlayer.h" +#endif #include "cores/ExternalPlayer/ExternalPlayer.h" #include "utils/log.h" @@ -74,11 +77,26 @@ friend class CPlayerCoreFactory; switch(m_eCore) { case EPC_MPLAYER: + // TODO: this hack needs removal until we have a better player selection +#if defined(HAS_OMXPLAYER) + case EPC_DVDPLAYER: + pPlayer = new COMXPlayer(callback); + CLog::Log(LOGINFO, "Created player %s for core %d / OMXPlayer forced as DVDPlayer", "OMXPlayer", m_eCore); + break; + case EPC_PAPLAYER: + pPlayer = new COMXPlayer(callback); + CLog::Log(LOGINFO, "Created player %s for core %d / OMXPlayer forced as PAPLayer", "OMXPlayer", m_eCore); + break; +#else case EPC_DVDPLAYER: pPlayer = new CDVDPlayer(callback); break; case EPC_PAPLAYER: pPlayer = new PAPlayer(callback); break; +#endif case EPC_EXTPLAYER: pPlayer = new CExternalPlayer(callback); break; #if defined(HAS_AMLPLAYER) case EPC_AMLPLAYER: pPlayer = new CAMLPlayer(callback); break; +#endif +#if defined(HAS_OMXPLAYER) + case EPC_OMXPLAYER: pPlayer = new COMXPlayer(callback); break; #endif default: return NULL; } diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp index 9a4506bbf9251..96f29738233e3 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp +++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp @@ -287,6 +287,13 @@ bool CPlayerCoreFactory::LoadConfiguration(TiXmlElement* pConfig, bool clear) s_vecCoreConfigs.push_back(amlplayer); #endif +#if defined(HAS_OMXPLAYER) + CPlayerCoreConfig* omxplayer = new CPlayerCoreConfig("OMXPlayer", EPC_OMXPLAYER, NULL); + omxplayer->m_bPlaysAudio = true; + omxplayer->m_bPlaysVideo = true; + s_vecCoreConfigs.push_back(omxplayer); +#endif + for(std::vector::iterator it = s_vecCoreSelectionRules.begin(); it != s_vecCoreSelectionRules.end(); it++) delete *it; s_vecCoreSelectionRules.clear(); diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.h b/xbmc/cores/playercorefactory/PlayerCoreFactory.h index 69070e0908d76..0043ec6a5b995 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreFactory.h +++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.h @@ -37,10 +37,13 @@ enum EPLAYERCORES EPC_DVDPLAYER, EPC_MPLAYER, EPC_PAPLAYER, - EPC_EXTPLAYER, #if defined(HAS_AMLPLAYER) - EPC_AMLPLAYER + EPC_AMLPLAYER, #endif +#if defined(HAS_OMXPLAYER) + EPC_OMXPLAYER, +#endif + EPC_EXTPLAYER }; typedef unsigned int PLAYERCOREID; @@ -52,6 +55,9 @@ const PLAYERCOREID PCID_PAPLAYER = EPC_PAPLAYER; #if defined(HAS_AMLPLAYER) const PLAYERCOREID PCID_AMLPLAYER = EPC_AMLPLAYER; #endif +#if defined(HAS_OMXPLAYER) +const PLAYERCOREID PCID_OMXPLAYER = EPC_OMXPLAYER; +#endif class CPlayerCoreFactory { diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp index cc0ad48bd65bf..e2b72fdf51f01 100644 --- a/xbmc/guilib/GUIFontTTFGL.cpp +++ b/xbmc/guilib/GUIFontTTFGL.cpp @@ -79,7 +79,7 @@ void CGUIFontTTFGL::Begin() } // Turn Blending On - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); glEnable(GL_BLEND); #ifdef HAS_GL glEnable(GL_TEXTURE_2D); diff --git a/xbmc/guilib/GUITexture.cpp b/xbmc/guilib/GUITexture.cpp index c025fc4525724..d62225006963b 100644 --- a/xbmc/guilib/GUITexture.cpp +++ b/xbmc/guilib/GUITexture.cpp @@ -321,6 +321,9 @@ bool CGUITextureBase::AllocResources() return false; m_texture = texture; + + m_info.orientation = m_texture.m_orientation; + changed = true; } else diff --git a/xbmc/guilib/GUITextureGLES.cpp b/xbmc/guilib/GUITextureGLES.cpp index a4118d603926b..c3a29a8327b0a 100644 --- a/xbmc/guilib/GUITextureGLES.cpp +++ b/xbmc/guilib/GUITextureGLES.cpp @@ -107,7 +107,7 @@ void CGUITextureGLES::Begin(color_t color) if ( hasAlpha ) { - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); glEnable( GL_BLEND ); } else diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp index bddfa9abb168f..931d8fafe92e1 100644 --- a/xbmc/guilib/GraphicContext.cpp +++ b/xbmc/guilib/GraphicContext.cpp @@ -417,6 +417,22 @@ void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan) overscan.right = 1920; overscan.bottom = 1080; break; + case RES_HDTV_720pSBS: + overscan.right = 640; + overscan.bottom = 720; + break; + case RES_HDTV_720pTB: + overscan.right = 1280; + overscan.bottom = 360; + break; + case RES_HDTV_1080pSBS: + overscan.right = 960; + overscan.bottom = 1080; + break; + case RES_HDTV_1080pTB: + overscan.right = 1920; + overscan.bottom = 540; + break; case RES_HDTV_720p: overscan.right = 1280; overscan.bottom = 720; @@ -458,6 +474,38 @@ void CGraphicContext::ResetScreenParameters(RESOLUTION res) g_settings.m_ResInfo[res].fPixelRatio = 1.0f; g_settings.m_ResInfo[res].strMode ="1080i 16:9"; break; + case RES_HDTV_720pSBS: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 720); + g_settings.m_ResInfo[res].iWidth = 640; + g_settings.m_ResInfo[res].iHeight = 720; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN | D3DPRESENTFLAG_MODE3DSBS; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode = "720pSBS 16:9"; + break; + case RES_HDTV_720pTB: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 360); + g_settings.m_ResInfo[res].iWidth = 1280; + g_settings.m_ResInfo[res].iHeight = 360; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN | D3DPRESENTFLAG_MODE3DTB; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode = "720pTB 16:9"; + break; + case RES_HDTV_1080pSBS: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 1080); + g_settings.m_ResInfo[res].iWidth = 960; + g_settings.m_ResInfo[res].iHeight = 1080; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN | D3DPRESENTFLAG_MODE3DSBS; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode = "1080pSBS 16:9"; + break; + case RES_HDTV_1080pTB: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 540); + g_settings.m_ResInfo[res].iWidth = 1920; + g_settings.m_ResInfo[res].iHeight = 540; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN | D3DPRESENTFLAG_MODE3DTB; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode = "1080pTB 16:9"; + break; case RES_HDTV_720p: g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 720); g_settings.m_ResInfo[res].iWidth = 1280; diff --git a/xbmc/guilib/Resolution.h b/xbmc/guilib/Resolution.h index dc8fbc4062933..95bf9ab4d5db0 100644 --- a/xbmc/guilib/Resolution.h +++ b/xbmc/guilib/Resolution.h @@ -34,19 +34,23 @@ typedef int DisplayMode; enum RESOLUTION { RES_INVALID = -1, RES_HDTV_1080i = 0, - RES_HDTV_720p = 1, - RES_HDTV_480p_4x3 = 2, - RES_HDTV_480p_16x9 = 3, - RES_NTSC_4x3 = 4, - RES_NTSC_16x9 = 5, - RES_PAL_4x3 = 6, - RES_PAL_16x9 = 7, - RES_PAL60_4x3 = 8, - RES_PAL60_16x9 = 9, - RES_AUTORES = 10, - RES_WINDOW = 11, - RES_DESKTOP = 12, // Desktop resolution for primary screen - RES_CUSTOM = 12 + 1, // Desktop resolution for screen #2 + RES_HDTV_720pSBS = 1, + RES_HDTV_720pTB = 2, + RES_HDTV_1080pSBS = 3, + RES_HDTV_1080pTB = 4, + RES_HDTV_720p = 5, + RES_HDTV_480p_4x3 = 6, + RES_HDTV_480p_16x9 = 7, + RES_NTSC_4x3 = 8, + RES_NTSC_16x9 = 9, + RES_PAL_4x3 = 10, + RES_PAL_16x9 = 11, + RES_PAL60_4x3 = 12, + RES_PAL60_16x9 = 13, + RES_AUTORES = 14, + RES_WINDOW = 15, + RES_DESKTOP = 16, // Desktop resolution for primary screen + RES_CUSTOM = 17 + 1, // Desktop resolution for screen #2 // ... // 12 + N - 1 // Desktop resolution for screen N // 12 + N // First additional resolution, in a N screen configuration. diff --git a/xbmc/guilib/Texture.cpp b/xbmc/guilib/Texture.cpp index 4c44c6ab56588..f8557a5b3078a 100644 --- a/xbmc/guilib/Texture.cpp +++ b/xbmc/guilib/Texture.cpp @@ -36,6 +36,11 @@ #include "URL.h" #include "filesystem/AndroidAppFile.h" #endif + +#ifdef TARGET_RASPBERRY_PI +#include "xbmc/cores/omxplayer/OMXImage.h" +#endif + /************************************************************************/ /* */ /************************************************************************/ @@ -230,6 +235,56 @@ CBaseTexture *CBaseTexture::LoadFromFileInMemory(unsigned char *buffer, size_t b bool CBaseTexture::LoadFromFile(const CStdString& texturePath, unsigned int maxWidth, unsigned int maxHeight, bool autoRotate, unsigned int *originalWidth, unsigned int *originalHeight) { +#ifdef TARGET_RASPBERRY_PI + if (URIUtils::GetExtension(texturePath).Equals(".jpg") || + URIUtils::GetExtension(texturePath).Equals(".tbn") + /*|| URIUtils::GetExtension(texturePath).Equals(".png")*/) + { + COMXImage omx_image; + + if(omx_image.ReadFile(texturePath)) + { + // TODO: we only decode as half width and height. this is a workaround for the PI memory limitation + if(omx_image.Decode(omx_image.GetWidth() / 2, omx_image.GetHeight() / 2)) + { + Allocate(omx_image.GetDecodedWidth(), omx_image.GetDecodedHeight(), XB_FMT_A8R8G8B8); + + if(!m_pixels) + { + CLog::Log(LOGERROR, "Texture manager (OMX) out of memory"); + omx_image.Close(); + return false; + } + + if (originalWidth) + *originalWidth = omx_image.GetOriginalWidth(); + if (originalHeight) + *originalHeight = omx_image.GetOriginalHeight(); + + m_hasAlpha = omx_image.IsAlpha(); + + if (autoRotate && omx_image.GetOrientation()) + m_orientation = omx_image.GetOrientation() - 1; + + if(omx_image.GetDecodedData()) + { + int size = ( ( GetPitch() * GetRows() ) > omx_image.GetDecodedSize() ) ? + omx_image.GetDecodedSize() : ( GetPitch() * GetRows() ); + + memcpy(m_pixels, (unsigned char *)omx_image.GetDecodedData(), size); + } + + omx_image.Close(); + + return true; + } + else + { + omx_image.Close(); + } + } + } +#endif if (URIUtils::GetExtension(texturePath).Equals(".dds")) { // special case for DDS images CDDSImage image; diff --git a/xbmc/guilib/gui3d.h b/xbmc/guilib/gui3d.h index adbee960d0e58..5cbf6ea47faf7 100644 --- a/xbmc/guilib/gui3d.h +++ b/xbmc/guilib/gui3d.h @@ -32,9 +32,11 @@ #define GAMMA_RAMP_FLAG D3DSGR_CALIBRATE -#define D3DPRESENTFLAG_INTERLACED 1 -#define D3DPRESENTFLAG_WIDESCREEN 2 -#define D3DPRESENTFLAG_PROGRESSIVE 4 +#define D3DPRESENTFLAG_INTERLACED 1 +#define D3DPRESENTFLAG_WIDESCREEN 2 +#define D3DPRESENTFLAG_PROGRESSIVE 4 +#define D3DPRESENTFLAG_MODE3DSBS 8 +#define D3DPRESENTFLAG_MODE3DTB 16 #define D3DFMT_LIN_A8R8G8B8 D3DFMT_A8R8G8B8 #define D3DFMT_LIN_X8R8G8B8 D3DFMT_X8R8G8B8 diff --git a/xbmc/input/linux/LinuxInputDevices.h b/xbmc/input/linux/LinuxInputDevices.h index 445b79c6c488d..fa6f3e06d0b88 100644 --- a/xbmc/input/linux/LinuxInputDevices.h +++ b/xbmc/input/linux/LinuxInputDevices.h @@ -44,6 +44,7 @@ class CLinuxInputDevice XBMC_Event ReadEvent(); private: + void SetupKeyboardAutoRepeat(int fd); XBMCKey TranslateKey(unsigned short code); bool KeyEvent(const struct input_event& levt, XBMC_Event& devt); bool RelEvent(const struct input_event& levt, XBMC_Event& devt); diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp index 40cfd0120cc48..3e4c94c5406fa 100644 --- a/xbmc/interfaces/Builtins.cpp +++ b/xbmc/interfaces/Builtins.cpp @@ -459,6 +459,10 @@ int CBuiltins::Execute(const CStdString& execString) else if (parameter.Equals("ntsc")) res = RES_NTSC_4x3; else if (parameter.Equals("ntsc16x9")) res = RES_NTSC_16x9; else if (parameter.Equals("720p")) res = RES_HDTV_720p; + else if (parameter.Equals("720pSBS")) res = RES_HDTV_720pSBS; + else if (parameter.Equals("720pTB")) res = RES_HDTV_720pTB; + else if (parameter.Equals("1080pSBS")) res = RES_HDTV_1080pSBS; + else if (parameter.Equals("1080pTB")) res = RES_HDTV_1080pTB; else if (parameter.Equals("1080i")) res = RES_HDTV_1080i; if (g_graphicsContext.IsValidResolution(res)) { diff --git a/xbmc/linux/DllBCM.h b/xbmc/linux/DllBCM.h new file mode 100644 index 0000000000000..5cc20a78570ed --- /dev/null +++ b/xbmc/linux/DllBCM.h @@ -0,0 +1,227 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(TARGET_RASPBERRY_PI) + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +extern "C" { +#include +} + +#include "DynamicDll.h" +#include "utils/log.h" + +#define USE_EXTERNAL_LIBBCM_HOST 1 + +//////////////////////////////////////////////////////////////////////////////////////////// + +class DllBcmHostInterface +{ +public: + virtual ~DllBcmHostInterface() {} + + virtual void bcm_host_init() = 0; + virtual void bcm_host_deinit() = 0; + virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) = 0; + virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0; + virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0; + + virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes, + uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) = 0; + virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) = 0; + virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) = 0; + virtual int vc_tv_show_info(uint32_t show) = 0; + virtual int vc_gencmd(char *response, int maxlen, const char *string) = 0; + virtual void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) = 0; + virtual void vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback) = 0; + virtual void vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) = 0; + //virtual void vc_cec_unregister_callback(CECSERVICE_CALLBACK_T callback) = 0; + virtual DISPMANX_DISPLAY_HANDLE_T vc_dispmanx_display_open( uint32_t device ) = 0; + virtual DISPMANX_UPDATE_HANDLE_T vc_dispmanx_update_start( int32_t priority ) = 0; + virtual DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ) = 0; + virtual int vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) = 0; + virtual int vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) = 0; + virtual int vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) = 0; + virtual int vc_dispmanx_display_get_info( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_MODEINFO_T * pinfo ) = 0; + virtual int vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ) = 0; + virtual int vc_tv_hdmi_audio_supported(uint32_t audio_format, uint32_t num_channels, + EDID_AudioSampleRate fs, uint32_t bitrate) = 0; +}; + +#if defined(USE_EXTERNAL_LIBBCM_HOST) +class DllBcmHost : public DllDynamic, DllBcmHostInterface +{ +public: + virtual void bcm_host_init() + { return ::bcm_host_init(); }; + virtual void bcm_host_deinit() + { return ::bcm_host_deinit(); }; + virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) + { return ::graphics_get_display_size(display_number, width, height); }; + virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) + { return ::vc_tv_hdmi_power_on_best(width, height, frame_rate, scan_mode, match_flags); }; + virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) + { return ::vc_tv_hdmi_power_on_best_3d(width, height, frame_rate, scan_mode, match_flags); }; + virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes, + uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) + { return ::vc_tv_hdmi_get_supported_modes(group, supported_modes, max_supported_modes, preferred_group, preferred_mode); }; + virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) + { return ::vc_tv_hdmi_power_on_explicit(mode, group, code); }; + virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) + { return ::vc_tv_get_state(tvstate); }; + virtual int vc_tv_show_info(uint32_t show) + { return ::vc_tv_show_info(show); }; + virtual int vc_gencmd(char *response, int maxlen, const char *string) + { return ::vc_gencmd(response, maxlen, string); }; + virtual void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) + { ::vc_tv_register_callback(callback, callback_data); }; + virtual void vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback) + { ::vc_tv_unregister_callback(callback); }; + virtual void vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) + { ::vc_cec_register_callback(callback, callback_data); }; + //virtual void vc_cec_unregister_callback(CECSERVICE_CALLBACK_T callback) + // { ::vc_cec_unregister_callback(callback); }; + virtual DISPMANX_DISPLAY_HANDLE_T vc_dispmanx_display_open( uint32_t device ) + { return ::vc_dispmanx_display_open(device); }; + virtual DISPMANX_UPDATE_HANDLE_T vc_dispmanx_update_start( int32_t priority ) + { return ::vc_dispmanx_update_start(priority); }; + virtual DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ) + { return ::vc_dispmanx_element_add(update, display, layer, dest_rect, src, src_rect, protection, alpha, clamp, transform); }; + virtual int vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) + { return ::vc_dispmanx_update_submit_sync(update); }; + virtual int vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) + { return ::vc_dispmanx_element_remove(update, element); }; + virtual int vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) + { return ::vc_dispmanx_display_close(display); }; + virtual int vc_dispmanx_display_get_info( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_MODEINFO_T *pinfo ) + { return ::vc_dispmanx_display_get_info(display, pinfo); }; + virtual int vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ) + { return ::vc_dispmanx_display_set_background(update, display, red, green, blue); }; + virtual int vc_tv_hdmi_audio_supported(uint32_t audio_format, uint32_t num_channels, + EDID_AudioSampleRate fs, uint32_t bitrate) + { return ::vc_tv_hdmi_audio_supported(audio_format, num_channels, fs, bitrate); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllBcm: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllBcmHost : public DllDynamic, DllBcmHostInterface +{ + DECLARE_DLL_WRAPPER(DllBcmHost, "/opt/vc/lib/libbcm_host.so") + + DEFINE_METHOD0(void, bcm_host_init) + DEFINE_METHOD0(void, bcm_host_deinit) + DEFINE_METHOD3(int32_t, graphics_get_display_size, (const uint16_t p1, uint32_t *p2, uint32_t *p3)) + DEFINE_METHOD5(int, vc_tv_hdmi_power_on_best, (uint32_t p1, uint32_t p2, uint32_t p3, + HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5)) + DEFINE_METHOD5(int, vc_tv_hdmi_power_on_best_3d, (uint32_t p1, uint32_t p2, uint32_t p3, + HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5)) + DEFINE_METHOD5(int, vc_tv_hdmi_get_supported_modes, (HDMI_RES_GROUP_T p1, TV_SUPPORTED_MODE_T *p2, + uint32_t p3, HDMI_RES_GROUP_T *p4, uint32_t *p5)) + DEFINE_METHOD3(int, vc_tv_hdmi_power_on_explicit, (HDMI_MODE_T p1, HDMI_RES_GROUP_T p2, uint32_t p3)) + DEFINE_METHOD1(int, vc_tv_get_state, (TV_GET_STATE_RESP_T *p1)) + DEFINE_METHOD1(int, vc_tv_show_info, (uint32_t p1)) + DEFINE_METHOD3(int, vc_gencmd, (char *p1, int p2, const char *p3)) + + DEFINE_METHOD2(void, vc_tv_register_callback, (TVSERVICE_CALLBACK_T p1, void *p2)) + DEFINE_METHOD1(void, vc_tv_unregister_callback, (TVSERVICE_CALLBACK_T p1)) + + DEFINE_METHOD2(void, vc_cec_register_callback, (CECSERVICE_CALLBACK_T p1, void *p2)) + //DEFINE_METHOD1(void, vc_cec_unregister_callback, (CECSERVICE_CALLBACK_T p1)) + DEFINE_METHOD1(DISPMANX_DISPLAY_HANDLE_T, vc_dispmanx_display_open, (uint32_t p1 )) + DEFINE_METHOD1(DISPMANX_UPDATE_HANDLE_T, vc_dispmanx_update_start, (int32_t p1 )) + DEFINE_METHOD10(DISPMANX_ELEMENT_HANDLE_T, vc_dispmanx_element_add, (DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_DISPLAY_HANDLE_T p2, + int32_t p3, const VC_RECT_T *p4, DISPMANX_RESOURCE_HANDLE_T p5, + const VC_RECT_T *p6, DISPMANX_PROTECTION_T p7, + VC_DISPMANX_ALPHA_T *p8, + DISPMANX_CLAMP_T *p9, DISPMANX_TRANSFORM_T p10 )) + DEFINE_METHOD1(int, vc_dispmanx_update_submit_sync, (DISPMANX_UPDATE_HANDLE_T p1)) + DEFINE_METHOD2(int, vc_dispmanx_element_remove, (DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_ELEMENT_HANDLE_T p2)) + DEFINE_METHOD1(int, vc_dispmanx_display_close, (DISPMANX_DISPLAY_HANDLE_T p1)) + DEFINE_METHOD2(int, vc_dispmanx_display_get_info, (DISPMANX_DISPLAY_HANDLE_T p1, DISPMANX_MODEINFO_T *p2)) + DEFINE_METHOD5(int, vc_dispmanx_display_set_background, ( DISPMANX_UPDATE_HANDLE_T p1, DISPMANX_DISPLAY_HANDLE_T p2, + uint8_t p3, uint8_t p4, uint8_t p5 )) + DEFINE_METHOD4(int, vc_tv_hdmi_audio_supported, (uint32_t p1, uint32_t p2, EDID_AudioSampleRate p3, uint32_t p4)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(bcm_host_init) + RESOLVE_METHOD(bcm_host_deinit) + RESOLVE_METHOD(graphics_get_display_size) + RESOLVE_METHOD(vc_tv_hdmi_power_on_best) + RESOLVE_METHOD(vc_tv_hdmi_power_on_best_3d) + RESOLVE_METHOD(vc_tv_hdmi_get_supported_modes) + RESOLVE_METHOD(vc_tv_hdmi_power_on_explicit) + RESOLVE_METHOD(vc_tv_get_state) + RESOLVE_METHOD(vc_tv_show_info) + RESOLVE_METHOD(vc_gencmd) + RESOLVE_METHOD(vc_tv_register_callback) + RESOLVE_METHOD(vc_tv_unregister_callback) + RESOLVE_METHOD(vc_cec_register_callback) + //RESOLVE_METHOD(vc_cec_unregister_callback) + RESOLVE_METHOD(vc_dispmanx_display_open) + RESOLVE_METHOD(vc_dispmanx_update_start) + RESOLVE_METHOD(vc_dispmanx_element_add) + RESOLVE_METHOD(vc_dispmanx_update_submit_sync) + RESOLVE_METHOD(vc_dispmanx_element_remove) + RESOLVE_METHOD(vc_dispmanx_display_close) + RESOLVE_METHOD(vc_dispmanx_display_get_info) + RESOLVE_METHOD(vc_dispmanx_display_set_background) + RESOLVE_METHOD(vc_tv_hdmi_audio_supported) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +#endif diff --git a/xbmc/linux/DllOMX.h b/xbmc/linux/DllOMX.h new file mode 100644 index 0000000000000..a4b1ed6fbbbad --- /dev/null +++ b/xbmc/linux/DllOMX.h @@ -0,0 +1,122 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_OMXLIB) + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#include "DynamicDll.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////// + +class DllOMXInterface +{ +public: + virtual ~DllOMXInterface() {} + + virtual OMX_ERRORTYPE OMX_Init(void) = 0; + virtual OMX_ERRORTYPE OMX_Deinit(void) = 0; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) = 0; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) = 0; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) = 0; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) = 0; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) = 0; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) = 0; + +}; + +#if (defined USE_EXTERNAL_OMX) +class DllOMX : public DllDynamic, DllOMXInterface +{ +public: + virtual OMX_ERRORTYPE OMX_Init(void) + { return ::OMX_Init(); }; + virtual OMX_ERRORTYPE OMX_Deinit(void) + { return ::OMX_Deinit(); }; + virtual OMX_ERRORTYPE OMX_GetHandle(OMX_HANDLETYPE *pHandle, OMX_STRING cComponentName, OMX_PTR pAppData, OMX_CALLBACKTYPE *pCallBacks) + { return ::OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks); }; + virtual OMX_ERRORTYPE OMX_FreeHandle(OMX_HANDLETYPE hComponent) + { return ::OMX_FreeHandle(hComponent); }; + virtual OMX_ERRORTYPE OMX_GetComponentsOfRole(OMX_STRING role, OMX_U32 *pNumComps, OMX_U8 **compNames) + { return ::OMX_GetComponentsOfRole(role, pNumComps, compNames); }; + virtual OMX_ERRORTYPE OMX_GetRolesOfComponent(OMX_STRING compName, OMX_U32 *pNumRoles, OMX_U8 **roles) + { return ::OMX_GetRolesOfComponent(compName, pNumRoles, roles); }; + virtual OMX_ERRORTYPE OMX_ComponentNameEnum(OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) + { return ::OMX_ComponentNameEnum(cComponentName, nNameLength, nIndex); }; + virtual OMX_ERRORTYPE OMX_SetupTunnel(OMX_HANDLETYPE hOutput, OMX_U32 nPortOutput, OMX_HANDLETYPE hInput, OMX_U32 nPortInput) + { return ::OMX_SetupTunnel(hOutput, nPortOutput, hInput, nPortInput); }; + virtual bool ResolveExports() + { return true; } + virtual bool Load() + { + CLog::Log(LOGDEBUG, "DllOMX: Using omx system library"); + return true; + } + virtual void Unload() {} +}; +#else +class DllOMX : public DllDynamic, DllOMXInterface +{ + DECLARE_DLL_WRAPPER(DllOMX, "libopenmaxil.so") + + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Init) + DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Deinit) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_GetHandle, (OMX_HANDLETYPE *p1, OMX_STRING p2, OMX_PTR p3, OMX_CALLBACKTYPE *p4)) + DEFINE_METHOD1(OMX_ERRORTYPE, OMX_FreeHandle, (OMX_HANDLETYPE p1)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetComponentsOfRole, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_GetRolesOfComponent, (OMX_STRING p1, OMX_U32 *p2, OMX_U8 **p3)) + DEFINE_METHOD3(OMX_ERRORTYPE, OMX_ComponentNameEnum, (OMX_STRING p1, OMX_U32 p2, OMX_U32 p3)) + DEFINE_METHOD4(OMX_ERRORTYPE, OMX_SetupTunnel, (OMX_HANDLETYPE p1, OMX_U32 p2, OMX_HANDLETYPE p3, OMX_U32 p4)); + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(OMX_Init) + RESOLVE_METHOD(OMX_Deinit) + RESOLVE_METHOD(OMX_GetHandle) + RESOLVE_METHOD(OMX_FreeHandle) + RESOLVE_METHOD(OMX_GetComponentsOfRole) + RESOLVE_METHOD(OMX_GetRolesOfComponent) + RESOLVE_METHOD(OMX_ComponentNameEnum) + RESOLVE_METHOD(OMX_SetupTunnel) + END_METHOD_RESOLVE() + +public: + virtual bool Load() + { + return DllDynamic::Load(); + } +}; +#endif + +#endif diff --git a/xbmc/linux/Makefile.in b/xbmc/linux/Makefile.in index 412357a74c517..1e5e6b8cb0219 100644 --- a/xbmc/linux/Makefile.in +++ b/xbmc/linux/Makefile.in @@ -15,6 +15,9 @@ SRCS=ConvUtils.cpp \ XLCDproc.cpp \ XMemUtils.cpp \ XTimeUtils.cpp \ + RBP.cpp \ + OMXClock.cpp \ + OMXCore.cpp \ ifeq (@USE_ANDROID@,1) SRCS+=getdelim.c diff --git a/xbmc/linux/OMXClock.cpp b/xbmc/linux/OMXClock.cpp new file mode 100644 index 0000000000000..b8edf774bdd2a --- /dev/null +++ b/xbmc/linux/OMXClock.cpp @@ -0,0 +1,1068 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#if defined(HAVE_OMXLIB) + +#include "video/VideoReferenceClock.h" +#include "settings/GUISettings.h" + +#include "OMXClock.h" +#include "utils/MathUtils.h" + +int64_t OMXClock::m_systemOffset; +int64_t OMXClock::m_systemFrequency; +bool OMXClock::m_ismasterclock; + +OMXClock::OMXClock() +{ + m_has_video = false; + m_has_audio = false; + m_video_start = false; + m_audio_start = false; + m_pause = false; + + m_systemOffset = 0; + m_systemFrequency = 0; + + CheckSystemClock(); + + m_systemUsed = m_systemFrequency; + m_pauseClock = 0; + m_bReset = true; + m_iDisc = 0; + m_maxspeedadjust = 0.0; + m_speedadjust = false; + m_ismasterclock = true; + m_ClockOffset = 0; + m_fps = 25.0f; + m_omx_speed = DVD_PLAYSPEED_NORMAL; + m_audio_buffer = false; + + pthread_mutex_init(&m_lock, NULL); +} + +OMXClock::~OMXClock() +{ + OMXDeinitialize(); + pthread_mutex_destroy(&m_lock); +} + +void OMXClock::Lock() +{ + pthread_mutex_lock(&m_lock); +} + +void OMXClock::UnLock() +{ + pthread_mutex_unlock(&m_lock); +} + +double OMXClock::GetAbsoluteClock(bool interpolated /*= true*/) +{ + Lock(); + CheckSystemClock(); + + int64_t current; + current = g_VideoReferenceClock.GetTime(interpolated); + + UnLock(); + return SystemToAbsolute(current); +} + +double OMXClock::WaitAbsoluteClock(double target) +{ + Lock(); + CheckSystemClock(); + + int64_t systemtarget, freq, offset; + freq = m_systemFrequency; + offset = m_systemOffset; + UnLock(); + + systemtarget = (int64_t)(target / DVD_TIME_BASE * (double)freq); + systemtarget += offset; + systemtarget = g_VideoReferenceClock.Wait(systemtarget); + systemtarget -= offset; + return (double)systemtarget / freq * DVD_TIME_BASE; +} + +// Returns the current absolute clock in units of DVD_TIME_BASE (usually microseconds). +void OMXClock::CheckSystemClock() +{ + if(!m_systemFrequency) + m_systemFrequency = g_VideoReferenceClock.GetFrequency(); + + if(!m_systemOffset) + m_systemOffset = g_VideoReferenceClock.GetTime(); +} + +double OMXClock::GetClock(bool interpolated /*= true*/) +{ + Lock(); + double clock = SystemToPlaying(g_VideoReferenceClock.GetTime(interpolated)); + UnLock(); + return clock; +} + +double OMXClock::GetClock(double& absolute, bool interpolated /*= true*/) +{ + int64_t current = g_VideoReferenceClock.GetTime(interpolated); + + Lock(); + CheckSystemClock(); + absolute = SystemToAbsolute(current); + current = SystemToPlaying(current); + UnLock(); + + return current; +} + +void OMXClock::SetSpeed(int iSpeed) +{ + // this will sometimes be a little bit of due to rounding errors, ie clock might jump abit when changing speed + Lock(); + + if(iSpeed == DVD_PLAYSPEED_PAUSE) + { + if(!m_pauseClock) + m_pauseClock = g_VideoReferenceClock.GetTime(); + UnLock(); + return; + } + + int64_t current; + int64_t newfreq = m_systemFrequency * DVD_PLAYSPEED_NORMAL / iSpeed; + + current = g_VideoReferenceClock.GetTime(); + if( m_pauseClock ) + { + m_startClock += current - m_pauseClock; + m_pauseClock = 0; + } + + m_startClock = current - (int64_t)((double)(current - m_startClock) * newfreq / m_systemUsed); + m_systemUsed = newfreq; + UnLock(); +} + +void OMXClock::Discontinuity(double currentPts) +{ + Lock(); + m_startClock = g_VideoReferenceClock.GetTime(); + if(m_pauseClock) + m_pauseClock = m_startClock; + m_iDisc = currentPts; + m_bReset = false; + UnLock(); +} + +void OMXClock::Pause() +{ + Lock(); + if(!m_pauseClock) + m_pauseClock = g_VideoReferenceClock.GetTime(); + UnLock(); +} + +void OMXClock::Resume() +{ + Lock(); + if( m_pauseClock ) + { + int64_t current; + current = g_VideoReferenceClock.GetTime(); + + m_startClock += current - m_pauseClock; + m_pauseClock = 0; + } + UnLock(); +} + +bool OMXClock::SetMaxSpeedAdjust(double speed) +{ + Lock(); + m_maxspeedadjust = speed; + UnLock(); + return m_speedadjust; +} + +//returns the refreshrate if the videoreferenceclock is running, -1 otherwise +int OMXClock::UpdateFramerate(double fps, double* interval /*= NULL*/) +{ + //sent with fps of 0 means we are not playing video + if(fps == 0.0) + { + Lock(); + m_speedadjust = false; + UnLock(); + return -1; + } + + //check if the videoreferenceclock is running, will return -1 if not + int rate = g_VideoReferenceClock.GetRefreshRate(interval); + + if (rate <= 0) + return -1; + + Lock(); + + m_speedadjust = true; + + double weight = (double)rate / (double)MathUtils::round_int(fps); + + //set the speed of the videoreferenceclock based on fps, refreshrate and maximum speed adjust set by user + if (m_maxspeedadjust > 0.05) + { + if (weight / MathUtils::round_int(weight) < 1.0 + m_maxspeedadjust / 100.0 + && weight / MathUtils::round_int(weight) > 1.0 - m_maxspeedadjust / 100.0) + weight = MathUtils::round_int(weight); + } + double speed = (double)rate / (fps * weight); + UnLock(); + + g_VideoReferenceClock.SetSpeed(speed); + + return rate; +} + +double OMXClock::SystemToAbsolute(int64_t system) +{ + return DVD_TIME_BASE * (double)(system - m_systemOffset) / m_systemFrequency; +} + +double OMXClock::SystemToPlaying(int64_t system) +{ + int64_t current; + + if (m_bReset) + { + m_startClock = system; + m_systemUsed = m_systemFrequency; + m_pauseClock = 0; + m_iDisc = 0; + m_bReset = false; + } + + if (m_pauseClock) + current = m_pauseClock; + else + current = system; + + return DVD_TIME_BASE * (double)(current - m_startClock) / m_systemUsed + m_iDisc; +} + +void OMXClock::OMXSetClockPorts(OMX_TIME_CONFIG_CLOCKSTATETYPE *clock) +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + if(!clock) + return; + + if(m_has_audio) + { + m_audio_start = true; + clock->nWaitMask |= OMX_CLOCKPORT0; + } + + if(m_has_video) + { + m_video_start = true; + clock->nWaitMask |= OMX_CLOCKPORT1; + } +} + +bool OMXClock::OMXSetReferenceClock(bool lock /* = true */) +{ + if(lock) + Lock(); + + bool ret = true; + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; + OMX_INIT_STRUCTURE(refClock); + + if(g_guiSettings.GetBool("videoplayer.usedisplayasclock") && m_has_video) + refClock.eClock = OMX_TIME_RefClockVideo; + else if(m_has_audio) + refClock.eClock = OMX_TIME_RefClockAudio; + else + refClock.eClock = OMX_TIME_RefClockVideo; + + CLog::Log(LOGNOTICE, "OMXClock using %s as reference\n", refClock.eClock == OMX_TIME_RefClockVideo ? "video" : "audio"); + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeActiveRefClock, &refClock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXSetReferenceClock error setting OMX_IndexConfigTimeActiveRefClock\n"); + ret = false; + } + + UnLock(); + + return ret; +} + +bool OMXClock::OMXInitialize(bool has_video, bool has_audio) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + std::string componentName = ""; + + m_has_video = has_video; + m_has_audio = has_audio; + + m_video_start = false; + m_audio_start = false; + m_pause = false; + m_audio_buffer = false; + + componentName = "OMX.broadcom.clock"; + if(!m_omx_clock.Initialize((const std::string)componentName, OMX_IndexParamOtherInit)) + return false; + + if(!OMXSetReferenceClock(false)) + return false; + + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGWARNING, "OMXClock::OMXInitialize setting OMX_IndexConfigTimeClockState\n"); + + return true; +} + +void OMXClock::OMXDeinitialize() +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + m_omx_clock.Deinitialize(); + + m_omx_speed = DVD_PLAYSPEED_NORMAL; +} + +bool OMXClock::OMXStatePause(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + if(m_omx_clock.GetState() != OMX_StatePause) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + omx_err = m_omx_clock.SetStateForComponent(OMX_StatePause); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StatePause m_omx_clock.SetStateForComponent\n"); + if(lock) + UnLock(); + return false; + } + } + + if(lock) + UnLock(); + + return true; +} + +bool OMXClock::OMXStateExecute(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(m_omx_clock.GetState() != OMX_StateExecuting) + { + + OMXStateIdle(false); + + omx_err = m_omx_clock.SetStateForComponent(OMX_StateExecuting); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StateExecute m_omx_clock.SetStateForComponent\n"); + if(lock) + UnLock(); + return false; + } + } + + /* + if(m_has_audio) + { + omx_err = m_omx_clock.EnablePort(m_omx_clock.GetInputPort(), true); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StateExecute - Error enable port %d on component %s omx_err(0x%08x)", + m_omx_clock.GetInputPort(), m_omx_clock.GetName().c_str(), (int)omx_err); + } + } + + if(m_has_video) + { + omx_err = m_omx_clock.EnablePort(m_omx_clock.GetInputPort() + 1, true); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::StateExecute - Error enable port %d on component %s omx_err(0x%08x)", + m_omx_clock.GetInputPort(), m_omx_clock.GetName().c_str(), (int)omx_err); + } + } + + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + clock.nStartTime = ToOMXTime(0LL); + clock.nOffset = ToOMXTime(0LL); + clock.nWaitMask = 0; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXStateExecute error setting OMX_IndexConfigTimeClockState\n"); + } + */ + + //OMXStart(lock); + + if(lock) + UnLock(); + + return true; +} + +void OMXClock::OMXStateIdle(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + if(lock) + Lock(); + + if(m_omx_clock.GetState() == OMX_StateExecuting) + m_omx_clock.SetStateForComponent(OMX_StatePause); + + if(m_omx_clock.GetState() != OMX_StateIdle) + m_omx_clock.SetStateForComponent(OMX_StateIdle); + + if(lock) + UnLock(); +} + +COMXCoreComponent *OMXClock::GetOMXClock() +{ + return &m_omx_clock; +} + +void OMXClock::OMXSaveState(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_INIT_STRUCTURE(m_clock_state); + + omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeClockState, &m_clock_state); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "OMXClock::SaveState error geting OMX_IndexConfigTimeClockState\n"); + + if(lock) + UnLock(); +} + +void OMXClock::OMXRestoreState(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &m_clock_state); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "OMXClock::RestoreState error setting OMX_IndexConfigTimeClockState\n"); + + if(lock) + UnLock(); +} + +bool OMXClock::OMXStop(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + CLog::Log(LOGDEBUG, "OMXClock::OMXStop\n"); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateStopped; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Stop error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + + if(lock) + UnLock(); + + return true; +} + +bool OMXClock::OMXStart(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + CLog::Log(LOGDEBUG, "OMXClock::OMXStart\n"); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateRunning; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Start error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + + if(lock) + UnLock(); + + return true; +} + +void OMXClock::VideoStart(bool video_start) +{ + Lock(); + m_video_start = video_start; + UnLock(); +}; + +void OMXClock::AudioStart(bool audio_start) +{ + Lock(); + m_audio_start = audio_start; + UnLock(); +}; + +bool OMXClock::OMXReset(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + CLog::Log(LOGDEBUG, "OMXClock::OMXReset 0x%08x\n", m_omx_clock.GetState()); + + m_audio_buffer = false; + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!OMXSetReferenceClock(false)) + { + UnLock(); + return false; + } + + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + //clock.nOffset = ToOMXTime(-1000LL * 200); + + OMXSetClockPorts(&clock); + + if(clock.nWaitMask) + { + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXReset error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + } + + if(lock) + UnLock(); + + return true; +} + +double OMXClock::OMXWallTime(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return 0; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + double pts = 0; + + OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp; + OMX_INIT_STRUCTURE(timeStamp); + timeStamp.nPortIndex = m_omx_clock.GetInputPort(); + + omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentWallTime, &timeStamp); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::WallTime error getting OMX_IndexConfigTimeCurrentWallTime\n"); + if(lock) + UnLock(); + return 0; + } + + pts = FromOMXTime(timeStamp.nTimestamp); + + if(lock) + UnLock(); + + return pts; +} + +double OMXClock::OMXMediaTime(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return 0; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + double pts = 0; + + OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp; + OMX_INIT_STRUCTURE(timeStamp); + timeStamp.nPortIndex = m_omx_clock.GetInputPort(); + + omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime\n"); + if(lock) + UnLock(); + return 0; + } + + pts = FromOMXTime(timeStamp.nTimestamp); + if(lock) + UnLock(); + + return pts; +} + +bool OMXClock::OMXPause(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(m_pause) + return true; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = 0; // pause + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Pause error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + + CLog::Log(LOGDEBUG, "OMXClock::OMXPause\n"); + + m_pause = true; + + if(lock) + UnLock(); + + return true; +} + +bool OMXClock::OMXResume(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(!m_pause) + return true; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = (1<<16); // normal speed + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Resume error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + + m_pause = false; + + CLog::Log(LOGDEBUG, "OMXClock::OMXResume\n"); + + if(lock) + UnLock(); + + return true; +} + +bool OMXClock::OMXUpdateClock(double pts, bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_TIMESTAMPTYPE ts; + OMX_INIT_STRUCTURE(ts); + + ts.nPortIndex = OMX_ALL; + ts.nTimestamp = ToOMXTime((uint64_t)pts); + + CLog::Log(LOGDEBUG, "OMXClock::OMXUpdateClock %f", pts / DVD_TIME_BASE); + + if(m_has_audio) + { + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeCurrentAudioReference, &ts); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "OMXClock::OMXUpdateClock error setting OMX_IndexConfigTimeCurrentAudioReference\n"); + } + else + { + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeCurrentVideoReference, &ts); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "OMXClock::OMXUpdateClock error setting OMX_IndexConfigTimeCurrentVideoReference\n"); + } + + if(lock) + UnLock(); + + return true; +} + +bool OMXClock::OMXWaitStart(double pts, bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateWaitingForStartTime; + OMXSetClockPorts(&clock); + clock.nStartTime = ToOMXTime((uint64_t)pts); + //clock.nOffset = ToOMXTime(-1000LL * 200); + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXWaitStart error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + + if(lock) + UnLock(); + + return true; +} + +void OMXClock::OMXHandleBackward(bool lock /* = true */) +{ + /* + if(m_omx_clock.GetComponent() == NULL) + return; + + if(lock) + Lock(); + + if(m_omx_speed < 0) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_CLOCKSTATETYPE clock; + OMX_INIT_STRUCTURE(clock); + + clock.eState = OMX_TIME_ClockStateRunning; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXHandleBackward error setting OMX_IndexConfigTimeClockState\n"); + } + } + + if(lock) + UnLock(); + */ +} + +bool OMXClock::OMXSetSpeed(int speed, bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(m_pause) + return true; + + if(lock) + Lock(); + + m_omx_speed = speed; + + CLog::Log(LOGDEBUG, "OMXClock::OMXSetSpeed fake %d\n\n", m_omx_speed / DVD_PLAYSPEED_NORMAL); + + // only adjust speed when not audio buffering + if(!m_audio_buffer) + { + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = ((m_omx_speed / DVD_PLAYSPEED_NORMAL) << 16); + + CLog::Log(LOGDEBUG, "OMXClock::OMXSetSpeed real %d", m_omx_speed / DVD_PLAYSPEED_NORMAL); + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::OMXSetSpeed error setting OMX_IndexConfigTimeClockState\n"); + if(lock) + UnLock(); + return false; + } + } + + if(lock) + UnLock(); + + return true; +} + +void OMXClock::AddTimespecs(struct timespec &time, long millisecs) +{ + time.tv_sec += millisecs / 1000; + time.tv_nsec += (millisecs % 1000) * 1000000; + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + +bool OMXClock::HDMIClockSync(bool lock /* = true */) +{ + if(m_omx_clock.GetComponent() == NULL) + return false; + + if(lock) + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_CONFIG_LATENCYTARGETTYPE latencyTarget; + OMX_INIT_STRUCTURE(latencyTarget); + + latencyTarget.nPortIndex = OMX_ALL; + latencyTarget.bEnabled = OMX_TRUE; + latencyTarget.nFilter = 10; + latencyTarget.nTarget = 0; + latencyTarget.nShift = 3; + latencyTarget.nSpeedFactor = -200; + latencyTarget.nInterFactor = 100; + latencyTarget.nAdjCap = 100; + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "OMXClock::Speed error setting OMX_IndexConfigLatencyTarget\n"); + if(lock) + UnLock(); + return false; + } + + if(lock) + UnLock(); + + return true; +} + +int64_t OMXClock::CurrentHostCounter(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return( ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec ); +} + +int64_t OMXClock::CurrentHostFrequency(void) +{ + return( (int64_t)1000000000L ); +} + +void OMXClock::AddTimeSpecNano(struct timespec &time, uint64_t nanoseconds) +{ + time.tv_sec += nanoseconds / 1000000000; + time.tv_nsec += (nanoseconds % 1000000000); + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + +int OMXClock::GetRefreshRate(double* interval) +{ + if(!interval) + return false; + + *interval = m_fps; + return true; +} + +void OMXClock::OMXAudioBufferStart() +{ + Lock(); + + m_audio_buffer = true; + + if(m_omx_clock.GetComponent() == NULL) + { + UnLock(); + return; + } + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_TIME_CONFIG_SCALETYPE scaleType; + OMX_INIT_STRUCTURE(scaleType); + + scaleType.xScale = 0; + + CLog::Log(LOGDEBUG, "OMXClock::OMXAudioBufferStart"); + + omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType); + if(omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "OMXClock::OMXAudioBufferStart error setting OMX_IndexConfigTimeClockState\n"); + + UnLock(); +} + +void OMXClock::OMXAudioBufferStop() +{ + Lock(); + + CLog::Log(LOGDEBUG, "OMXClock::OMXAudioBufferStop"); + + m_audio_buffer = false; + + OMXSetSpeed(m_omx_speed, false); + + UnLock(); +} + +double OMXClock::NormalizeFrameduration(double frameduration) +{ + //if the duration is within 20 microseconds of a common duration, use that + const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0, + DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0, + DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0}; + + double lowestdiff = DVD_TIME_BASE; + int selected = -1; + for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); i++) + { + double diff = fabs(frameduration - durations[i]); + if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff) + { + selected = i; + lowestdiff = diff; + } + } + + if (selected != -1) + return durations[selected]; + else + return frameduration; +} + +#endif diff --git a/xbmc/linux/OMXClock.h b/xbmc/linux/OMXClock.h new file mode 100644 index 0000000000000..1327996dbb1c7 --- /dev/null +++ b/xbmc/linux/OMXClock.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef _AVCLOCK_H_ +#define _AVCLOCK_H_ + +#if defined(HAVE_OMXLIB) + +#include "OMXCore.h" +#include "DVDClock.h" +#include "linux/XTimeUtils.h" + +#ifdef OMX_SKIP64BIT +static inline OMX_TICKS ToOMXTime(int64_t pts) +{ + OMX_TICKS ticks; + ticks.nLowPart = pts; + ticks.nHighPart = pts >> 32; + return ticks; +} +static inline uint64_t FromOMXTime(OMX_TICKS ticks) +{ + uint64_t pts = ticks.nLowPart | ((uint64_t)ticks.nHighPart << 32); + return pts; +} +#else +#define FromOMXTime(x) (x) +#define ToOMXTime(x) (x) +#endif + +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, +}; + +class OMXClock +{ +protected: + bool m_pause; + bool m_has_video; + bool m_has_audio; + int m_play_speed; + pthread_mutex_t m_lock; + double SystemToAbsolute(int64_t system); + double SystemToPlaying(int64_t system); + int64_t m_systemUsed; + int64_t m_startClock; + int64_t m_pauseClock; + double m_iDisc; + bool m_bReset; + static int64_t m_systemFrequency; + static int64_t m_systemOffset; + int64_t m_ClockOffset; + double m_maxspeedadjust; + bool m_speedadjust; + static bool m_ismasterclock; + double m_fps; + int m_omx_speed; + bool m_video_start; + bool m_audio_start; + bool m_audio_buffer; + CDVDClock m_clock; + OMX_TIME_CONFIG_CLOCKSTATETYPE m_clock_state; +private: + COMXCoreComponent m_omx_clock; +public: + OMXClock(); + ~OMXClock(); + void Lock(); + void UnLock(); + double GetAbsoluteClock(bool interpolated = true); + double GetFrequency() { return (double)m_systemFrequency ; } + double WaitAbsoluteClock(double target); + double GetClock(bool interpolated = true); + double GetClock(double& absolute, bool interpolated = true); + void CheckSystemClock(); + void SetSpeed(int iSpeed); + void SetMasterClock(bool ismasterclock) { m_ismasterclock = ismasterclock; } + bool IsMasterClock() { return m_ismasterclock; } + void Discontinuity(double currentPts = 0LL); + + void Reset() { m_bReset = true; } + void Pause(); + void Resume(); + + int UpdateFramerate(double fps, double* interval = NULL); + bool SetMaxSpeedAdjust(double speed); + + void OMXSetClockPorts(OMX_TIME_CONFIG_CLOCKSTATETYPE *clock); + bool OMXSetReferenceClock(bool lock = true); + bool OMXInitialize(bool has_video, bool has_audio); + void OMXDeinitialize(); + bool OMXIsPaused() { return m_pause; }; + void OMXSaveState(bool lock = true); + void OMXRestoreState(bool lock = true); + bool OMXStop(bool lock = true); + bool OMXStart(bool lock = true); + bool OMXReset(bool lock = true); + double OMXWallTime(bool lock = true); + double OMXMediaTime(bool lock = true); + bool OMXPause(bool lock = true); + bool OMXResume(bool lock = true); + bool OMXUpdateClock(double pts, bool lock = true); + bool OMXWaitStart(double pts, bool lock = true); + void OMXHandleBackward(bool lock = true); + bool OMXSetSpeed(int speed, bool lock = true); + int OMXPlaySpeed() { return m_omx_speed; }; + int OMXGetPlaySpeed() { return m_omx_speed; }; + COMXCoreComponent *GetOMXClock(); + bool OMXStatePause(bool lock = true); + bool OMXStateExecute(bool lock = true); + void OMXStateIdle(bool lock = true); + static void AddTimespecs(struct timespec &time, long millisecs); + bool HDMIClockSync(bool lock = true); + static int64_t CurrentHostCounter(void); + static int64_t CurrentHostFrequency(void); + bool HasVideo() { return m_has_video; }; + bool HasAudio() { return m_has_audio; }; + void HasVideo(bool has_video) { m_has_video = has_video; }; + void HasAudio(bool has_audio) { m_has_audio = has_audio; }; + bool VideoStart() { return m_video_start; }; + bool AudioStart() { return m_audio_start; }; + void VideoStart(bool video_start); + void AudioStart(bool audio_start); + static void AddTimeSpecNano(struct timespec &time, uint64_t nanoseconds); + + void OMXAudioBufferStart(); + void OMXAudioBufferStop(); + bool OMXAudioBuffer() { return m_audio_buffer; }; + + int GetRefreshRate(double* interval = NULL); + void SetRefreshRate(double fps) { m_fps = fps; }; + + static double NormalizeFrameduration(double frameduration); +}; + +#endif + +#endif diff --git a/xbmc/linux/OMXCore.cpp b/xbmc/linux/OMXCore.cpp new file mode 100644 index 0000000000000..938d79b07da8e --- /dev/null +++ b/xbmc/linux/OMXCore.cpp @@ -0,0 +1,1711 @@ +/* + * Copyright (C) 2010 Team XBMCn + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#elif defined(_WIN32) +#include "system.h" +#endif + +#include +#include + +#if defined(HAVE_OMXLIB) +#include "OMXCore.h" +#include "utils/log.h" + +#include "OMXClock.h" + +#ifdef _LINUX +#include "XMemUtils.h" +#endif + +//#define OMX_DEBUG_EVENTS +//#define OMX_DEBUG_EVENTHANDLER + +//////////////////////////////////////////////////////////////////////////////////////////// +#define CLASSNAME "COMXCoreComponent" +//////////////////////////////////////////////////////////////////////////////////////////// + +static void add_timespecs(struct timespec &time, long millisecs) +{ + time.tv_sec += millisecs / 1000; + time.tv_nsec += (millisecs % 1000) * 1000000; + if (time.tv_nsec > 1000000000) + { + time.tv_sec += 1; + time.tv_nsec -= 1000000000; + } +} + + +COMXCoreTunel::COMXCoreTunel() +{ + m_src_component = NULL; + m_dst_component = NULL; + m_src_port = 0; + m_dst_port = 0; + m_portSettingsChanged = false; + m_DllOMX = new DllOMX(); + m_DllOMXOpen = m_DllOMX->Load(); + + pthread_mutex_init(&m_lock, NULL); +} + +COMXCoreTunel::~COMXCoreTunel() +{ + Deestablish(); + if(m_DllOMXOpen) + m_DllOMX->Unload(); + delete m_DllOMX; + + pthread_mutex_destroy(&m_lock); +} + +void COMXCoreTunel::Lock() +{ + pthread_mutex_lock(&m_lock); +} + +void COMXCoreTunel::UnLock() +{ + pthread_mutex_unlock(&m_lock); +} + +void COMXCoreTunel::Initialize(COMXCoreComponent *src_component, unsigned int src_port, COMXCoreComponent *dst_component, unsigned int dst_port) +{ + if(!m_DllOMXOpen) + return; + m_src_component = src_component; + m_src_port = src_port; + m_dst_component = dst_component; + m_dst_port = dst_port; +} + +OMX_ERRORTYPE COMXCoreTunel::Flush() +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + if(!m_src_component || !m_dst_component) + return OMX_ErrorUndefined; + + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + if(m_src_component->GetComponent()) + { + omx_err = OMX_SendCommand(m_src_component->GetComponent(), OMX_CommandFlush, m_src_port, NULL); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Flush - Error flush port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = OMX_SendCommand(m_dst_component->GetComponent(), OMX_CommandFlush, m_dst_port, NULL); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Flush - Error flush port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_src_component->GetComponent()) + omx_err = m_src_component->WaitForCommand(OMX_CommandFlush, m_src_port); + + if(m_dst_component->GetComponent()) + omx_err = m_dst_component->WaitForCommand(OMX_CommandFlush, m_dst_port); + + UnLock(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreTunel::Deestablish(bool noWait) +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + if(!m_src_component || !m_dst_component) + return OMX_ErrorUndefined; + + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(m_src_component->GetComponent() && m_portSettingsChanged && !noWait) + omx_err = m_src_component->WaitForEvent(OMX_EventPortSettingsChanged); + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->DisablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - Error disable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->DisablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - Error disable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_src_component->GetComponent(), m_src_port, NULL, 0); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorIncorrectStateOperation) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - could not unset tunnel on comp src %s port %d omx_err(0x%08x)\n", + m_src_component->GetName().c_str(), m_src_port, (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_dst_component->GetComponent(), m_dst_port, NULL, 0); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorIncorrectStateOperation) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deestablish - could not unset tunnel on comp dst %s port %d omx_err(0x%08x)\n", + m_dst_component->GetName().c_str(), m_dst_port, (int)omx_err); + } + } + + UnLock(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreTunel::Establish(bool portSettingsChanged) +{ + if(!m_DllOMXOpen) + return OMX_ErrorUndefined; + + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_PARAM_U32TYPE param; + OMX_INIT_STRUCTURE(param); + + if(!m_src_component || !m_dst_component) + { + UnLock(); + return OMX_ErrorUndefined; + } + + if(m_src_component->GetState() == OMX_StateLoaded) + { + omx_err = m_src_component->SetStateForComponent(OMX_StateIdle); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error setting state to idle %s omx_err(0x%08x)", + m_src_component->GetName().c_str(), (int)omx_err); + UnLock(); + return omx_err; + } + } + + if(portSettingsChanged) + { + omx_err = m_src_component->WaitForEvent(OMX_EventPortSettingsChanged); + if(omx_err != OMX_ErrorNone) + { + UnLock(); + return omx_err; + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->DisablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error disable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->DisablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState) { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error disable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + } + } + + if(m_src_component->GetComponent() && m_dst_component->GetComponent()) + { + omx_err = m_DllOMX->OMX_SetupTunnel(m_src_component->GetComponent(), m_src_port, m_dst_component->GetComponent(), m_dst_port); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - could not setup tunnel src %s port %d dst %s port %d omx_err(0x%08x)\n", + m_src_component->GetName().c_str(), m_src_port, m_dst_component->GetName().c_str(), m_dst_port, (int)omx_err); + UnLock(); + return omx_err; + } + } + else + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - could not setup tunnel\n"); + UnLock(); + return OMX_ErrorUndefined; + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->EnablePort(m_src_port, false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error enable port %d on component %s omx_err(0x%08x)", + m_src_port, m_src_component->GetName().c_str(), (int)omx_err); + UnLock(); + return omx_err; + } + } + + if(m_dst_component->GetComponent()) + { + omx_err = m_dst_component->EnablePort(m_dst_port, false); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error enable port %d on component %s omx_err(0x%08x)", + m_dst_port, m_dst_component->GetName().c_str(), (int)omx_err); + UnLock(); + return omx_err; + } + } + + if(m_dst_component->GetComponent()) + { + if(m_dst_component->GetState() == OMX_StateLoaded) + { + omx_err = m_dst_component->WaitForCommand(OMX_CommandPortEnable, m_dst_port); + if(omx_err != OMX_ErrorNone) + { + UnLock(); + return omx_err; + } + + omx_err = m_dst_component->SetStateForComponent(OMX_StateIdle); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Establish - Error setting state to idle %s omx_err(0x%08x)", + m_src_component->GetName().c_str(), (int)omx_err); + UnLock(); + return omx_err; + } + } + else + { + omx_err = m_dst_component->WaitForCommand(OMX_CommandPortEnable, m_dst_port); + if(omx_err != OMX_ErrorNone) + { + UnLock(); + return omx_err; + } + } + } + + if(m_src_component->GetComponent()) + { + omx_err = m_src_component->WaitForCommand(OMX_CommandPortEnable, m_src_port); + if(omx_err != OMX_ErrorNone) + { + UnLock(); + return omx_err; + } + } + + m_portSettingsChanged = portSettingsChanged; + + UnLock(); + + return OMX_ErrorNone; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +COMXCoreComponent::COMXCoreComponent() +{ + m_input_port = 0; + m_output_port = 0; + m_handle = NULL; + + m_input_alignment = 0; + m_input_buffer_size = 0; + m_input_buffer_count = 0; + + m_output_alignment = 0; + m_output_buffer_size = 0; + m_output_buffer_count = 0; + m_flush_input = false; + m_flush_output = false; + + m_eos = false; + + m_exit = false; + m_DllOMXOpen = false; + + pthread_mutex_init(&m_omx_input_mutex, NULL); + pthread_mutex_init(&m_omx_output_mutex, NULL); + pthread_mutex_init(&m_omx_event_mutex, NULL); + pthread_cond_init(&m_input_buffer_cond, NULL); + pthread_cond_init(&m_output_buffer_cond, NULL); + pthread_cond_init(&m_omx_event_cond, NULL); + + m_omx_input_use_buffers = false; + m_omx_output_use_buffers = false; + + m_DllOMX = new DllOMX(); + + pthread_mutex_init(&m_lock, NULL); + sem_init(&m_omx_fill_buffer_done, 0, 0); +} + +COMXCoreComponent::~COMXCoreComponent() +{ + Deinitialize(); + + pthread_mutex_destroy(&m_omx_input_mutex); + pthread_mutex_destroy(&m_omx_output_mutex); + pthread_mutex_destroy(&m_omx_event_mutex); + pthread_cond_destroy(&m_input_buffer_cond); + pthread_cond_destroy(&m_output_buffer_cond); + pthread_cond_destroy(&m_omx_event_cond); + + pthread_mutex_destroy(&m_lock); + sem_destroy(&m_omx_fill_buffer_done); + + delete m_DllOMX; +} + +void COMXCoreComponent::Lock() +{ + pthread_mutex_lock(&m_lock); +} + +void COMXCoreComponent::UnLock() +{ + pthread_mutex_unlock(&m_lock); +} + +OMX_ERRORTYPE COMXCoreComponent::EmptyThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_EmptyThisBuffer(m_handle, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::EmptyThisBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FillThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_FillThisBuffer(m_handle, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FillThisBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeOutputBuffer(OMX_BUFFERHEADERTYPE *omx_buffer) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle || !omx_buffer) + return OMX_ErrorUndefined; + + omx_err = OMX_FreeBuffer(m_handle, m_output_port, omx_buffer); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeOutputBuffer component(%s) - failed with result(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + return omx_err; +} + +unsigned int COMXCoreComponent::GetInputBufferSize() +{ + int free = m_input_buffer_count * m_input_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetOutputBufferSize() +{ + int free = m_output_buffer_count * m_output_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetInputBufferSpace() +{ + int free = m_omx_input_avaliable.size() * m_input_buffer_size; + return free; +} + +unsigned int COMXCoreComponent::GetOutputBufferSpace() +{ + int free = m_omx_output_available.size() * m_output_buffer_size; + return free; +} + +void COMXCoreComponent::FlushAll() +{ + FlushInput(); + FlushOutput(); +} + +void COMXCoreComponent::FlushInput() +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + omx_err = OMX_SendCommand(m_handle, OMX_CommandFlush, m_input_port, NULL); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FlushInput - Error on component %s omx_err(0x%08x)", + m_componentName.c_str(), (int)omx_err); + } + WaitForCommand(OMX_CommandFlush, m_input_port); + + UnLock(); +} + +void COMXCoreComponent::FlushOutput() +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + omx_err = OMX_SendCommand(m_handle, OMX_CommandFlush, m_output_port, NULL); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FlushOutput - Error on component %s omx_err(0x%08x)", + m_componentName.c_str(), (int)omx_err); + } + WaitForCommand(OMX_CommandFlush, m_output_port); + + UnLock(); +} + +// timeout in milliseconds +OMX_BUFFERHEADERTYPE *COMXCoreComponent::GetInputBuffer(long timeout) +{ + OMX_BUFFERHEADERTYPE *omx_input_buffer = NULL; + + if(!m_handle) + return NULL; + + pthread_mutex_lock(&m_omx_input_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while (1 && !m_flush_input) + { + if(!m_omx_input_avaliable.empty()) + { + omx_input_buffer = m_omx_input_avaliable.front(); + m_omx_input_avaliable.pop(); + break; + } + + int retcode = pthread_cond_timedwait(&m_input_buffer_cond, &m_omx_input_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::GetInputBuffer %s wait event timeout\n", m_componentName.c_str()); + break; + } + } + pthread_mutex_unlock(&m_omx_input_mutex); + return omx_input_buffer; +} + +OMX_BUFFERHEADERTYPE *COMXCoreComponent::GetOutputBuffer() +{ + OMX_BUFFERHEADERTYPE *omx_output_buffer = NULL; + + if(!m_handle) + return NULL; + + pthread_mutex_lock(&m_omx_output_mutex); + + if(!m_omx_output_available.empty()) + { + omx_output_buffer = m_omx_output_available.front(); + m_omx_output_available.pop(); + } + + pthread_mutex_unlock(&m_omx_output_mutex); + return omx_output_buffer; +} + +OMX_ERRORTYPE COMXCoreComponent::AllocInputBuffers(bool use_buffers /* = false **/) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + m_omx_input_use_buffers = use_buffers; + + if(!m_handle) + return OMX_ErrorUndefined; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = m_input_port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() != OMX_StateIdle) + { + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + SetStateForComponent(OMX_StateIdle); + } + + omx_err = EnablePort(m_input_port, false); + if(omx_err != OMX_ErrorNone) + return omx_err; + + m_input_alignment = portFormat.nBufferAlignment; + m_input_buffer_count = portFormat.nBufferCountActual; + m_input_buffer_size = portFormat.nBufferSize; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::AllocInputBuffers component(%s) - port(%d), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu), nBufferAlignmen(%lu)\n", + m_componentName.c_str(), GetInputPort(), portFormat.nBufferCountMin, + portFormat.nBufferCountActual, portFormat.nBufferSize, portFormat.nBufferAlignment); + + for (size_t i = 0; i < portFormat.nBufferCountActual; i++) + { + OMX_BUFFERHEADERTYPE *buffer = NULL; + OMX_U8* data = NULL; + + if(m_omx_input_use_buffers) + { + data = (OMX_U8*)_aligned_malloc(portFormat.nBufferSize, m_input_alignment); + omx_err = OMX_UseBuffer(m_handle, &buffer, m_input_port, NULL, portFormat.nBufferSize, data); + } + else + { + omx_err = OMX_AllocateBuffer(m_handle, &buffer, m_input_port, NULL, portFormat.nBufferSize); + } + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::AllocInputBuffers component(%s) - OMX_UseBuffer failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + + if(m_omx_input_use_buffers && data) + _aligned_free(data); + + return omx_err; + } + buffer->nInputPortIndex = m_input_port; + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->pAppPrivate = (void*)i; + m_omx_input_buffers.push_back(buffer); + m_omx_input_avaliable.push(buffer); + } + + omx_err = WaitForCommand(OMX_CommandPortEnable, m_input_port); + + m_flush_input = false; + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::AllocOutputBuffers(bool use_buffers /* = false */) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + m_omx_output_use_buffers = use_buffers; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = m_output_port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + return omx_err; + + if(GetState() != OMX_StateIdle) + { + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + SetStateForComponent(OMX_StateIdle); + } + + omx_err = EnablePort(m_output_port, false); + if(omx_err != OMX_ErrorNone) + return omx_err; + + m_output_alignment = portFormat.nBufferAlignment; + m_output_buffer_count = portFormat.nBufferCountActual; + m_output_buffer_size = portFormat.nBufferSize; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::AllocOutputBuffers component(%s) - port(%d), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu) nBufferAlignmen(%lu)\n", + m_componentName.c_str(), m_output_port, portFormat.nBufferCountMin, + portFormat.nBufferCountActual, portFormat.nBufferSize, portFormat.nBufferAlignment); + + for (size_t i = 0; i < portFormat.nBufferCountActual; i++) + { + OMX_BUFFERHEADERTYPE *buffer = NULL; + OMX_U8* data = NULL; + + if(m_omx_output_use_buffers) + { + data = (OMX_U8*)_aligned_malloc(portFormat.nBufferSize, m_output_alignment); + omx_err = OMX_UseBuffer(m_handle, &buffer, m_output_port, NULL, portFormat.nBufferSize, data); + } + else + { + omx_err = OMX_AllocateBuffer(m_handle, &buffer, m_output_port, NULL, portFormat.nBufferSize); + } + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::AllocOutputBuffers component(%s) - OMX_UseBuffer failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + + if(m_omx_output_use_buffers && data) + _aligned_free(data); + + return omx_err; + } + buffer->nOutputPortIndex = m_output_port; + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->pAppPrivate = (void*)i; + m_omx_output_buffers.push_back(buffer); + m_omx_output_available.push(buffer); + } + + omx_err = WaitForCommand(OMX_CommandPortEnable, m_output_port); + + m_flush_output = false; + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeInputBuffers(bool wait) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + if(m_omx_input_buffers.empty()) + return OMX_ErrorNone; + + m_flush_input = true; + + pthread_mutex_lock(&m_omx_input_mutex); + pthread_cond_broadcast(&m_input_buffer_cond); + + omx_err = DisablePort(m_input_port, false); + + for (size_t i = 0; i < m_omx_input_buffers.size(); i++) + { + uint8_t *buf = m_omx_input_buffers[i]->pBuffer; + + omx_err = OMX_FreeBuffer(m_handle, m_input_port, m_omx_input_buffers[i]); + + if(m_omx_input_use_buffers && buf) + _aligned_free(buf); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeInputBuffers error deallocate omx input buffer on component %s omx_err(0x%08x)\n", m_componentName.c_str(), omx_err); + } + } + + WaitForCommand(OMX_CommandPortDisable, m_input_port); + assert(m_omx_input_buffers.size() == m_omx_input_avaliable.size()); + + m_omx_input_buffers.clear(); + + while (!m_omx_input_avaliable.empty()) + m_omx_input_avaliable.pop(); + + m_input_alignment = 0; + m_input_buffer_size = 0; + m_input_buffer_count = 0; + + pthread_mutex_unlock(&m_omx_input_mutex); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::FreeOutputBuffers(bool wait) +{ + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + return OMX_ErrorUndefined; + + if(m_omx_output_buffers.empty()) + return OMX_ErrorNone; + + m_flush_output = true; + + pthread_mutex_lock(&m_omx_output_mutex); + pthread_cond_broadcast(&m_output_buffer_cond); + + omx_err = DisablePort(m_output_port, false); + + for (size_t i = 0; i < m_omx_output_buffers.size(); i++) + { + uint8_t *buf = m_omx_output_buffers[i]->pBuffer; + + omx_err = OMX_FreeBuffer(m_handle, m_output_port, m_omx_output_buffers[i]); + + if(m_omx_output_use_buffers && buf) + _aligned_free(buf); + + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::FreeOutputBuffers error deallocate omx output buffer on component %s omx_err(0x%08x)\n", m_componentName.c_str(), omx_err); + } + } + + WaitForCommand(OMX_CommandPortDisable, m_output_port); + assert(m_omx_output_buffers.size() == m_omx_output_available.size()); + + m_omx_output_buffers.clear(); + + while (!m_omx_output_available.empty()) + m_omx_output_available.pop(); + + m_output_alignment = 0; + m_output_buffer_size = 0; + m_output_buffer_count = 0; + + pthread_mutex_unlock(&m_omx_output_mutex); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::DisableAllPorts() +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + if(!m_handle) + { + UnLock(); + return OMX_ErrorUndefined; + } + + OMX_INDEXTYPE idxTypes[] = { + OMX_IndexParamAudioInit, + OMX_IndexParamImageInit, + OMX_IndexParamVideoInit, + OMX_IndexParamOtherInit + }; + + OMX_PORT_PARAM_TYPE ports; + OMX_INIT_STRUCTURE(ports); + + int i; + for(i=0; i < 4; i++) + { + omx_err = OMX_GetParameter(m_handle, idxTypes[i], &ports); + if(omx_err == OMX_ErrorNone) { + + uint32_t j; + for(j=0; j::iterator it = m_omx_events.begin(); it != m_omx_events.end(); ) + { + omx_event event = *it; + + if(event.eEvent == eEvent && event.nData1 == nData1 && event.nData2 == nData2) + { + it = m_omx_events.erase(it); + continue; + } + ++it; + } +} + +OMX_ERRORTYPE COMXCoreComponent::AddEvent(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2) +{ + omx_event event; + + event.eEvent = eEvent; + event.nData1 = nData1; + event.nData2 = nData2; + + pthread_mutex_lock(&m_omx_event_mutex); + Remove(eEvent, nData1, nData2); + m_omx_events.push_back(event); + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_omx_event_cond); + pthread_mutex_unlock(&m_omx_event_mutex); + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::AddEvent %s add event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + return OMX_ErrorNone; +} + +// timeout in milliseconds +OMX_ERRORTYPE COMXCoreComponent::WaitForEvent(OMX_EVENTTYPE eventType, long timeout) +{ +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s wait event 0x%08x\n", + m_componentName.c_str(), (int)eventType); +#endif + + pthread_mutex_lock(&m_omx_event_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while(true) + { + for (std::vector::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) + { + omx_event event = *it; + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s inlist event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + + if(event.eEvent == OMX_EventError && event.nData1 == (OMX_U32)OMX_ErrorSameState && event.nData2 == 1) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + else if(event.eEvent == OMX_EventError) + { + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return (OMX_ERRORTYPE)event.nData1; + } + else if(event.eEvent == eventType) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForEvent %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + } + + int retcode = pthread_cond_timedwait(&m_omx_event_cond, &m_omx_event_mutex, &endtime); + if (retcode != 0) + { + CLog::Log(LOGERROR, "COMXCoreComponent::WaitForEvent %s wait event 0x%08x timeout %ld\n", + m_componentName.c_str(), (int)eventType, timeout); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorMax; + } + } + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; +} + +// timeout in milliseconds +OMX_ERRORTYPE COMXCoreComponent::WaitForCommand(OMX_U32 command, OMX_U32 nData2, long timeout) +{ +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s wait event.eEvent 0x%08x event.command 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)OMX_EventCmdComplete, (int)command, (int)nData2); +#endif + + pthread_mutex_lock(&m_omx_event_mutex); + struct timespec endtime; + clock_gettime(CLOCK_REALTIME, &endtime); + add_timespecs(endtime, timeout); + while(true) + { + for (std::vector::iterator it = m_omx_events.begin(); it != m_omx_events.end(); it++) + { + omx_event event = *it; + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s inlist event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + if(event.eEvent == OMX_EventError && event.nData1 == (OMX_U32)OMX_ErrorSameState && event.nData2 == 1) + { +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + else if(event.eEvent == OMX_EventError) + { + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return (OMX_ERRORTYPE)event.nData1; + } + else if(event.eEvent == OMX_EventCmdComplete && event.nData1 == command && event.nData2 == nData2) + { + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, "COMXCoreComponent::WaitForCommand %s remove event event.eEvent 0x%08x event.nData1 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)event.eEvent, (int)event.nData1, (int)event.nData2); +#endif + + m_omx_events.erase(it); + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; + } + } + + int retcode = pthread_cond_timedwait(&m_omx_event_cond, &m_omx_event_mutex, &endtime); + if (retcode != 0) { + CLog::Log(LOGERROR, "COMXCoreComponent::WaitForCommand %s wait timeout event.eEvent 0x%08x event.command 0x%08x event.nData2 %d\n", + m_componentName.c_str(), (int)OMX_EventCmdComplete, (int)command, (int)nData2); + + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorMax; + } + } + pthread_mutex_unlock(&m_omx_event_mutex); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreComponent::SetStateForComponent(OMX_STATETYPE state) +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + OMX_STATETYPE state_actual = OMX_StateMax; + + if(!m_handle) + { + UnLock(); + return OMX_ErrorUndefined; + } + + OMX_GetState(m_handle, &state_actual); + if(state == state_actual) + { + UnLock(); + return OMX_ErrorNone; + } + + omx_err = OMX_SendCommand(m_handle, OMX_CommandStateSet, state, 0); + if (omx_err != OMX_ErrorNone) + { + if(omx_err == OMX_ErrorSameState) + { + omx_err = OMX_ErrorNone; + } + else + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetStateForComponent - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + } + else + { + omx_err = WaitForCommand(OMX_CommandStateSet, state); + if(omx_err == OMX_ErrorSameState) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetStateForComponent - %s ignore OMX_ErrorSameState\n", + m_componentName.c_str()); + UnLock(); + return OMX_ErrorNone; + } + } + + UnLock(); + + return omx_err; +} + +OMX_STATETYPE COMXCoreComponent::GetState() +{ + Lock(); + + OMX_STATETYPE state; + + if(m_handle) + { + OMX_GetState(m_handle, &state); + UnLock(); + return state; + } + + UnLock(); + + return (OMX_STATETYPE)0; +} + +OMX_ERRORTYPE COMXCoreComponent::SetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SetParameter(m_handle, paramIndex, paramStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetParameter - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::GetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_GetParameter(m_handle, paramIndex, paramStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::GetParameter - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::SetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SetConfig(m_handle, configIndex, configStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SetConfig - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::GetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_GetConfig(m_handle, configIndex, configStruct); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::GetConfig - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::SendCommand(OMX_COMMANDTYPE cmd, OMX_U32 cmdParam, OMX_PTR cmdParamData) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_SendCommand(m_handle, cmd, cmdParam, cmdParamData); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::SendCommand - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::EnablePort(unsigned int port, bool wait) +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::EnablePort - Error get port %d status on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + } + + if(portFormat.bEnabled == OMX_FALSE) + { + omx_err = OMX_SendCommand(m_handle, OMX_CommandPortEnable, port, NULL); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::EnablePort - Error enable port %d on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + { + UnLock(); + return omx_err; + } + } + else + { + if(wait) + omx_err = WaitForCommand(OMX_CommandPortEnable, port); + } + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::DisablePort(unsigned int port, bool wait) +{ + Lock(); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; + + OMX_PARAM_PORTDEFINITIONTYPE portFormat; + OMX_INIT_STRUCTURE(portFormat); + portFormat.nPortIndex = port; + + omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::DisablePort - Error get port %d status on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + } + + if(portFormat.bEnabled == OMX_TRUE) + { + omx_err = OMX_SendCommand(m_handle, OMX_CommandPortDisable, port, NULL); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::DIsablePort - Error disable port %d on component %s omx_err(0x%08x)", + port, m_componentName.c_str(), (int)omx_err); + { + UnLock(); + return omx_err; + } + } + else + { + if(wait) + omx_err = WaitForCommand(OMX_CommandPortDisable, port); + } + } + + UnLock(); + + return omx_err; +} + +OMX_ERRORTYPE COMXCoreComponent::UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage) +{ + Lock(); + + OMX_ERRORTYPE omx_err; + + omx_err = OMX_UseEGLImage(m_handle, ppBufferHdr, nPortIndex, pAppPrivate, eglImage); + if(omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::UseEGLImage - %s failed with omx_err(0x%x)\n", + m_componentName.c_str(), omx_err); + } + + UnLock(); + + return omx_err; +} + +bool COMXCoreComponent::Initialize( const std::string &component_name, OMX_INDEXTYPE index) +{ + OMX_ERRORTYPE omx_err; + + if(!m_DllOMX->Load()) + return false; + + m_DllOMXOpen = true; + + m_componentName = component_name; + + m_callbacks.EventHandler = &COMXCoreComponent::DecoderEventHandlerCallback; + m_callbacks.EmptyBufferDone = &COMXCoreComponent::DecoderEmptyBufferDoneCallback; + m_callbacks.FillBufferDone = &COMXCoreComponent::DecoderFillBufferDoneCallback; + + // Get video component handle setting up callbacks, component is in loaded state on return. + omx_err = m_DllOMX->OMX_GetHandle(&m_handle, (char*)component_name.c_str(), this, &m_callbacks); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - could not get component handle for %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + Deinitialize(); + return false; + } + + OMX_PORT_PARAM_TYPE port_param; + OMX_INIT_STRUCTURE(port_param); + + omx_err = OMX_GetParameter(m_handle, index, &port_param); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - could not get port_param for component %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + } + + omx_err = DisableAllPorts(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Initialize - error disable ports on component %s omx_err(0x%08x)\n", + component_name.c_str(), (int)omx_err); + } + + m_input_port = port_param.nStartPortNumber; + m_output_port = m_input_port + 1; + + if(m_componentName == "OMX.broadcom.audio_mixer") + { + m_input_port = port_param.nStartPortNumber + 1; + m_output_port = port_param.nStartPortNumber; + } + + if (m_output_port > port_param.nStartPortNumber+port_param.nPorts-1) + m_output_port = port_param.nStartPortNumber+port_param.nPorts-1; + + CLog::Log(LOGDEBUG, "COMXCoreComponent::Initialize %s input port %d output port %d\n", + m_componentName.c_str(), m_input_port, m_output_port); + + m_exit = false; + m_flush_input = false; + m_flush_output = false; + + return true; +} + +bool COMXCoreComponent::Deinitialize() +{ + OMX_ERRORTYPE omx_err; + + if(!m_DllOMXOpen) + return false; + + m_exit = true; + + m_flush_input = true; + m_flush_output = true; + + if(m_handle) + { + + FlushAll(); + + FreeOutputBuffers(true); + FreeInputBuffers(true); + + if(GetState() == OMX_StateExecuting) + SetStateForComponent(OMX_StatePause); + + if(GetState() != OMX_StateIdle) + SetStateForComponent(OMX_StateIdle); + + if(GetState() != OMX_StateLoaded) + SetStateForComponent(OMX_StateLoaded); + + omx_err = m_DllOMX->OMX_FreeHandle(m_handle); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCoreComponent::Deinitialize - failed to free handle for component %s omx_err(0x%08x)", + m_componentName.c_str(), omx_err); + } + + m_handle = NULL; + } + + m_input_port = 0; + m_output_port = 0; + m_componentName = ""; + m_DllOMXOpen = false; + + m_DllOMX->Unload(); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// DecoderEventHandler -- OMX event callback +OMX_ERRORTYPE COMXCoreComponent::DecoderEventHandlerCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderEventHandler(hComponent, pAppData, eEvent, nData1, nData2, pEventData); +} + +// DecoderEmptyBufferDone -- OMXCore input buffer has been emptied +OMX_ERRORTYPE COMXCoreComponent::DecoderEmptyBufferDoneCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderEmptyBufferDone( hComponent, pAppData, pBuffer); +} + +// DecoderFillBufferDone -- OMXCore output buffer has been filled +OMX_ERRORTYPE COMXCoreComponent::DecoderFillBufferDoneCallback( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData) + return OMX_ErrorNone; + + COMXCoreComponent *comp = static_cast(pAppData); + return comp->DecoderFillBufferDone(hComponent, pAppData, pBuffer); +} + +OMX_ERRORTYPE COMXCoreComponent::DecoderEmptyBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData || m_exit) + return OMX_ErrorNone; + + COMXCoreComponent *ctx = static_cast(pAppData); + + pthread_mutex_lock(&ctx->m_omx_input_mutex); + ctx->m_omx_input_avaliable.push(pBuffer); + + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_input_buffer_cond); + + pthread_mutex_unlock(&ctx->m_omx_input_mutex); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE COMXCoreComponent::DecoderFillBufferDone(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + if(!pAppData || m_exit) + return OMX_ErrorNone; + + COMXCoreComponent *ctx = static_cast(pAppData); + + pthread_mutex_lock(&ctx->m_omx_output_mutex); + ctx->m_omx_output_available.push(pBuffer); + + // this allows (all) blocked tasks to be awoken + pthread_cond_broadcast(&m_output_buffer_cond); + + pthread_mutex_unlock(&ctx->m_omx_output_mutex); + + sem_post(&ctx->m_omx_fill_buffer_done); + + return OMX_ErrorNone; +} + +// DecoderEmptyBufferDone -- OMXCore input buffer has been emptied +//////////////////////////////////////////////////////////////////////////////////////////// +// Component event handler -- OMX event callback +OMX_ERRORTYPE COMXCoreComponent::DecoderEventHandler( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData) +{ + COMXCoreComponent *comp = static_cast(pAppData); + +#ifdef OMX_DEBUG_EVENTS + CLog::Log(LOGDEBUG, + "COMXCore::%s - %s eEvent(0x%x), nData1(0x%lx), nData2(0x%lx), pEventData(0x%p)\n", + __func__, (char *)m_componentName.c_str(), eEvent, nData1, nData2, pEventData); +#endif + + AddEvent(eEvent, nData1, nData2); + + switch (eEvent) + { + case OMX_EventCmdComplete: + + switch(nData1) + { + case OMX_CommandStateSet: + switch ((int)nData2) + { + case OMX_StateInvalid: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateInvalid\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateLoaded: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateLoaded\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateIdle: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateIdle\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateExecuting: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateExecuting\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StatePause: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StatePause\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + case OMX_StateWaitForResources: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_StateWaitForResources\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + default: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, + "%s::%s %s - Unknown OMX_Statexxxxx, state(%d)\n", CLASSNAME, __func__, comp->GetName().c_str(), (int)nData2); + #endif + break; + } + break; + case OMX_CommandFlush: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandFlush, port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), (int)nData2); + #endif + break; + case OMX_CommandPortDisable: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandPortDisable, nData1(0x%lx), port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, (int)nData2); + #endif + break; + case OMX_CommandPortEnable: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandPortEnable, nData1(0x%lx), port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, (int)nData2); + #endif + break; + #if defined(OMX_DEBUG_EVENTHANDLER) + case OMX_CommandMarkBuffer: + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_CommandMarkBuffer, nData1(0x%lx), port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, (int)nData2); + break; + #endif + } + break; + case OMX_EventBufferFlag: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventBufferFlag(input)\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + switch(nData2) + { + case OMX_BUFFERFLAG_EOS: + m_eos = true; + break; + default: + break; + } + break; + case OMX_EventPortSettingsChanged: + #if defined(OMX_DEBUG_EVENTHANDLER) + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventPortSettingsChanged(output)\n", CLASSNAME, __func__, comp->GetName().c_str()); + #endif + break; + #if defined(OMX_DEBUG_EVENTHANDLER) + case OMX_EventMark: + CLog::Log(LOGDEBUG, "%s::%s %s - OMX_EventMark\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_EventResourcesAcquired: + CLog::Log(LOGDEBUG, "%s::%s %s- OMX_EventResourcesAcquired\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + #endif + case OMX_EventError: + switch((OMX_S32)nData1) + { + case OMX_ErrorSameState: + break; + case OMX_ErrorInsufficientResources: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorInsufficientResources, insufficient resources\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_ErrorFormatNotDetected: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorFormatNotDetected, cannot parse input stream\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + case OMX_ErrorPortUnpopulated: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorPortUnpopulated port %d, cannot parse input stream\n", CLASSNAME, __func__, comp->GetName().c_str(), (int)nData2); + break; + case OMX_ErrorStreamCorrupt: + CLog::Log(LOGERROR, "%s::%s %s - OMX_ErrorStreamCorrupt, Bitstream corrupt\n", CLASSNAME, __func__, comp->GetName().c_str()); + break; + default: + CLog::Log(LOGERROR, "%s::%s %s - OMX_EventError detected, nData1(0x%lx), port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), nData1, (int)nData2); + break; + } + sem_post(&comp->m_omx_fill_buffer_done); + break; + default: + CLog::Log(LOGWARNING, "%s::%s %s - Unknown eEvent(0x%x), nData1(0x%lx), port %d\n", CLASSNAME, __func__, comp->GetName().c_str(), eEvent, nData1, (int)nData2); + break; + } + + return OMX_ErrorNone; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +COMXCore::COMXCore() +{ + m_is_open = false; + + m_DllOMX = new DllOMX(); +} + +COMXCore::~COMXCore() +{ + delete m_DllOMX; +} + +bool COMXCore::Initialize() +{ + if(!m_DllOMX->Load()) + return false; + + OMX_ERRORTYPE omx_err = m_DllOMX->OMX_Init(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCore::Initialize - OMXCore failed to init, omx_err(0x%08x)", omx_err); + return false; + } + + m_is_open = true; + return true; +} + +void COMXCore::Deinitialize() +{ + if(m_is_open) + { + OMX_ERRORTYPE omx_err = m_DllOMX->OMX_Deinit(); + if (omx_err != OMX_ErrorNone) + { + CLog::Log(LOGERROR, "COMXCore::Deinitialize - OMXCore failed to deinit, omx_err(0x%08x)", omx_err); + } + m_DllOMX->Unload(); + } +} + +#endif diff --git a/xbmc/linux/OMXCore.h b/xbmc/linux/OMXCore.h new file mode 100644 index 0000000000000..c390b2662d74c --- /dev/null +++ b/xbmc/linux/OMXCore.h @@ -0,0 +1,222 @@ +#pragma once +/* + * Copyright (C) 2010 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(HAVE_OMXLIB) + +#include +#include + +// TODO: should this be in configure +#ifndef OMX_SKIP64BIT +#define OMX_SKIP64BIT +#endif + +#include "DllOMX.h" + +#include + +//////////////////////////////////////////////////////////////////////////////////////////// +// debug spew defines +#if 0 +#define OMX_DEBUG_VERBOSE +#define OMX_DEBUG_EVENTHANDLER +#endif + +#define OMX_INIT_STRUCTURE(a) \ + memset(&(a), 0, sizeof(a)); \ + (a).nSize = sizeof(a); \ + (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (a).nVersion.s.nStep = OMX_VERSION_STEP + +#include "DllAvFormat.h" + +#define OMX_MAX_PORTS 10 + +typedef struct omx_event { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; +} omx_event; + +class DllLibOMXCore; +class COMXCore; +class COMXCoreComponent; +class COMXCoreTunel; +class COMXCoreClock; + +class COMXCoreTunel +{ +public: + COMXCoreTunel(); + ~COMXCoreTunel(); + + void Initialize(COMXCoreComponent *src_component, unsigned int src_port, COMXCoreComponent *dst_component, unsigned int dst_port); + OMX_ERRORTYPE Flush(); + OMX_ERRORTYPE Deestablish(bool noWait = false); + OMX_ERRORTYPE Establish(bool portSettingsChanged); +private: + pthread_mutex_t m_lock; + bool m_portSettingsChanged; + COMXCoreComponent *m_src_component; + COMXCoreComponent *m_dst_component; + unsigned int m_src_port; + unsigned int m_dst_port; + DllOMX *m_DllOMX; + bool m_DllOMXOpen; + void Lock(); + void UnLock(); +}; + +class COMXCoreComponent +{ +public: + COMXCoreComponent(); + ~COMXCoreComponent(); + + OMX_HANDLETYPE GetComponent() { return m_handle; }; + unsigned int GetInputPort() { return m_input_port; }; + unsigned int GetOutputPort() { return m_output_port; }; + std::string GetName() { return m_componentName; }; + + OMX_ERRORTYPE DisableAllPorts(); + void Remove(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2); + OMX_ERRORTYPE AddEvent(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2); + OMX_ERRORTYPE WaitForEvent(OMX_EVENTTYPE event, long timeout = 300); + OMX_ERRORTYPE WaitForCommand(OMX_U32 command, OMX_U32 nData2, long timeout = 2000); + OMX_ERRORTYPE SetStateForComponent(OMX_STATETYPE state); + OMX_STATETYPE GetState(); + OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct); + OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE paramIndex, OMX_PTR paramStruct); + OMX_ERRORTYPE SetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct); + OMX_ERRORTYPE GetConfig(OMX_INDEXTYPE configIndex, OMX_PTR configStruct); + OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE cmd, OMX_U32 cmdParam, OMX_PTR cmdParamData); + OMX_ERRORTYPE EnablePort(unsigned int port, bool wait = true); + OMX_ERRORTYPE DisablePort(unsigned int port, bool wait = true); + OMX_ERRORTYPE UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage); + + bool Initialize( const std::string &component_name, OMX_INDEXTYPE index); + bool Deinitialize(); + + // OMXCore Decoder delegate callback routines. + static OMX_ERRORTYPE DecoderEventHandlerCallback(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); + static OMX_ERRORTYPE DecoderEmptyBufferDoneCallback( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + static OMX_ERRORTYPE DecoderFillBufferDoneCallback( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBufferHeader); + + // OMXCore decoder callback routines. + OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); + OMX_ERRORTYPE DecoderEmptyBufferDone( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + OMX_ERRORTYPE DecoderFillBufferDone( + OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); + + OMX_ERRORTYPE EmptyThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + OMX_ERRORTYPE FillThisBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + OMX_ERRORTYPE FreeOutputBuffer(OMX_BUFFERHEADERTYPE *omx_buffer); + + unsigned int GetInputBufferSize(); + unsigned int GetOutputBufferSize(); + + unsigned int GetInputBufferSpace(); + unsigned int GetOutputBufferSpace(); + + void FlushAll(); + void FlushInput(); + void FlushOutput(); + + OMX_BUFFERHEADERTYPE *GetInputBuffer(long timeout=200); + OMX_BUFFERHEADERTYPE *GetOutputBuffer(); + + OMX_ERRORTYPE AllocInputBuffers(bool use_buffers = false); + OMX_ERRORTYPE AllocOutputBuffers(bool use_buffers = false); + + OMX_ERRORTYPE FreeInputBuffers(bool wait); + OMX_ERRORTYPE FreeOutputBuffers(bool wait); + + bool IsEOS() { return m_eos; }; + +private: + OMX_HANDLETYPE m_handle; + unsigned int m_input_port; + unsigned int m_output_port; + std::string m_componentName; + pthread_mutex_t m_omx_event_mutex; + pthread_mutex_t m_lock; + std::vector m_omx_events; + + OMX_CALLBACKTYPE m_callbacks; + + // OMXCore input buffers (demuxer packets) + pthread_mutex_t m_omx_input_mutex; + std::queue m_omx_input_avaliable; + std::vector m_omx_input_buffers; + unsigned int m_input_alignment; + unsigned int m_input_buffer_size; + unsigned int m_input_buffer_count; + bool m_omx_input_use_buffers; + + // OMXCore output buffers (video frames) + pthread_mutex_t m_omx_output_mutex; + std::queue m_omx_output_available; + std::vector m_omx_output_buffers; + unsigned int m_output_alignment; + unsigned int m_output_buffer_size; + unsigned int m_output_buffer_count; + bool m_omx_output_use_buffers; + sem_t m_omx_fill_buffer_done; + + bool m_exit; + DllOMX *m_DllOMX; + bool m_DllOMXOpen; + pthread_cond_t m_input_buffer_cond; + pthread_cond_t m_output_buffer_cond; + pthread_cond_t m_omx_event_cond; + bool m_eos; + bool m_flush_input; + bool m_flush_output; + void Lock(); + void UnLock(); +}; + +class COMXCore +{ +public: + COMXCore(); + ~COMXCore(); + + // initialize OMXCore and get decoder component + bool Initialize(); + void Deinitialize(); + +protected: + bool m_is_open; + bool m_Initialized; + DllOMX *m_DllOMX; +}; + +#endif + diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp new file mode 100644 index 0000000000000..3a07d1cae710f --- /dev/null +++ b/xbmc/linux/RBP.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "RBP.h" +#if defined(TARGET_RASPBERRY_PI) + +#include "utils/log.h" + +CRBP::CRBP() +{ + m_initialized = false; + m_omx_initialized = false; + m_DllBcmHost = new DllBcmHost(); + m_OMX = new COMXCore(); +} + +CRBP::~CRBP() +{ + Deinitialize(); + delete m_OMX; + delete m_DllBcmHost; +} + +bool CRBP::Initialize() +{ + m_initialized = m_DllBcmHost->Load(); + if(!m_initialized) + return false; + + m_DllBcmHost->bcm_host_init(); + + m_omx_initialized = m_OMX->Initialize(); + if(!m_omx_initialized) + return false; + + return true; +} + +void CRBP::LogFirmwareVerison() +{ + char response[80]; + m_DllBcmHost->vc_gencmd(response, sizeof response, "version"); + CLog::Log(LOGNOTICE, "Raspberry PI firmware version: %s\n", response); +} + +void CRBP::Deinitialize() +{ + if(m_omx_initialized) + m_OMX->Deinitialize(); + + m_DllBcmHost->bcm_host_deinit(); + + if(m_initialized) + m_DllBcmHost->Unload(); + + m_initialized = false; + m_omx_initialized = false; +} +#endif diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h new file mode 100644 index 0000000000000..8d5b8a1479690 --- /dev/null +++ b/xbmc/linux/RBP.h @@ -0,0 +1,60 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef USE_VCHIQ_ARM +#define USE_VCHIQ_ARM +#endif +#ifndef __VIDEOCORE4__ +#define __VIDEOCORE4__ +#endif +#ifndef HAVE_VMCS_CONFIG +#define HAVE_VMCS_CONFIG +#endif + +#if defined(HAVE_CONFIG_H) && !defined(TARGET_WINDOWS) +#include "config.h" +#define DECLARE_UNUSED(a,b) a __attribute__((unused)) b; +#endif + +#if defined(TARGET_RASPBERRY_PI) +#include "DllBCM.h" +#include "OMXCore.h" + +class CRBP +{ +public: + CRBP(); + ~CRBP(); + + bool Initialize(); + void LogFirmwareVerison(); + void Deinitialize(); + +private: + DllBcmHost *m_DllBcmHost; + bool m_initialized; + bool m_omx_initialized; + COMXCore *m_OMX; +}; + +extern CRBP g_RBP; +#endif diff --git a/xbmc/peripherals/PeripheralTypes.h b/xbmc/peripherals/PeripheralTypes.h index 53831fc3f70cd..75dd3dabbec04 100644 --- a/xbmc/peripherals/PeripheralTypes.h +++ b/xbmc/peripherals/PeripheralTypes.h @@ -35,7 +35,8 @@ namespace PERIPHERALS { PERIPHERAL_BUS_UNKNOWN = 0, PERIPHERAL_BUS_USB, - PERIPHERAL_BUS_PCI + PERIPHERAL_BUS_PCI, + PERIPHERAL_BUS_RPI }; enum PeripheralFeature @@ -135,6 +136,8 @@ namespace PERIPHERALS return "usb"; case PERIPHERAL_BUS_PCI: return "pci"; + case PERIPHERAL_BUS_RPI: + return "rpi"; default: return "unknown"; } @@ -149,6 +152,8 @@ namespace PERIPHERALS return PERIPHERAL_BUS_USB; else if (strTypeLowerCase.Equals("pci")) return PERIPHERAL_BUS_PCI; + else if (strTypeLowerCase.Equals("rpi")) + return PERIPHERAL_BUS_RPI; return PERIPHERAL_BUS_UNKNOWN; }; diff --git a/xbmc/peripherals/Peripherals.cpp b/xbmc/peripherals/Peripherals.cpp index ed08632ef5d7f..07ac846baf0a9 100644 --- a/xbmc/peripherals/Peripherals.cpp +++ b/xbmc/peripherals/Peripherals.cpp @@ -31,6 +31,10 @@ #include "bus/PeripheralBusUSB.h" #include "dialogs/GUIDialogPeripheralManager.h" +#ifdef HAVE_CEC_RPI_API +#include "bus/linux/PeripheralBusRPi.h" +#endif + #include "threads/SingleLock.h" #include "utils/log.h" #include "utils/XMLUtils.h" @@ -80,6 +84,9 @@ void CPeripherals::Initialise(void) #if defined(HAVE_PERIPHERAL_BUS_USB) m_busses.push_back(new CPeripheralBusUSB(this)); #endif +#ifdef HAVE_CEC_RPI_API + m_busses.push_back(new CPeripheralBusRPi(this)); +#endif /* initialise all known busses */ for (int iBusPtr = (int)m_busses.size() - 1; iBusPtr >= 0; iBusPtr--) @@ -138,9 +145,8 @@ void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERA CPeripheralBus *CPeripherals::GetBusByType(const PeripheralBusType type) const { - CPeripheralBus *bus(NULL); - CSingleLock lock(m_critSection); + CPeripheralBus *bus(NULL); for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++) { if (m_busses.at(iBusPtr)->Type() == type) @@ -155,8 +161,8 @@ CPeripheralBus *CPeripherals::GetBusByType(const PeripheralBusType type) const CPeripheral *CPeripherals::GetPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const { - CPeripheral *peripheral(NULL); CSingleLock lock(m_critSection); + CPeripheral *peripheral(NULL); for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++) { /* check whether the bus matches if a bus type other than unknown was passed */ @@ -191,8 +197,8 @@ CPeripheralBus *CPeripherals::GetBusWithDevice(const CStdString &strLocation) co int CPeripherals::GetPeripheralsWithFeature(vector &results, const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const { - int iReturn(0); CSingleLock lock(m_critSection); + int iReturn(0); for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++) { /* check whether the bus matches if a bus type other than unknown was passed */ diff --git a/xbmc/peripherals/bus/Makefile.in b/xbmc/peripherals/bus/Makefile.in index 2595a9cc97fc0..ed62c99b10430 100644 --- a/xbmc/peripherals/bus/Makefile.in +++ b/xbmc/peripherals/bus/Makefile.in @@ -12,6 +12,10 @@ ifeq ($(findstring osx,@ARCH@),osx) SRCS+=osx/PeripheralBusUSB.cpp endif +ifeq (@USE_CEC_RPI_API@,1) +SRCS+=linux/PeripheralBusRPi.cpp +endif + LIB=peripheral-bus.a include ../../../Makefile.include diff --git a/xbmc/peripherals/bus/linux/PeripheralBusRPi.cpp b/xbmc/peripherals/bus/linux/PeripheralBusRPi.cpp new file mode 100644 index 0000000000000..3cbc6e2d4928c --- /dev/null +++ b/xbmc/peripherals/bus/linux/PeripheralBusRPi.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PeripheralBusRPi.h" + +extern "C" { +#include +#include +} + +using namespace PERIPHERALS; + +#define RPI_PERIPHERAL_BUS_VID 0x2708 +#define RPI_PERIPHERAL_CEC_PID 0x1001 + +CPeripheralBusRPi::CPeripheralBusRPi(CPeripherals *manager) : + CPeripheralBus(manager, PERIPHERAL_BUS_RPI) +{ + m_bNeedsPolling = false; +} + +bool CPeripheralBusRPi::PerformDeviceScan(PeripheralScanResults &results) +{ + if (FindAdapter()) + { + PeripheralScanResult result; + result.m_iVendorId = RPI_PERIPHERAL_BUS_VID; + result.m_iProductId = RPI_PERIPHERAL_CEC_PID; + result.m_type = PERIPHERAL_CEC; + result.m_strLocation = "RPI/CEC"; + + if (!results.ContainsResult(result)) + results.m_results.push_back(result); + } + + return true; +} + +bool CPeripheralBusRPi::FindAdapter(void) +{ + uint8_t iResult; + + VCHI_INSTANCE_T vchiq_instance; + if ((iResult = vchi_initialise(&vchiq_instance)) != VCHIQ_SUCCESS) + return false; + + if ((iResult = vchi_connect(NULL, 0, vchiq_instance)) != VCHIQ_SUCCESS) + return false; + + return true; +} diff --git a/xbmc/peripherals/bus/linux/PeripheralBusRPi.h b/xbmc/peripherals/bus/linux/PeripheralBusRPi.h new file mode 100644 index 0000000000000..f2b887788924f --- /dev/null +++ b/xbmc/peripherals/bus/linux/PeripheralBusRPi.h @@ -0,0 +1,41 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "peripherals/bus/PeripheralBus.h" +#include "peripherals/devices/Peripheral.h" + +namespace PERIPHERALS +{ + class CPeripherals; + + class CPeripheralBusRPi : public CPeripheralBus + { + public: + CPeripheralBusRPi(CPeripherals *manager); + virtual ~CPeripheralBusRPi(void) {}; + + bool PerformDeviceScan(PeripheralScanResults &results); + + private: + bool FindAdapter(void); + }; +} diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp index 0b7cf83d57e42..34d4c1a9ff996 100644 --- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp +++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp @@ -1198,7 +1198,9 @@ int CPeripheralCecAdapter::CecLogMessage(void *cbParam, const cec_log_message &m bool CPeripheralCecAdapter::TranslateComPort(CStdString &strLocation) { - if (strLocation.Left(18).Equals("peripherals://usb/") && strLocation.Right(4).Equals(".dev")) + if ((strLocation.Left(18).Equals("peripherals://usb/") || + strLocation.Left(18).Equals("peripherals://rpi/")) && + strLocation.Right(4).Equals(".dev")) { strLocation = strLocation.Right(strLocation.length() - 18); strLocation = strLocation.Left(strLocation.length() - 4); diff --git a/xbmc/pictures/Picture.cpp b/xbmc/pictures/Picture.cpp index 0980efb79e9ad..88a2abf9ce314 100644 --- a/xbmc/pictures/Picture.cpp +++ b/xbmc/pictures/Picture.cpp @@ -19,6 +19,11 @@ * */ +#include "system.h" +#if (defined HAVE_CONFIG_H) && (!defined WIN32) + #include "config.h" +#endif + #include "Picture.h" #include "settings/AdvancedSettings.h" #include "settings/GUISettings.h" @@ -30,14 +35,22 @@ #include "DllSwScale.h" #include "guilib/JpegIO.h" #include "guilib/Texture.h" +#if defined(TARGET_RASPBERRY_PI) +#include "cores/omxplayer/OMXImage.h" +#endif using namespace XFILE; bool CPicture::CreateThumbnailFromSurface(const unsigned char *buffer, int width, int height, int stride, const CStdString &thumbFile) { CLog::Log(LOGDEBUG, "cached image '%s' size %dx%d", thumbFile.c_str(), width, height); - if (URIUtils::GetExtension(thumbFile).Equals(".jpg")) + if (URIUtils::GetExtension(thumbFile).Equals(".jpg") || URIUtils::GetExtension(thumbFile).Equals(".tbn")) { +#if defined(TARGET_RASPBERRY_PI) + COMXImage omxImage; + if (omxImage.CreateThumbnailFromSurface((BYTE *)buffer, width, height, XB_FMT_A8R8G8B8, stride, thumbFile.c_str())) + return true; +#endif CJpegIO jpegImage; if (jpegImage.CreateThumbnailFromSurface((BYTE *)buffer, width, height, XB_FMT_A8R8G8B8, stride, thumbFile.c_str())) return true; @@ -104,6 +117,7 @@ bool CPicture::CacheTexture(uint8_t *pixels, uint32_t width, uint32_t height, ui dest_width = std::min(width, dest_width); dest_height = std::min(height, dest_height); + // create a buffer large enough for the resulting image GetScale(width, height, dest_width, dest_height); uint32_t *buffer = new uint32_t[dest_width * dest_height]; diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp index da952ba1ec330..d9ea6d5f7310a 100644 --- a/xbmc/rendering/gles/RenderSystemGLES.cpp +++ b/xbmc/rendering/gles/RenderSystemGLES.cpp @@ -139,7 +139,11 @@ bool CRenderSystemGLES::ResetRenderSystem(int width, int height, bool fullScreen g_matrices.MatrixMode(MM_PROJECTION); g_matrices.LoadIdentity(); +#ifdef TARGET_RASPBERRY_PI + g_matrices.Ortho(0.0f, width-1, height-1, 0.0f, +1.0f, 1.0f); +#else g_matrices.Ortho(0.0f, width-1, height-1, 0.0f, -1.0f, 1.0f); +#endif g_matrices.MatrixMode(MM_MODELVIEW); g_matrices.LoadIdentity(); diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 9726cf3a3f953..22dcd90d4a64d 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -62,6 +62,9 @@ void CAdvancedSettings::Initialize() m_limiterHold = 0.025f; m_limiterRelease = 0.1f; + m_omxHWAudioDecode = false; + m_omxDecodeStartWithValidFrame = false; + m_karaokeSyncDelayCDG = 0.0f; m_karaokeSyncDelayLRC = 0.0f; m_karaokeChangeGenreForKaraokeSongs = false; @@ -407,6 +410,13 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetFloat(pElement, "limiterrelease", m_limiterRelease, 0.001f, 100.0f); } + pElement = pRootElement->FirstChildElement("omx"); + if (pElement) + { + XMLUtils::GetBoolean(pElement, "omxhwaudiodecode", m_omxHWAudioDecode); + XMLUtils::GetBoolean(pElement, "omxdecodestartwithvalidframe", m_omxDecodeStartWithValidFrame); + } + pElement = pRootElement->FirstChildElement("karaoke"); if (pElement) { diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index e777764c53a7b..89acbc326551e 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -115,6 +115,9 @@ class CAdvancedSettings float m_limiterHold; float m_limiterRelease; + bool m_omxHWAudioDecode; + bool m_omxDecodeStartWithValidFrame; + float m_videoSubsDelayRange; float m_videoAudioDelayRange; int m_videoSmallStepBackSeconds; diff --git a/xbmc/settings/GUISettings.cpp b/xbmc/settings/GUISettings.cpp index 8edd76c9bfc3c..c8b8129e88b42 100644 --- a/xbmc/settings/GUISettings.cpp +++ b/xbmc/settings/GUISettings.cpp @@ -395,7 +395,7 @@ void CGUISettings::Initialize() // this setting would ideally not be saved, as its value is systematically derived from videoscreen.screenmode. // contains a DISPLAYMODE -#if !defined(TARGET_DARWIN_IOS_ATV2) +#if !defined(TARGET_DARWIN_IOS_ATV2) && !defined(TARGET_RASPBERRY_PI) AddInt(vs, "videoscreen.screen", 240, 0, -1, 1, 32, SPIN_CONTROL_TEXT); #endif // this setting would ideally not be saved, as its value is systematically derived from videoscreen.screenmode. @@ -458,9 +458,15 @@ void CGUISettings::Initialize() map audiomode; audiomode.insert(make_pair(338,AUDIO_ANALOG)); +#if !defined(TARGET_RASPBERRY_PI) audiomode.insert(make_pair(339,AUDIO_IEC958)); +#endif audiomode.insert(make_pair(420,AUDIO_HDMI )); +#if defined(TARGET_RASPBERRY_PI) + AddInt(ao, "audiooutput.mode", 337, AUDIO_HDMI, audiomode, SPIN_CONTROL_TEXT); +#else AddInt(ao, "audiooutput.mode", 337, AUDIO_ANALOG, audiomode, SPIN_CONTROL_TEXT); +#endif map channelLayout; for(int layout = AE_CH_LAYOUT_2_0; layout < AE_CH_LAYOUT_MAX; ++layout) @@ -479,17 +485,18 @@ void CGUISettings::Initialize() AddBool(aocat, "audiooutput.dtspassthrough" , 254, true); -#if !defined(TARGET_DARWIN) +#if !defined(TARGET_DARWIN) && !defined(TARGET_RASPBERRY_PI) AddBool(aocat, "audiooutput.passthroughaac" , 299, false); #endif -#if !defined(TARGET_DARWIN_IOS) +#if !defined(TARGET_DARWIN_IOS) && !defined(TARGET_RASPBERRY_PI) AddBool(aocat, "audiooutput.multichannellpcm" , 348, true ); #endif -#if !defined(TARGET_DARWIN) +#if !defined(TARGET_DARWIN) && !defined(TARGET_RASPBERRY_PI) AddBool(aocat, "audiooutput.truehdpassthrough", 349, true ); AddBool(aocat, "audiooutput.dtshdpassthrough" , 347, true ); #endif +#if !defined(TARGET_RASPBERRY_PI) #if defined(TARGET_DARWIN) #if defined(TARGET_DARWIN_IOS) CStdString defaultDeviceName = "Default"; @@ -505,12 +512,15 @@ void CGUISettings::Initialize() AddString (ao, "audiooutput.passthroughdevice", 546, CStdString(CAEFactory::GetDefaultDevice(true )), SPIN_CONTROL_TEXT); AddSeparator(ao, "audiooutput.sep2"); #endif +#endif +#if !defined(TARGET_RASPBERRY_PI) map guimode; guimode.insert(make_pair(34121, AE_SOUND_IDLE )); guimode.insert(make_pair(34122, AE_SOUND_ALWAYS)); guimode.insert(make_pair(34123, AE_SOUND_OFF )); AddInt(ao, "audiooutput.guisoundmode", 34120, AE_SOUND_IDLE, guimode, SPIN_CONTROL_TEXT); +#endif CSettingsCategory* in = AddCategory(SETTINGS_SYSTEM, "input", 14094); AddString(in, "input.peripherals", 35000, "", BUTTON_CONTROL_STANDARD); @@ -705,7 +715,11 @@ void CGUISettings::Initialize() adjustTypes.insert(make_pair(36036, ADJUST_REFRESHRATE_ON_STARTSTOP)); #if !defined(TARGET_DARWIN_IOS) +#if defined(TARGET_RASPBERRY_PI) + AddBool(vp, "videoplayer.adjustrefreshrate", 170, true); +#else AddInt(vp, "videoplayer.adjustrefreshrate", 170, ADJUST_REFRESHRATE_OFF, adjustTypes, SPIN_CONTROL_TEXT); +#endif // AddBool(vp, "videoplayer.adjustrefreshrate", 170, false); AddInt(vp, "videoplayer.pauseafterrefreshchange", 13550, 0, 0, 1, MAXREFRESHCHANGEDELAY, SPIN_CONTROL_TEXT); #else diff --git a/xbmc/system.h b/xbmc/system.h index 584a2b3aebbd3..1999320381e6a 100644 --- a/xbmc/system.h +++ b/xbmc/system.h @@ -166,6 +166,8 @@ #define HAS_SDL_OPENGL #endif #define HAS_SDL_WIN_EVENTS +#else +#define HAS_LINUX_EVENTS #endif #define HAS_LINUX_NETWORK #define HAS_LIRC diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h index 90a87d40ea94e..502711796938b 100644 --- a/xbmc/utils/MathUtils.h +++ b/xbmc/utils/MathUtils.h @@ -36,14 +36,16 @@ #if defined(__ppc__) || \ defined(__powerpc__) || \ (defined(__APPLE__) && defined(__arm__) && defined(__llvm__)) || \ - (defined(__ANDROID__) && defined(__arm__)) + (defined(__ANDROID__) && defined(__arm__)) || \ + defined(TARGET_RASPBERRY_PI) #define DISABLE_MATHUTILS_ASM_ROUND_INT #endif #if defined(__ppc__) || \ defined(__powerpc__) || \ (defined(__APPLE__) && defined(__llvm__)) || \ - (defined(__ANDROID__) && defined(__arm__)) + (defined(__ANDROID__) && defined(__arm__)) || \ + defined(TARGET_RASPBERRY_PI) #define DISABLE_MATHUTILS_ASM_TRUNCATE_INT #endif diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp index ca13609a96242..041c9f0fd7108 100644 --- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp @@ -78,7 +78,9 @@ void CGUIDialogVideoSettings::CreateSettings() { vector > entries; entries.push_back(make_pair(VS_DEINTERLACEMODE_OFF , 16039)); +#ifndef TARGET_RASPBERRY_PI entries.push_back(make_pair(VS_DEINTERLACEMODE_AUTO , 16040)); +#endif entries.push_back(make_pair(VS_DEINTERLACEMODE_FORCE , 16041)); /* remove unsupported methods */ @@ -92,6 +94,7 @@ void CGUIDialogVideoSettings::CreateSettings() AddSpin(VIDEO_SETTINGS_DEINTERLACEMODE, 16037, (int*)&g_settings.m_currentVideoSettings.m_DeinterlaceMode, entries); } +#ifndef TARGET_RASPBERRY_PI { vector > entries; entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO , 16019)); @@ -157,6 +160,7 @@ void CGUIDialogVideoSettings::CreateSettings() AddSpin(VIDEO_SETTINGS_SCALINGMETHOD, 16300, (int*)&g_settings.m_currentVideoSettings.m_ScalingMethod, entries); } +#endif AddBool(VIDEO_SETTINGS_CROP, 644, &g_settings.m_currentVideoSettings.m_Crop); { const int entries[] = {630, 631, 632, 633, 634, 635, 636 }; diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp index 7a1972fb4fa66..dac0f4bd10ff1 100644 --- a/xbmc/video/windows/GUIWindowFullScreen.cpp +++ b/xbmc/video/windows/GUIWindowFullScreen.cpp @@ -1115,6 +1115,9 @@ void CGUIWindowFullScreen::RenderTTFSubtitles() if ((g_application.GetCurrentPlayer() == EPC_MPLAYER || #if defined(HAS_AMLPLAYER) g_application.GetCurrentPlayer() == EPC_AMLPLAYER || +#endif +#if defined(HAS_OMXPLAYER) + g_application.GetCurrentPlayer() == EPC_OMXPLAYER || #endif g_application.GetCurrentPlayer() == EPC_DVDPLAYER) && CUtil::IsUsingTTFSubtitles() && (g_application.m_pPlayer->GetSubtitleVisible())) diff --git a/xbmc/windowing/WinSystem.cpp b/xbmc/windowing/WinSystem.cpp index 303a5fe71e786..be8874d088944 100644 --- a/xbmc/windowing/WinSystem.cpp +++ b/xbmc/windowing/WinSystem.cpp @@ -35,6 +35,7 @@ CWinSystemBase::CWinSystemBase() m_bFullScreen = false; m_nScreen = 0; m_bBlankOtherDisplay = false; + m_fRefreshRate = 0.0f; } CWinSystemBase::~CWinSystemBase() diff --git a/xbmc/windowing/WinSystem.h b/xbmc/windowing/WinSystem.h index e6b08e28e0f71..82571c97dc870 100644 --- a/xbmc/windowing/WinSystem.h +++ b/xbmc/windowing/WinSystem.h @@ -114,6 +114,7 @@ class CWinSystemBase bool m_bFullScreen; int m_nScreen; bool m_bBlankOtherDisplay; + float m_fRefreshRate; }; diff --git a/xbmc/windowing/egl/Makefile b/xbmc/windowing/egl/Makefile index 2a42d859fc36a..a9fe48b8504bb 100644 --- a/xbmc/windowing/egl/Makefile +++ b/xbmc/windowing/egl/Makefile @@ -3,6 +3,7 @@ INCLUDES=-I. SRCS = WinSystemGLES.cpp SRCS+= WinEGLPlatformGeneric.cpp SRCS+= WinEGLPlatformAndroid.cpp +SRCS+= WinEGLPlatformRaspberryPI.cpp LIB = windowing_egl.a diff --git a/xbmc/windowing/egl/WinEGLPlatform.h b/xbmc/windowing/egl/WinEGLPlatform.h index 4715c8ce88253..559328cd40e3e 100644 --- a/xbmc/windowing/egl/WinEGLPlatform.h +++ b/xbmc/windowing/egl/WinEGLPlatform.h @@ -24,7 +24,11 @@ #include "system.h" -#if defined(TARGET_ANDROID) +#if defined(TARGET_RASPBERRY_PI) + #include "xbmc/windowing/egl/WinEGLPlatformRaspberryPI.h" + class CWinEGLPlatformRaspberryPI; + #define CWinEGLPlatform CWinEGLPlatformRaspberryPI +#elif defined(TARGET_ANDROID) #include "xbmc/windowing/egl/WinEGLPlatformAndroid.h" class CWinEGLPlatformAndroid; #define CWinEGLPlatform CWinEGLPlatformAndroid @@ -36,4 +40,4 @@ #endif -#endif \ No newline at end of file +#endif diff --git a/xbmc/windowing/egl/WinEGLPlatformAndroid.cpp b/xbmc/windowing/egl/WinEGLPlatformAndroid.cpp index 7f0add58bc664..c2d084150ceef 100644 --- a/xbmc/windowing/egl/WinEGLPlatformAndroid.cpp +++ b/xbmc/windowing/egl/WinEGLPlatformAndroid.cpp @@ -19,6 +19,10 @@ * */ +#include "system.h" + +#if defined(TARGET_ANDROID) + #include #include #include @@ -69,3 +73,5 @@ EGLNativeWindowType CWinEGLPlatformAndroid::getNativeWindow() { return (EGLNativeWindowType)CXBMCApp::GetNativeWindow(); } + +#endif diff --git a/xbmc/windowing/egl/WinEGLPlatformGeneric.cpp b/xbmc/windowing/egl/WinEGLPlatformGeneric.cpp index 2f2a83bc55b53..497a90747b88a 100644 --- a/xbmc/windowing/egl/WinEGLPlatformGeneric.cpp +++ b/xbmc/windowing/egl/WinEGLPlatformGeneric.cpp @@ -40,6 +40,18 @@ CWinEGLPlatformGeneric::CWinEGLPlatformGeneric() // default to 720p m_width = 1280; m_height = 720; + + m_desktopRes.iScreen = 0; + m_desktopRes.iWidth = 1280; + m_desktopRes.iHeight = 720; + //m_desktopRes.iScreenWidth = 1280; + //m_desktopRes.iScreenHeight = 720; + m_desktopRes.fRefreshRate = 60.0f; + m_desktopRes.bFullScreen = true; + m_desktopRes.iSubtitles = (int)(0.965 * 720); + m_desktopRes.dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN; + m_desktopRes.fPixelRatio = 1.0f; + m_desktopRes.strMode = "720p 16:9"; } CWinEGLPlatformGeneric::~CWinEGLPlatformGeneric() @@ -61,7 +73,7 @@ void CWinEGLPlatformGeneric::DestroyWindowSystem(EGLNativeWindowType native_wind UninitializeDisplay(); } -bool CWinEGLPlatformGeneric::SetDisplayResolution(int width, int height, float refresh, bool interlace) +bool CWinEGLPlatformGeneric::SetDisplayResolution(RESOLUTION_INFO& res) { return false; } @@ -73,13 +85,32 @@ bool CWinEGLPlatformGeneric::ClampToGUIDisplayLimits(int &width, int &height) return true; } -bool CWinEGLPlatformGeneric::ProbeDisplayResolutions(std::vector &resolutions) +bool CWinEGLPlatformGeneric::ProbeDisplayResolutions(std::vector &resolutions) { - resolutions.clear(); - - CStdString resolution; - resolution.Format("%dx%dp60Hz", m_width, m_height); - resolutions.push_back(resolution); + int gui_width = m_width; + int gui_height = m_height; + float gui_refresh = 60.0f; + RESOLUTION_INFO res; + + ClampToGUIDisplayLimits(gui_width, gui_height); + + res.iScreen = 0; + res.bFullScreen = true; + res.iSubtitles = (int)(0.965 * gui_height); + res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE; + res.fRefreshRate = gui_refresh; + res.fPixelRatio = 1.0f; + res.iWidth = gui_width; + res.iHeight = gui_height; + //res.iScreenWidth = gui_width; + //res.iScreenHeight = gui_height; + res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN; + // temp until split gui/display res comes in + //res.iScreenWidth = width; + //res.iScreenHeight = height; + res.strMode.Format("%dx%d @ %.2f - Full Screen", gui_width, gui_height, gui_refresh); + + resolutions.push_back(res); return true; } @@ -363,6 +394,11 @@ EGLDisplay CWinEGLPlatformGeneric::GetEGLDisplay() return m_display; } +EGLSurface CWinEGLPlatformGeneric::GetEGLSurface() +{ + return m_surface; +} + EGLContext CWinEGLPlatformGeneric::GetEGLContext() { return m_context; diff --git a/xbmc/windowing/egl/WinEGLPlatformGeneric.h b/xbmc/windowing/egl/WinEGLPlatformGeneric.h index 5aa657cabf380..859f14ab731b5 100644 --- a/xbmc/windowing/egl/WinEGLPlatformGeneric.h +++ b/xbmc/windowing/egl/WinEGLPlatformGeneric.h @@ -27,6 +27,9 @@ #include #endif +#include "guilib/gui3d.h" +#include "guilib/Resolution.h" + class CWinEGLPlatformGeneric { public: @@ -35,9 +38,9 @@ class CWinEGLPlatformGeneric virtual EGLNativeWindowType InitWindowSystem(EGLNativeDisplayType nativeDisplay, int width, int height, int bpp); virtual void DestroyWindowSystem(EGLNativeWindowType native_window); - virtual bool SetDisplayResolution(int width, int height, float refresh, bool interlace); + virtual bool SetDisplayResolution(RESOLUTION_INFO& res); virtual bool ClampToGUIDisplayLimits(int &width, int &height); - virtual bool ProbeDisplayResolutions(std::vector &resolutions); + virtual bool ProbeDisplayResolutions(std::vector &resolutions); virtual bool InitializeDisplay(); virtual bool UninitializeDisplay(); @@ -52,8 +55,13 @@ class CWinEGLPlatformGeneric virtual bool IsExtSupported(const char* extension); virtual EGLDisplay GetEGLDisplay(); + virtual EGLSurface GetEGLSurface(); virtual EGLContext GetEGLContext(); + virtual bool FixedDesktop() { return true; } + virtual RESOLUTION_INFO GetDesktopRes() { return m_desktopRes; } + virtual bool Support3D() { return false; } + protected: virtual EGLNativeWindowType getNativeWindow(); @@ -66,4 +74,5 @@ class CWinEGLPlatformGeneric CStdString m_eglext; int m_width; int m_height; + RESOLUTION_INFO m_desktopRes; }; diff --git a/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.cpp b/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.cpp new file mode 100644 index 0000000000000..9087a3d1ccddd --- /dev/null +++ b/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.cpp @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#if defined(TARGET_RASPBERRY_PI) + +#include "system_gl.h" + +#ifdef HAS_EGL + +#include "WinEGLPlatformRaspberryPI.h" +#include "utils/log.h" +#include "guilib/gui3d.h" +#include "xbmc/cores/VideoRenderers/RenderManager.h" +#include "settings/Settings.h" + +#include + +#ifndef __VIDEOCORE4__ +#define __VIDEOCORE4__ +#endif + +#define __VCCOREVER__ 0x04000000 + +#define IS_WIDESCREEN(m) ( m == 3 || m == 7 || m == 9 || \ + m == 11 || m == 13 || m == 15 || m == 18 || m == 22 || \ + m == 24 || m == 26 || m == 28 || m == 30 || m == 36 || \ + m == 38 || m == 43 || m == 45 || m == 49 || m == 51 || \ + m == 53 || m == 55 || m == 57 || m == 59) + +#define MAKEFLAGS(group, mode, interlace, mode3d) \ + ( ( (mode)<<24 ) | ( (group)<<16 ) | \ + ( (interlace) != 0 ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE) | \ + ( ((group) == HDMI_RES_GROUP_CEA && IS_WIDESCREEN(mode) ) ? D3DPRESENTFLAG_WIDESCREEN : 0) | \ + ( (mode3d) != 0 ? D3DPRESENTFLAG_MODE3DSBS : 0 )) + +#define GETFLAGS_INTERLACE(f) ( ( (f) & D3DPRESENTFLAG_INTERLACED ) != 0) +#define GETFLAGS_WIDESCREEN(f) ( ( (f) & D3DPRESENTFLAG_WIDESCREEN ) != 0) +#define GETFLAGS_GROUP(f) ( (HDMI_RES_GROUP_T)( ((f) >> 16) & 0xff )) +#define GETFLAGS_MODE(f) ( ( (f) >>24 ) & 0xff ) +#define GETFLAGS_MODE3D(f) ( ( (f) & D3DPRESENTFLAG_MODE3DSBS ) !=0 ) + +#define TV_MAX_SUPPORTED_MODES 60 + +CWinEGLPlatformRaspberryPI::CWinEGLPlatformRaspberryPI() +{ + m_surface = EGL_NO_SURFACE; + m_context = EGL_NO_CONTEXT; + m_display = EGL_NO_DISPLAY; + + m_desktopRes.iScreen = 0; + m_desktopRes.iWidth = 1280; + m_desktopRes.iHeight = 720; + m_desktopRes.fRefreshRate = 60.0f; + m_desktopRes.bFullScreen = true; + m_desktopRes.iSubtitles = (int)(0.965 * 720); + m_desktopRes.dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN; + m_desktopRes.fPixelRatio = 1.0f; + m_desktopRes.strMode = "720p 16:9"; + m_sdMode = false; + + m_dispman_element = DISPMANX_NO_HANDLE; + m_dispman_element2 = DISPMANX_NO_HANDLE; + m_dispman_display = DISPMANX_NO_HANDLE; + + m_DllBcmHost.Load(); + + // get current display settings state + memset(&m_tv_state, 0, sizeof(TV_GET_STATE_RESP_T)); + m_DllBcmHost.vc_tv_get_state(&m_tv_state); + + m_nativeWindow = (EGL_DISPMANX_WINDOW_T *)malloc(sizeof(EGL_DISPMANX_WINDOW_T)); + memset(m_nativeWindow, 0x0, sizeof(EGL_DISPMANX_WINDOW_T)); +} + +CWinEGLPlatformRaspberryPI::~CWinEGLPlatformRaspberryPI() +{ + UninitializeDisplay(); + + free(m_nativeWindow); + + if(m_DllBcmHost.IsLoaded()) + m_DllBcmHost.Unload(); +} + +bool CWinEGLPlatformRaspberryPI::SetDisplayResolution(RESOLUTION_INFO& res) +{ + bool bFound = false; + + DestroyDispmaxWindow(); + + RESOLUTION_INFO resSearch; + + int best_score = 0; + + for (size_t i = 0; i < m_res.size(); i++) + { + if(m_res[i].iWidth == res.iWidth && m_res[i].iHeight == res.iHeight && m_res[i].fRefreshRate == res.fRefreshRate) + { + int score = 0; + + /* prefere progressive over interlaced */ + if(!GETFLAGS_INTERLACE(m_res[i].dwFlags)) + score = 1; + + if(score >= best_score) + { + resSearch = m_res[i]; + bFound = true; + } + } + } + + if(m_res.size() < 2) + bFound = false; + + if(bFound && !m_sdMode) + { + sem_init(&m_tv_synced, 0, 0); + m_DllBcmHost.vc_tv_register_callback(CallbackTvServiceCallback, this); + + int success = m_DllBcmHost.vc_tv_hdmi_power_on_explicit(HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags), GETFLAGS_MODE(resSearch.dwFlags)); + + if (success == 0) + { + CLog::Log(LOGDEBUG, "EGL set HDMI mode (%d,%d,%d)=%d\n", + GETFLAGS_MODE3D(resSearch.dwFlags) ? HDMI_MODE_3D:HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags), + GETFLAGS_MODE(resSearch.dwFlags), success); + sem_wait(&m_tv_synced); + } + else + { + CLog::Log(LOGERROR, "EGL failed to set HDMI mode (%d,%d,%d)=%d\n", + GETFLAGS_MODE3D(resSearch.dwFlags) ? HDMI_MODE_3D:HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags), + GETFLAGS_MODE(resSearch.dwFlags), success); + } + m_DllBcmHost.vc_tv_unregister_callback(CallbackTvServiceCallback); + sem_destroy(&m_tv_synced); + } + + m_dispman_display = m_DllBcmHost.vc_dispmanx_display_open(0); + + m_width = res.iWidth; + m_height = res.iHeight; + + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = res.iWidth; + dst_rect.height = res.iHeight; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = m_width << 16; + src_rect.height = m_height << 16; + + VC_DISPMANX_ALPHA_T alpha; + memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); + alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; + + DISPMANX_CLAMP_T clamp; + memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T)); + + DISPMANX_TRANSFORM_T transform = DISPMANX_NO_ROTATE; + DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost.vc_dispmanx_update_start(0); + + CLog::Log(LOGDEBUG, "EGL set resolution %dx%d -> %dx%d @ %.2f fps\n", + m_width, m_height, dst_rect.width, dst_rect.height, bFound ? resSearch.fRefreshRate : res.fRefreshRate); + + // The trick for SBS is that we stick two dispman elements together + // and the PI firmware knows that we are in SBS mode and it renders the gui in SBS + if(bFound && (resSearch.dwFlags & D3DPRESENTFLAG_MODE3DSBS)) + { + // right side + dst_rect.x = res.iWidth; + dst_rect.width >>= dst_rect.width - dst_rect.x; + + m_dispman_element2 = m_DllBcmHost.vc_dispmanx_element_add(dispman_update, + m_dispman_display, + 1, // layer + &dst_rect, + (DISPMANX_RESOURCE_HANDLE_T)0, // src + &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha, // alpha + &clamp, // clamp + transform); // transform + assert(m_dispman_element2 != DISPMANX_NO_HANDLE); + assert(m_dispman_element2 != (unsigned)DISPMANX_INVALID); + + // left side - fall through + dst_rect.x = 0; + dst_rect.width = res.iWidth - dst_rect.x; + } + + m_dispman_element = m_DllBcmHost.vc_dispmanx_element_add(dispman_update, + m_dispman_display, + 1, // layer + &dst_rect, + (DISPMANX_RESOURCE_HANDLE_T)0, // src + &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha, //alphe + &clamp, //clamp + transform); // transform + + assert(m_dispman_element != DISPMANX_NO_HANDLE); + assert(m_dispman_element != (unsigned)DISPMANX_INVALID); + + memset(m_nativeWindow, 0, sizeof(EGL_DISPMANX_WINDOW_T)); + + m_nativeWindow->element = m_dispman_element; + m_nativeWindow->width = m_width; + m_nativeWindow->height = m_height; + + m_DllBcmHost.vc_dispmanx_display_set_background(dispman_update, m_dispman_display, 0x00, 0x00, 0x00); + m_DllBcmHost.vc_dispmanx_update_submit_sync(dispman_update); + + return true; +} + +bool CWinEGLPlatformRaspberryPI::ClampToGUIDisplayLimits(int &width, int &height) +{ + return true; +} + +bool CWinEGLPlatformRaspberryPI::ProbeDisplayResolutions(std::vector &resolutions) +{ + resolutions.clear(); + + GetSupportedModes(HDMI_RES_GROUP_CEA, resolutions); + GetSupportedModes(HDMI_RES_GROUP_DMT, resolutions); + GetSupportedModes(HDMI_RES_GROUP_CEA_3D, resolutions); + + if(resolutions.size() == 0) + { + m_sdMode = true; + + TV_GET_STATE_RESP_T tv; + m_DllBcmHost.vc_tv_get_state(&tv); + + RESOLUTION_INFO res; + CLog::Log(LOGDEBUG, "EGL probe resolution %dx%d@%d %s:%x\n", + tv.width, tv.height, tv.frame_rate, tv.scan_mode?"I":""); + + res.iScreen = 0; + res.bFullScreen = true; + res.iSubtitles = (int)(0.965 * tv.height); + res.dwFlags = tv.scan_mode ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE; + res.fRefreshRate = (float)tv.frame_rate; + res.fPixelRatio = 1.0f; + res.iWidth = tv.width; + res.iHeight = tv.height; + //res.iScreenWidth = tv.width; + //res.iScreenHeight = tv.height; + res.strMode.Format("%dx%d", tv.width, tv.height); + if((float)tv.frame_rate > 1) + { + res.strMode.Format("%s @ %.2f%s - Full Screen", res.strMode, (float)tv.frame_rate, + res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : ""); + } + + resolutions.push_back(res); + m_desktopRes = res; + m_res.push_back(res); + } + + return true; +} + +EGLNativeWindowType CWinEGLPlatformRaspberryPI::InitWindowSystem(EGLNativeDisplayType nativeDisplay, int width, int height, int bpp) +{ + m_nativeDisplay = nativeDisplay; + m_width = width; + m_height = height; + + return getNativeWindow(); +} + +void CWinEGLPlatformRaspberryPI::DestroyWindowSystem(EGLNativeWindowType native_window) +{ + UninitializeDisplay(); +} + +bool CWinEGLPlatformRaspberryPI::InitializeDisplay() +{ + EGLBoolean eglStatus; + EGLint configCount; + EGLConfig* configList = NULL; + + m_display = eglGetDisplay(m_nativeDisplay); + if (m_display == EGL_NO_DISPLAY) + { + CLog::Log(LOGERROR, "EGL failed to obtain display"); + return false; + } + + if (!eglInitialize(m_display, 0, 0)) + { + CLog::Log(LOGERROR, "EGL failed to initialize"); + return false; + } + + EGLint configAttrs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_DEPTH_SIZE, 16, + EGL_SAMPLE_BUFFERS, 1, + EGL_NONE + }; + + // Find out how many configurations suit our needs + eglStatus = eglChooseConfig(m_display, configAttrs, NULL, 0, &configCount); + if (!eglStatus || !configCount) + { + CLog::Log(LOGERROR, "EGL failed to return any matching configurations: %d error : 0x%08x", eglStatus, eglGetError()); + return false; + } + + // Allocate room for the list of matching configurations + configList = (EGLConfig*)malloc(configCount * sizeof(EGLConfig)); + if (!configList) + { + CLog::Log(LOGERROR, "EGL malloc failure obtaining configuration list"); + return false; + } + + // Obtain the configuration list from EGL + eglStatus = eglChooseConfig(m_display, configAttrs, + configList, configCount, &configCount); + if (!eglStatus || !configCount) + { + CLog::Log(LOGERROR, "EGL failed to populate configuration list: %d error : 0x%08x", eglStatus, eglGetError()); + return false; + } + + // Select an EGL configuration that matches the native window + m_config = configList[0]; + + if (m_surface != EGL_NO_SURFACE) + ReleaseSurface(); + + free(configList); + + return true; +} + +bool CWinEGLPlatformRaspberryPI::UninitializeDisplay() +{ + EGLBoolean eglStatus; + + // Recreate a new rendering context doesn't work on the PI. + // We have to keep the first created context alive until we exit. + if (m_context != EGL_NO_CONTEXT) + { + eglStatus = eglDestroyContext(m_display, m_context); + if (!eglStatus) + CLog::Log(LOGERROR, "EGL destroy context error : 0x%08x", eglGetError()); + m_context = EGL_NO_CONTEXT; + } + + DestroyWindow(); + + DestroyDispmaxWindow(); + + if (m_display != EGL_NO_DISPLAY) + { + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + eglStatus = eglTerminate(m_display); + if (!eglStatus) + CLog::Log(LOGERROR, "EGL terminate error 0x%08x", eglGetError()); + m_display = EGL_NO_DISPLAY; + } + + return true; +} + +bool CWinEGLPlatformRaspberryPI::CreateWindow() +{ + if (m_display == EGL_NO_DISPLAY || m_config == NULL) + { + if (!InitializeDisplay()) + return false; + } + + if (m_surface != EGL_NO_SURFACE) + return true; + + m_nativeWindow = (EGL_DISPMANX_WINDOW_T *)getNativeWindow(); + + m_surface = eglCreateWindowSurface(m_display, m_config, m_nativeWindow, NULL); + if (!m_surface) + { + CLog::Log(LOGERROR, "EGL couldn't create window surface"); + return false; + } + + // Let's get the current width and height + EGLint width, height; + if (!eglQuerySurface(m_display, m_surface, EGL_WIDTH, &width) || !eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &height) || + width <= 0 || height <= 0) + { + CLog::Log(LOGERROR, "EGL couldn't provide the surface's width and/or height"); + return false; + } + + m_width = width; + m_height = height; + + return true; +} + +void CWinEGLPlatformRaspberryPI::DestroyDispmaxWindow() +{ + DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost.vc_dispmanx_update_start(0); + + if (m_dispman_element != DISPMANX_NO_HANDLE) + { + m_DllBcmHost.vc_dispmanx_element_remove(dispman_update, m_dispman_element); + m_dispman_element = DISPMANX_NO_HANDLE; + } + if (m_dispman_element2 != DISPMANX_NO_HANDLE) + { + m_DllBcmHost.vc_dispmanx_element_remove(dispman_update, m_dispman_element2); + m_dispman_element2 = DISPMANX_NO_HANDLE; + } + m_DllBcmHost.vc_dispmanx_update_submit_sync(dispman_update); + + if (m_dispman_display != DISPMANX_NO_HANDLE) + { + m_DllBcmHost.vc_dispmanx_display_close(m_dispman_display); + m_dispman_display = DISPMANX_NO_HANDLE; + } +} + +bool CWinEGLPlatformRaspberryPI::DestroyWindow() +{ + EGLBoolean eglStatus; + + ReleaseSurface(); + + if (m_surface == EGL_NO_SURFACE) + return true; + + eglStatus = eglDestroySurface(m_display, m_surface); + if (!eglStatus) + { + CLog::Log(LOGERROR, "EGL destroy surface error : 0x%08x", eglGetError()); + return false; + } + + m_surface = EGL_NO_SURFACE; + m_width = 0; + m_height = 0; + + return true; +} + +bool CWinEGLPlatformRaspberryPI::BindSurface() +{ + EGLBoolean eglStatus; + + if (m_display == EGL_NO_DISPLAY || m_surface == EGL_NO_SURFACE || m_config == NULL) + { + CLog::Log(LOGINFO, "EGL not configured correctly. Let's try to do that now..."); + if (!CreateWindow()) + { + CLog::Log(LOGERROR, "EGL not configured correctly to create a surface"); + return false; + } + } + + eglStatus = eglBindAPI(EGL_OPENGL_ES_API); + if (!eglStatus) + { + CLog::Log(LOGERROR, "EGL failed to bind API: %d error 0x%08x", eglStatus, eglGetError()); + return false; + } + + EGLint contextAttrs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + // Create an EGL context + if (m_context == EGL_NO_CONTEXT) + { + m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, contextAttrs); + if (!m_context) + { + CLog::Log(LOGERROR, "EGL couldn't create context"); + return false; + } + } + + // Make the context and surface current to this thread for rendering + eglStatus = eglMakeCurrent(m_display, m_surface, m_surface, m_context); + if (!eglStatus) + { + CLog::Log(LOGERROR, "EGL couldn't make context/surface current: %d error : 0x%08x", eglStatus, eglGetError()); + return false; + } + + eglSwapInterval(m_display, 0); + + // For EGL backend, it needs to clear all the back buffers of the window + // surface before drawing anything, otherwise the image will be blinking + // heavily. The default eglWindowSurface has 3 gdl surfaces as the back + // buffer, that's why glClear should be called 3 times. + glClearColor (0.0f, 0.0f, 0.0f, 0.0f); + glClear (GL_COLOR_BUFFER_BIT); + eglSwapBuffers(m_display, m_surface); + + glClear (GL_COLOR_BUFFER_BIT); + eglSwapBuffers(m_display, m_surface); + + glClear (GL_COLOR_BUFFER_BIT); + eglSwapBuffers(m_display, m_surface); + + m_eglext = " "; + m_eglext += eglQueryString(m_display, EGL_EXTENSIONS); + m_eglext += " "; + CLog::Log(LOGDEBUG, "EGL extensions:%s", m_eglext.c_str()); + + // setup for vsync disabled + eglSwapInterval(m_display, 0); + + CLog::Log(LOGINFO, "EGL window and context creation complete"); + + return true; +} + +bool CWinEGLPlatformRaspberryPI::ReleaseSurface() +{ + EGLBoolean eglStatus; + + if (m_display != EGL_NO_DISPLAY) + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + // Recreate a new rendering context doesn't work on the PI. + // We have to keep the first created context alive until we exit. + /* + if (m_context != EGL_NO_CONTEXT) + { + eglStatus = eglDestroyContext(m_display, m_context); + if (!eglStatus) + CLog::Log(LOGERROR, "Error destroying EGL context 0x%08x", eglGetError()); + m_context = EGL_NO_CONTEXT; + } + */ + + return true; +} + +bool CWinEGLPlatformRaspberryPI::ShowWindow(bool show) +{ + return true; +} + +void CWinEGLPlatformRaspberryPI::SwapBuffers() +{ + eglSwapBuffers(m_display, m_surface); +} + +bool CWinEGLPlatformRaspberryPI::SetVSync(bool enable) +{ + // depending how buffers are setup, eglSwapInterval + // might fail so let caller decide if this is an error. + return eglSwapInterval(m_display, enable ? 1 : 0); +} + +bool CWinEGLPlatformRaspberryPI::IsExtSupported(const char* extension) +{ + CStdString name; + + name = " "; + name += extension; + name += " "; + + return m_eglext.find(name) != std::string::npos; +} + +EGLNativeWindowType CWinEGLPlatformRaspberryPI::getNativeWindow() +{ + return (EGLNativeWindowType)m_nativeWindow; +} + +EGLDisplay CWinEGLPlatformRaspberryPI::GetEGLDisplay() +{ + return m_display; +} + +EGLSurface CWinEGLPlatformRaspberryPI::GetEGLSurface() +{ + return m_surface; +} + +EGLContext CWinEGLPlatformRaspberryPI::GetEGLContext() +{ + return m_context; +} + +void CWinEGLPlatformRaspberryPI::TvServiceCallback(uint32_t reason, uint32_t param1, uint32_t param2) +{ + CLog::Log(LOGDEBUG, "EGL tv_service_callback (%d,%d,%d)\n", reason, param1, param2); + switch(reason) + { + case VC_HDMI_UNPLUGGED: + break; + case VC_HDMI_STANDBY: + break; + case VC_SDTV_NTSC: + case VC_SDTV_PAL: + case VC_HDMI_HDMI: + case VC_HDMI_DVI: + //Signal we are ready now + sem_post(&m_tv_synced); + break; + default: + break; + } +} + +void CWinEGLPlatformRaspberryPI::CallbackTvServiceCallback(void *userdata, uint32_t reason, uint32_t param1, uint32_t param2) +{ + CWinEGLPlatformRaspberryPI *callback = static_cast(userdata); + callback->TvServiceCallback(reason, param1, param2); +} + +void CWinEGLPlatformRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::vector &resolutions) +{ + //Supported HDMI CEA/DMT resolutions, first one will be preferred resolution + TV_SUPPORTED_MODE_T supported_modes[TV_MAX_SUPPORTED_MODES]; + int32_t num_modes; + HDMI_RES_GROUP_T prefer_group; + uint32_t prefer_mode; + int i; + + num_modes = m_DllBcmHost.vc_tv_hdmi_get_supported_modes(group, + supported_modes, TV_MAX_SUPPORTED_MODES, &prefer_group, &prefer_mode); + + CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n", + group, num_modes, prefer_group, prefer_mode); + + if (num_modes > 0 && prefer_group != HDMI_RES_GROUP_INVALID) + { + TV_SUPPORTED_MODE_T *tv = supported_modes; + for (i=0; i < num_modes; i++, tv++) + { + // treat 3D modes as half-width SBS + unsigned int width = (group == HDMI_RES_GROUP_CEA_3D) ? tv->width>>1 : tv->width; + RESOLUTION_INFO res; + CLog::Log(LOGDEBUG, "EGL mode %d: %dx%d@%d %s%s:%x\n", i, width, tv->height, tv->frame_rate, + tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code); + + res.iScreen = 0; + res.bFullScreen = true; + res.iSubtitles = (int)(0.965 * tv->height); + res.dwFlags = MAKEFLAGS(group, tv->code, tv->scan_mode, group==HDMI_RES_GROUP_CEA_3D); + res.fRefreshRate = (float)tv->frame_rate; + res.fPixelRatio = 1.0f; + res.iWidth = width; + res.iHeight = tv->height; + //res.iScreenWidth = width; + //res.iScreenHeight = tv->height; + res.strMode.Format("%dx%d", width, tv->height); + if((float)tv->frame_rate > 1) + { + res.strMode.Format("%s @ %.2f%s - Full Screen", res.strMode, (float)tv->frame_rate, + res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : ""); + } + + resolutions.push_back(res); + + if(m_tv_state.width == width && m_tv_state.height == tv->height && + m_tv_state.scan_mode == tv->scan_mode && m_tv_state.frame_rate == tv->frame_rate) + m_desktopRes = res; + + m_res.push_back(res); + } + } +} + +#endif + +#endif diff --git a/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.h b/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.h new file mode 100644 index 0000000000000..9ece1a8a2f40c --- /dev/null +++ b/xbmc/windowing/egl/WinEGLPlatformRaspberryPI.h @@ -0,0 +1,98 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#if defined(TARGET_RASPBERRY_PI) + +#include "WinEGLPlatformGeneric.h" +#include "utils/StringUtils.h" +#include "guilib/Resolution.h" + +#include + +#include "linux/DllBCM.h" + +class CWinEGLPlatformRaspberryPI +{ +public: + CWinEGLPlatformRaspberryPI(); + virtual ~CWinEGLPlatformRaspberryPI(); + + virtual bool SetDisplayResolution(RESOLUTION_INFO &res); + virtual bool ClampToGUIDisplayLimits(int &width, int &height); + virtual bool ProbeDisplayResolutions(std::vector &resolutions); + + virtual EGLNativeWindowType InitWindowSystem(EGLNativeDisplayType nativeDisplay, int width, int height, int bpp); + virtual void DestroyWindowSystem(EGLNativeWindowType native_window); + + virtual bool InitializeDisplay(); + virtual bool UninitializeDisplay(); + virtual bool BindSurface(); + virtual bool CreateWindow(); + virtual bool DestroyWindow(); + virtual bool ShowWindow(bool show); + virtual bool ReleaseSurface(); + + virtual void SwapBuffers(); + virtual bool SetVSync(bool enable); + virtual bool IsExtSupported(const char* extension); + + virtual EGLDisplay GetEGLDisplay(); + virtual EGLSurface GetEGLSurface(); + virtual EGLContext GetEGLContext(); + virtual bool FixedDesktop() { return true; } + virtual RESOLUTION_INFO GetDesktopRes() { return m_desktopRes; } + +protected: + virtual EGLNativeWindowType getNativeWindow(); + void DestroyDispmaxWindow(); + + EGL_DISPMANX_WINDOW_T *m_nativeWindow; + EGLNativeDisplayType m_nativeDisplay; + EGLDisplay m_display; + EGLSurface m_surface; + EGLConfig m_config; + EGLContext m_context; + CStdString m_eglext; + int m_width; + int m_height; + + DllBcmHost m_DllBcmHost; + + DISPMANX_ELEMENT_HANDLE_T m_dispman_element; + DISPMANX_ELEMENT_HANDLE_T m_dispman_element2; + DISPMANX_DISPLAY_HANDLE_T m_dispman_display; + sem_t m_tv_synced; + + void GetSupportedModes(HDMI_RES_GROUP_T group, std::vector &resolutions); + void TvServiceCallback(uint32_t reason, uint32_t param1, uint32_t param2); + static void CallbackTvServiceCallback(void *userdata, uint32_t reason, uint32_t param1, uint32_t param2); + + std::vector m_res; + + RESOLUTION_INFO m_desktopRes; + + TV_GET_STATE_RESP_T m_tv_state; + + bool m_sdMode; +}; + +#endif diff --git a/xbmc/windowing/egl/WinSystemGLES.cpp b/xbmc/windowing/egl/WinSystemGLES.cpp index b090ff526c888..a1d67450ff7f3 100644 --- a/xbmc/windowing/egl/WinSystemGLES.cpp +++ b/xbmc/windowing/egl/WinSystemGLES.cpp @@ -58,8 +58,10 @@ bool CWinSystemGLES::InitWindowSystem() // Create a window to get valid width and height values // This needs to happen before the call to CWinSystemBase::InitWindowSystem() // (at least for Android) - if (!m_eglplatform->CreateWindow()) +#if defined(TARGET_ANDROID) + if(!m_eglplatform->CreateWindow()) return false; +#endif if (!CWinSystemBase::InitWindowSystem()) return false; @@ -77,7 +79,7 @@ bool CWinSystemGLES::DestroyWindowSystem() bool CWinSystemGLES::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction) { - if (m_bWindowCreated && m_nWidth == res.iWidth && m_nHeight == res.iHeight && m_bFullScreen == fullScreen) + if (m_bWindowCreated && m_nWidth == res.iWidth && m_nHeight == res.iHeight && m_fRefreshRate == res.fRefreshRate && m_bFullScreen == fullScreen) { CLog::Log(LOGDEBUG, "CWinSystemGLES::CreateNewWindow: No need to create a new window"); return true; @@ -86,15 +88,13 @@ bool CWinSystemGLES::CreateNewWindow(const CStdString& name, bool fullScreen, RE m_nWidth = res.iWidth; m_nHeight = res.iHeight; m_bFullScreen = fullScreen; + m_fRefreshRate = res.fRefreshRate; // Destroy any existing window if (m_bWindowCreated) m_eglplatform->DestroyWindow(); - // temp until split gui/display res comes in - //m_eglplatform->SetDisplayResolution(res.iScreenWidth, res.iScreenHeight, - m_eglplatform->SetDisplayResolution(res.iWidth, res.iHeight, - res.fRefreshRate, res.dwFlags & D3DPRESENTFLAG_INTERLACED); + m_eglplatform->SetDisplayResolution(res); // If we previously destroyed an existing window we need to create a new one // (otherwise this is taken care of by InitWindowSystem()) @@ -130,86 +130,78 @@ bool CWinSystemGLES::ResizeWindow(int newWidth, int newHeight, int newLeft, int bool CWinSystemGLES::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) { - CLog::Log(LOGDEBUG, "CWinSystemGLES::SetFullScreen"); CreateNewWindow("", fullScreen, res, NULL); - - CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight, true, 0); - + CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight, fullScreen, res.fRefreshRate); return true; } void CWinSystemGLES::UpdateResolutions() { - std::vector resolutions; + CWinSystemBase::UpdateResolutions(); + + //std::vector resolutions; + std::vector resolutions; m_eglplatform->ProbeDisplayResolutions(resolutions); - bool got_display_rez = false; - RESOLUTION Res720p60 = RES_INVALID; - RESOLUTION res_index = RES_DESKTOP; + RESOLUTION_INFO resDesktop = m_eglplatform->GetDesktopRes(); + + RESOLUTION ResDesktop = RES_INVALID; + RESOLUTION res_index = RES_DESKTOP; + + // Clear old resolutions + //g_settings.m_ResInfo.clear(); for (size_t i = 0; i < resolutions.size(); i++) { - // 1280x720p50Hz - // 1280x720p60Hz - // 1920x1080i50Hz - // 1920x1080i60Hz - // 1920x1080p24Hz - // 1920x1080p50Hz - // 1920x1080p60Hz - - char interlacing; - int refresh, width, height; - if (sscanf(resolutions[i].c_str(), "%dx%d%c%dHz", &width, &height, &interlacing, &refresh) == 4) + int gui_width = resolutions[i].iWidth; + int gui_height = resolutions[i].iHeight; + + m_eglplatform->ClampToGUIDisplayLimits(gui_width, gui_height); + + resolutions[i].iWidth = gui_width; + resolutions[i].iHeight = gui_height; + + // if this is a new setting, + // create a new empty setting to fill in. + if ((int)g_settings.m_ResInfo.size() <= res_index) + { + RESOLUTION_INFO res; + + g_settings.m_ResInfo.push_back(res); + } + + g_graphicsContext.ResetOverscan(resolutions[i]); + g_settings.m_ResInfo[res_index] = resolutions[i]; + + CLog::Log(LOGNOTICE, "Found resolution for display %d with %d x %d @ %f Hz\n", + resolutions[i].iScreen, + resolutions[i].iWidth, + resolutions[i].iHeight, + resolutions[i].fRefreshRate); + + if(m_eglplatform->FixedDesktop()) { - got_display_rez = true; - // if this is a new setting, - // create a new empty setting to fill in. - if ((int)g_settings.m_ResInfo.size() <= res_index) + if(resDesktop.iWidth == resolutions[i].iWidth && + resDesktop.iHeight == resolutions[i].iHeight && + resDesktop.fRefreshRate == resolutions[i].fRefreshRate) { - RESOLUTION_INFO res; - g_settings.m_ResInfo.push_back(res); + ResDesktop = res_index; } - int gui_width = width; - int gui_height = height; - float gui_refresh = refresh; - m_eglplatform->ClampToGUIDisplayLimits(gui_width, gui_height); - - g_settings.m_ResInfo[res_index].iScreen = 0; - g_settings.m_ResInfo[res_index].bFullScreen = true; - g_settings.m_ResInfo[res_index].iSubtitles = (int)(0.965 * gui_height); - g_settings.m_ResInfo[res_index].dwFlags = D3DPRESENTFLAG_PROGRESSIVE; - g_settings.m_ResInfo[res_index].fRefreshRate = gui_refresh; - g_settings.m_ResInfo[res_index].fPixelRatio = 1.0f; - g_settings.m_ResInfo[res_index].iWidth = gui_width; - g_settings.m_ResInfo[res_index].iHeight = gui_height; - // temp until split gui/display res comes in - //g_settings.m_ResInfo[res_index].iScreenWidth = width; - //g_settings.m_ResInfo[res_index].iScreenHeight = height; - g_settings.m_ResInfo[res_index].strMode.Format("%dx%d @ %.2f - Full Screen", width, height, gui_refresh); - g_graphicsContext.ResetOverscan(g_settings.m_ResInfo[res_index]); - /* - CLog::Log(LOGINFO, "Found possible resolution for display %d with %d x %d @ %f Hz\n", - g_settings.m_ResInfo[res_index].iScreen, - g_settings.m_ResInfo[res_index].iScreenWidth, - g_settings.m_ResInfo[res_index].iScreenHeight, - g_settings.m_ResInfo[res_index].fRefreshRate); - */ - - if (width == 1280 && height == 720 && refresh == 60) - Res720p60 = res_index; - - res_index = (RESOLUTION)((int)res_index + 1); } + + res_index = (RESOLUTION)((int)res_index + 1); } - // swap desktop index for 720p if available - if (Res720p60 != RES_INVALID) + + // swap desktop index for desktop res if available + if (ResDesktop != RES_INVALID) { - CLog::Log(LOGINFO, "Found 720p at %d, setting to RES_DESKTOP at %d", (int)Res720p60, (int)RES_DESKTOP); + CLog::Log(LOGNOTICE, "Found (%dx%d@%f) at %d, setting to RES_DESKTOP at %d", + resDesktop.iWidth, resDesktop.iHeight, resDesktop.fRefreshRate, (int)ResDesktop, (int)RES_DESKTOP); RESOLUTION_INFO desktop = g_settings.m_ResInfo[RES_DESKTOP]; - g_settings.m_ResInfo[RES_DESKTOP] = g_settings.m_ResInfo[Res720p60]; - g_settings.m_ResInfo[Res720p60] = desktop; + g_settings.m_ResInfo[RES_DESKTOP] = g_settings.m_ResInfo[ResDesktop]; + g_settings.m_ResInfo[ResDesktop] = desktop; } } @@ -284,4 +276,35 @@ EGLContext CWinSystemGLES::GetEGLContext() return m_eglplatform->GetEGLContext(); } +bool CWinSystemGLES::Support3D(int width, int height, uint32_t mode) const +{ + bool bFound = false; + int searchMode = 0; + int searchWidth = width; + int searchHeight = height; + + if(mode & D3DPRESENTFLAG_MODE3DSBS) + { + searchWidth /= 2; + searchMode = D3DPRESENTFLAG_MODE3DSBS; + } + else if(mode & D3DPRESENTFLAG_MODE3DTB) + { + searchHeight /= 2; + searchMode = D3DPRESENTFLAG_MODE3DTB; + } + + for(int i = 0; i < g_settings.m_ResInfo.size(); i++) + { + RESOLUTION_INFO res = g_settings.m_ResInfo[i]; + + if(res.iWidth == searchWidth && res.iHeight == searchHeight && (res.dwFlags & searchMode)) + { + return true; + } + } + + return bFound; +} + #endif diff --git a/xbmc/windowing/egl/WinSystemGLES.h b/xbmc/windowing/egl/WinSystemGLES.h index ff3959af634bb..6bcdc573aa031 100644 --- a/xbmc/windowing/egl/WinSystemGLES.h +++ b/xbmc/windowing/egl/WinSystemGLES.h @@ -56,6 +56,7 @@ class CWinSystemGLES : public CWinSystemBase, public CRenderSystemGLES EGLDisplay GetEGLDisplay(); EGLContext GetEGLContext(); + virtual bool Support3D(int width, int height, uint32_t mode) const; protected: virtual bool PresentRenderImpl(const CDirtyRegionList &dirty); diff --git a/xbmc/xbmc.cpp b/xbmc/xbmc.cpp index 75c6677b1380d..fc38a2819176e 100644 --- a/xbmc/xbmc.cpp +++ b/xbmc/xbmc.cpp @@ -21,6 +21,11 @@ #include "Application.h" #include "settings/AdvancedSettings.h" + +#ifdef TARGET_RASPBERRY_PI +#include "linux/RBP.h" +#endif + extern "C" int XBMC_Run(bool renderGUI) { int status = -1; @@ -42,6 +47,13 @@ extern "C" int XBMC_Run(bool renderGUI) fprintf(stderr, "ERROR: Unable to create application. Exiting\n"); return status; } + +#ifdef TARGET_RASPBERRY_PI + if(!g_RBP.Initialize()) + return false; + g_RBP.LogFirmwareVerison(); +#endif + if (renderGUI && !g_application.CreateGUI()) { fprintf(stderr, "ERROR: Unable to create GUI. Exiting\n"); @@ -63,5 +75,9 @@ extern "C" int XBMC_Run(bool renderGUI) status = -1; } +#ifdef TARGET_RASPBERRY_PI + g_RBP.Deinitialize(); +#endif + return status; }