aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhuceke <ebsi4711@gmail.com>2012-09-04 15:20:59 -0700
committerhuceke <ebsi4711@gmail.com>2012-09-04 15:20:59 -0700
commit45fe6fce168e99e34cd7ffd437d482eabb2bd3da (patch)
treef5d2d10366d2152e505ee8137e662300b845a0e4
parentd9d303f93a201e5fb70f84765766bbdbb906eecb (diff)
parent494cb5e4f24766f76cf6db8114f33a81fd00b8ba (diff)
Merge pull request #1282 from huceke/raspberrypi
RaspberryPI support
-rw-r--r--.gitignore1
-rw-r--r--Makefile.in4
-rw-r--r--Makefile.include.in4
-rw-r--r--configure.in107
-rw-r--r--docs/README.raspberrypi35
-rw-r--r--lib/libcec/Makefile2
-rw-r--r--lib/libsquish/Makefile.in9
-rw-r--r--lib/libsquish/config.h10
-rw-r--r--project/BuildDependencies/scripts/libcec_d.txt2
-rw-r--r--system/peripherals.xml20
-rw-r--r--system/playercorefactory.xml5
-rw-r--r--tools/android/depends/xbmc/Makefile2
-rw-r--r--tools/darwin/depends/libcec/Makefile2
-rw-r--r--tools/rbp/depends/Makefile37
-rw-r--r--tools/rbp/depends/autoconf/.gitignore1
-rw-r--r--tools/rbp/depends/autoconf/Makefile41
-rw-r--r--tools/rbp/depends/automake/.gitignore1
-rw-r--r--tools/rbp/depends/automake/Makefile41
-rw-r--r--tools/rbp/depends/cmake/.gitignore1
-rw-r--r--tools/rbp/depends/cmake/Makefile41
-rw-r--r--tools/rbp/depends/depends.mk55
-rw-r--r--tools/rbp/depends/help2man/.gitignore1
-rw-r--r--tools/rbp/depends/help2man/Makefile40
-rw-r--r--tools/rbp/depends/jasper/.gitignore1
-rw-r--r--tools/rbp/depends/jasper/Makefile43
-rw-r--r--tools/rbp/depends/liblzo2/.gitignore1
-rw-r--r--tools/rbp/depends/liblzo2/Makefile41
-rw-r--r--tools/rbp/depends/libnfs/.gitignore1
-rw-r--r--tools/rbp/depends/libnfs/Makefile41
-rw-r--r--tools/rbp/depends/libplist/0001-dontbuildswig.patch11
-rw-r--r--tools/rbp/depends/libplist/Makefile45
-rw-r--r--tools/rbp/depends/libshairport/.gitignore1
-rw-r--r--tools/rbp/depends/libshairport/001_add_ao.patch270
-rw-r--r--tools/rbp/depends/libshairport/002_fix_install_header.patch12
-rw-r--r--tools/rbp/depends/libshairport/003_fix_deadlock.patch10
-rw-r--r--tools/rbp/depends/libshairport/004_fix_bad_access.patch22
-rw-r--r--tools/rbp/depends/libshairport/005_fix_shutdown.patch10
-rw-r--r--tools/rbp/depends/libshairport/006_no_printf.patch843
-rw-r--r--tools/rbp/depends/libshairport/007_fix_syslog_defines.patch24
-rw-r--r--tools/rbp/depends/libshairport/008-add-missing-libs.patch21
-rw-r--r--tools/rbp/depends/libshairport/009_fix_ipv6.patch22
-rw-r--r--tools/rbp/depends/libshairport/010_handle_metadata.patch154
-rw-r--r--tools/rbp/depends/libshairport/Makefile50
-rw-r--r--tools/rbp/depends/libtool/.gitignore1
-rw-r--r--tools/rbp/depends/libtool/Makefile41
-rw-r--r--tools/rbp/depends/patchelf/.gitignore1
-rw-r--r--tools/rbp/depends/patchelf/Makefile41
-rw-r--r--tools/rbp/depends/pkg-config/.gitignore1
-rw-r--r--tools/rbp/depends/pkg-config/Makefile42
-rw-r--r--tools/rbp/depends/tiff/.gitignore1
-rw-r--r--tools/rbp/depends/tiff/Makefile41
-rw-r--r--tools/rbp/depends/tinyxml/.gitignore1
-rw-r--r--tools/rbp/depends/tinyxml/Makefile49
-rw-r--r--tools/rbp/depends/tinyxml/disable_xmltest.patch20
-rw-r--r--tools/rbp/depends/tinyxml/enforce-use-stl.patch18
-rw-r--r--tools/rbp/depends/tinyxml/entity.patch64
-rw-r--r--tools/rbp/depends/tinyxml/makefix.patch11
-rw-r--r--tools/rbp/depends/xbmc/Makefile27
-rw-r--r--tools/rbp/depends/yasm/.gitignore1
-rw-r--r--tools/rbp/depends/yasm/Makefile41
-rw-r--r--tools/rbp/setup-sdk.sh41
-rw-r--r--xbmc/Application.cpp10
-rw-r--r--xbmc/SystemGlobals.cpp8
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.cpp4
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEConvert.cpp4
-rw-r--r--xbmc/cores/VideoRenderers/BaseRenderer.cpp86
-rw-r--r--xbmc/cores/VideoRenderers/BaseRenderer.h3
-rw-r--r--xbmc/cores/VideoRenderers/LinuxRendererGLES.h1
-rw-r--r--xbmc/cores/VideoRenderers/RenderFlags.h4
-rw-r--r--xbmc/cores/dvdplayer/DVDMessageQueue.cpp2
-rw-r--r--xbmc/cores/omxplayer/BitstreamConverter.cpp861
-rw-r--r--xbmc/cores/omxplayer/BitstreamConverter.h170
-rw-r--r--xbmc/cores/omxplayer/DllOMX.h123
-rw-r--r--xbmc/cores/omxplayer/Makefile.in20
-rw-r--r--xbmc/cores/omxplayer/OMXAudio.cpp1482
-rw-r--r--xbmc/cores/omxplayer/OMXAudio.h152
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodec.h119
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodecOMX.cpp383
-rw-r--r--xbmc/cores/omxplayer/OMXAudioCodecOMX.h77
-rw-r--r--xbmc/cores/omxplayer/OMXImage.cpp1096
-rw-r--r--xbmc/cores/omxplayer/OMXImage.h108
-rw-r--r--xbmc/cores/omxplayer/OMXPlayer.cpp3822
-rw-r--r--xbmc/cores/omxplayer/OMXPlayer.h458
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerAudio.cpp823
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerAudio.h129
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerVideo.cpp819
-rw-r--r--xbmc/cores/omxplayer/OMXPlayerVideo.h135
-rw-r--r--xbmc/cores/omxplayer/OMXVideo.cpp961
-rw-r--r--xbmc/cores/omxplayer/OMXVideo.h98
-rw-r--r--xbmc/cores/omxplayer/OMXVideoCodec.h240
-rw-r--r--xbmc/cores/omxplayer/omxplayer_advancedsettings.xml10
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreConfig.h18
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.cpp7
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.h10
-rw-r--r--xbmc/guilib/GUIFontTTFGL.cpp2
-rw-r--r--xbmc/guilib/GUITexture.cpp3
-rw-r--r--xbmc/guilib/GUITextureGLES.cpp2
-rw-r--r--xbmc/guilib/GraphicContext.cpp48
-rw-r--r--xbmc/guilib/Resolution.h30
-rw-r--r--xbmc/guilib/Texture.cpp55
-rw-r--r--xbmc/guilib/gui3d.h8
-rw-r--r--xbmc/input/linux/LinuxInputDevices.h1
-rw-r--r--xbmc/interfaces/Builtins.cpp4
-rw-r--r--xbmc/linux/DllBCM.h227
-rw-r--r--xbmc/linux/DllOMX.h122
-rw-r--r--xbmc/linux/Makefile.in3
-rw-r--r--xbmc/linux/OMXClock.cpp1068
-rw-r--r--xbmc/linux/OMXClock.h158
-rw-r--r--xbmc/linux/OMXCore.cpp1711
-rw-r--r--xbmc/linux/OMXCore.h222
-rw-r--r--xbmc/linux/RBP.cpp77
-rw-r--r--xbmc/linux/RBP.h60
-rw-r--r--xbmc/peripherals/PeripheralTypes.h7
-rw-r--r--xbmc/peripherals/Peripherals.cpp14
-rw-r--r--xbmc/peripherals/bus/Makefile.in4
-rw-r--r--xbmc/peripherals/bus/linux/PeripheralBusRPi.cpp69
-rw-r--r--xbmc/peripherals/bus/linux/PeripheralBusRPi.h41
-rw-r--r--xbmc/peripherals/devices/PeripheralCecAdapter.cpp4
-rw-r--r--xbmc/pictures/Picture.cpp16
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.cpp4
-rw-r--r--xbmc/settings/AdvancedSettings.cpp10
-rw-r--r--xbmc/settings/AdvancedSettings.h3
-rw-r--r--xbmc/settings/GUISettings.cpp22
-rw-r--r--xbmc/system.h2
-rw-r--r--xbmc/utils/MathUtils.h6
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoSettings.cpp4
-rw-r--r--xbmc/video/windows/GUIWindowFullScreen.cpp3
-rw-r--r--xbmc/windowing/WinSystem.cpp1
-rw-r--r--xbmc/windowing/WinSystem.h1
-rw-r--r--xbmc/windowing/egl/Makefile1
-rw-r--r--xbmc/windowing/egl/WinEGLPlatform.h8
-rw-r--r--xbmc/windowing/egl/WinEGLPlatformAndroid.cpp6
-rw-r--r--xbmc/windowing/egl/WinEGLPlatformGeneric.cpp50
-rw-r--r--xbmc/windowing/egl/WinEGLPlatformGeneric.h13
-rw-r--r--xbmc/windowing/egl/WinEGLPlatformRaspberryPI.cpp713
-rw-r--r--xbmc/windowing/egl/WinEGLPlatformRaspberryPI.h98
-rw-r--r--xbmc/windowing/egl/WinSystemGLES.cpp155
-rw-r--r--xbmc/windowing/egl/WinSystemGLES.h1
-rw-r--r--xbmc/xbmc.cpp16
139 files changed, 19695 insertions, 148 deletions
diff --git a/.gitignore b/.gitignore
index a3d6d24800..7d5682a654 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 1cea93f3c4..cef82b880a 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 a517dd269e..704a4bc7f2 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 4c4b1252d1..41d3633ee8 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 0000000000..57b67f80fb
--- /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 c1eed1e6ea..b521c14551 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 f7b3cc1621..34f93bdeea 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 2fad5576a9..00a2d068bf 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 5bead42f9a..1e827d6b5b 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 cf0c6c442f..5d2eef3152 100644
--- a/system/peripherals.xml
+++ b/system/peripherals.xml
@@ -9,6 +9,26 @@
<setting key="key_power" value="XBMC.ShutDown()" label="13015" order="6" />
</peripheral>
+ <peripheral vendor_product="2708:1001" bus="rpi" name="Raspberry Pi CEC Adapter" mapTo="cec">
+ <setting key="enabled" type="bool" value="1" label="305" order="1" />
+ <setting key="activate_source" type="bool" value="1" label="36020" order="2" />
+ <setting key="wake_devices" type="string" value="0" label="36007" order="3" />
+ <setting key="standby_devices" type="string" value="0" label="36008" order="4" />
+ <setting key="cec_standby_screensaver" type="bool" value="1" label="36009" order="5" />
+ <setting key="standby_pc_on_tv_standby" type="enum" value="13011" label="36029" order="6" lvalues="36028|13005|13011" />
+ <setting key="standby_tv_on_pc_standby" type="bool" value="1" label="36026" order="7" />
+ <setting key="send_inactive_source" type="bool" value="1" label="36025" order="8" />
+ <setting key="use_tv_menu_language" type="bool" value="1" label="36018" order="9" />
+
+ <setting key="tv_vendor" type="int" value="0" configurable="0" />
+ <setting key="device_name" type="string" value="XBMC" configurable="0" />
+ <setting key="device_type" type="int" value="1" configurable="0" />
+ <setting key="physical_address" type="string" label="36021" value="0" configurable="0" />
+ <setting key="cec_hdmi_port" type="int" value="1" label="36015" configurable="0" />
+ <setting key="connected_device" type="int" label="36019" value="0" configurable="0" />
+ <setting key="port" type="string" value="" label="36022" configurable="0" />
+ </peripheral>
+
<peripheral vendor_product="2548:1001" bus="usb" name="Pulse-Eight CEC Adapter" mapTo="cec">
<setting key="enabled" type="bool" value="1" label="305" order="1" />
<setting key="activate_source" type="bool" value="1" label="36020" order="2" />
diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml
index 0267a5c612..76027b6315 100644
--- a/system/playercorefactory.xml
+++ b/system/playercorefactory.xml
@@ -11,7 +11,7 @@
<rules name="system rules">
<rule name="rtv" protocols="rtv" player="DVDPlayer" />
- <rule name="hdhomerun/myth/rtmp/mms/udp" protocols="hdhomerun|myth|cmyth|rtmp|mms|mmsh|udp" player="DVDPlayer" />
+ <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="DVDPlayer" />
<rule name="lastfm/shout" protocols="lastfm|shout" player="PAPlayer" />
<!-- dvdplayer can play standard rtsp streams -->
@@ -19,13 +19,12 @@
<!-- Internet streams -->
<rule name="streams" internetstream="true">
- <rule name="flv/aacp/sdp" mimetypes="video/x-flv|video-flv|audio/aacp|application/sdp" player="DVDPlayer" />
+ <rule name="aacp/sdp" mimetypes="audio/aacp|application/sdp" player="DVDPlayer" />
<rule name="mp2" mimetypes="application/octet-stream" filetypes="mp2" player="PAPlayer" />
</rule>
<!-- DVDs -->
<rule name="dvd" dvd="true" player="DVDPlayer" />
- <rule name="dvdfile" dvdfile="true" player="DVDPlayer" />
<rule name="dvdimage" dvdimage="true" player="DVDPlayer" />
<!-- Only dvdplayer can handle these normally -->
diff --git a/tools/android/depends/xbmc/Makefile b/tools/android/depends/xbmc/Makefile
index 38ca256899..f5de74080c 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 367386783e..c516346a23 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 0000000000..0bb195ef7d
--- /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 0000000000..e832e48ede
--- /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 0000000000..2a5ca9efdb
--- /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 0000000000..409d7e1529
--- /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 0000000000..38c5d6476c
--- /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 0000000000..5acc817257
--- /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 0000000000..a081187c9c
--- /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 0000000000..68d0ffeca5
--- /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 0000000000..eecdd7be98
--- /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 0000000000..e8e53a50b6
--- /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 0000000000..7030c19dfa
--- /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 0000000000..8898a56f83
--- /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 0000000000..ef00f815a5
--- /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 0000000000..7b627062ab
--- /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 0000000000..72f8fd72df
--- /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 0000000000..fb55937a1d
--- /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 0000000000..7ff491df8d
--- /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 0000000000..ebe049d59d
--- /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 0000000000..fbc1ee7dff
--- /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 0000000000..995f7b78ee
--- /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 <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <errno.h>
++
++/* --- 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 <stdio.h>
+ #include <stdlib.h>
+@@ -45,7 +45,7 @@
+ #include <sys/signal.h>
+ #include <fcntl.h>
+ #ifdef HAS_AO
+-#include <ao/ao.h>
++#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 <regex.h>
+ #include <sys/types.h>
+ #include <regex.h>
++#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 0000000000..495b97346c
--- /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 0000000000..c05970e138
--- /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 0000000000..1976904c4d
--- /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 0000000000..32bd282719
--- /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 0000000000..eb69d42b74
--- /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 0000000000..4d9b834f9c
--- /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 0000000000..6efd30222a
--- /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 0000000000..01ff026d07
--- /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 0000000000..a48782ed08
--- /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 0000000000..7993c0dafb
--- /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 0000000000..717e6efd35
--- /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 0000000000..b6edefee22
--- /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 0000000000..28b53ff265
--- /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 0000000000..7846462a03
--- /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 0000000000..b6b0a86fe2
--- /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 0000000000..716e6e3db7
--- /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 0000000000..48e2237089
--- /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 0000000000..e6539c38b7
--- /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 0000000000..f2c5bb8d1f
--- /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 0000000000..98212e2c4f
--- /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 0000000000..abc9f6c068
--- /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 0000000000..f930ba9c2c
--- /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 <debfx-pkg@fobos.de>
+
+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 0000000000..2ccbd701ac
--- /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.
+- // &#xA9; -- 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>foo&amp;#xa+bar</foo>");
++ std::string str;
++ str << xml;
++ XmlTest( "Entity escaping", "<foo>foo&amp;#xa+bar</foo>", 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 0000000000..fbc65ac728
--- /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 0000000000..5c81a31038
--- /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 0000000000..065ab0dec2
--- /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 0000000000..10026b1d94
--- /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 0000000000..9da68cbcb6
--- /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 93f76a1b9c..9b7a6a9c24 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 0d4bfbb378..9593c84e15 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 2e5a778066..5cf295d8c3 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 21724435a9..838600f3c2 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 e05e38adc4..0d6a15af0b 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 c7f2b489b6..d80613328e 100644
--- a/xbmc/cores/VideoRenderers/BaseRenderer.h
+++ b/xbmc/cores/VideoRenderers/BaseRenderer.h
@@ -120,6 +120,9 @@ protected:
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 87666e945b..9185327c55 100644
--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
@@ -205,7 +205,6 @@ protected:
bool m_bValidated;
std::vector<ERenderFormat> 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 33d737b0bd..a7d57eb89e 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 dd5eee05aa..7f9bc9c239 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 0000000000..8ae5d02e10
--- /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; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>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; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>i)>>((nbytes*8)-nbits-br->offbits);
+
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+// GStreamer h264 parser
+// Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+// (C) 2008 Wim Taymans <wim.taymans@gmail.com>
+// 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 (p<out && out<end && !out[-1])
+ out--;
+ return out;
+}
+
+const int CBitstreamConverter::avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ size = 0;
+ nal_start = avc_find_startcode(p, end);
+
+ for (;;) {
+ while (nal_start < end && !*(nal_start++));
+ if (nal_start == end)
+ break;
+
+ nal_end = avc_find_startcode(nal_start, end);
+ m_dllAvFormat->avio_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 <benoit.fouet@free.fr>
+ // 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 <benoit.fouet@free.fr>
+ // 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 <benoit.fouet@free.fr>
+ // 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 0000000000..e72f9caeb0
--- /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 <stdint.h>
+#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 <baptiste.coudurier@smartjog.com>
+// 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 0000000000..4bad4604c1
--- /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 <IL/OMX_Core.h>
+#include <IL/OMX_Component.h>
+#include <IL/OMX_Index.h>
+#include <IL/OMX_Image.h>
+#include <IL/OMX_Video.h>
+#include <IL/OMX_Broadcom.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+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 0000000000..0d6ec5c00e
--- /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 0000000000..6e57e402db
--- /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 0000000000..14d6056503
--- /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 0000000000..76d7049829
--- /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 <vector>
+#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 0000000000..b9f7ecb7e2
--- /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 <class AudioDataType>
+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 0000000000..e740e5b47c
--- /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 0000000000..bb4b91a473
--- /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 <sys/time.h>
+#include <inttypes.h>
+#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 0000000000..4a536c67ea
--- /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 <IL/OMX_Video.h>
+
+#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 0000000000..d1ab09dcbe
--- /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 <sstream>
+#include <iomanip>
+
+#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<OMXSelectionStream> COMXSelectionStreams::Get(StreamType type)
+{
+ std::vector<OMXSelectionStream> 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;i<count;i++)
+ {
+ OMXSelectionStream s;
+ s.source = source;
+ s.type = STREAM_AUDIO;
+ s.id = i;
+ s.name = nav->GetAudioStreamLanguage(i);
+ s.flags = CDemuxStream::FLAG_NONE;
+ s.filename = filename;
+ s.channels = 0;
+ Update(s);
+ }
+
+ count = nav->GetSubTitleStreamCount();
+ for(int i=0;i<count;i++)
+ {
+ OMXSelectionStream s;
+ s.source = source;
+ s.type = STREAM_SUBTITLE;
+ s.id = i;
+ s.name = nav->GetSubtitleStreamLanguage(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;i<count;i++)
+ {
+ CDemuxStream* stream = demuxer->GetStream(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<CStdString> 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<filenames.size();i++)
+ {
+ // if vobsub subtitle:
+ if (URIUtils::GetExtension(filenames[i]) == ".idx")
+ {
+ CStdString strSubFile;
+ if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
+ AddSubtitleFile(filenames[i], strSubFile);
+ }
+ else
+ {
+ if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
+ {
+ AddSubtitleFile(filenames[i]);
+ }
+ }
+ } // end loop over all subtitle files
+
+ g_settings.m_currentVideoSettings.m_SubtitleCached = true;
+ }
+
+ SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
+ SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
+ m_av_clock.Reset();
+ m_av_clock.OMXReset();
+ m_dvd.Clear();
+
+ return true;
+}
+
+bool COMXPlayer::OpenDemuxStream()
+{
+ if(m_pDemuxer)
+ SAFE_DELETE(m_pDemuxer);
+
+ CLog::Log(LOGNOTICE, "Creating Demuxer");
+
+ try
+ {
+ int attempts = 10;
+ while(!m_bStop && attempts-- > 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<CDVDInputStreamNavigator*>(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<CDVDInputStreamNavigator*>(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<CDVDInputStreamTV*>(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<CDVDMsgInt*>(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<CDVDInputStream::IChannel*>(m_pInputStream);
+ if(input)
+ {
+ g_infoManager.SetDisplayAfterSeek(100000);
+
+ bool result;
+ if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT))
+ result = input->SelectChannel(static_cast<CDVDMsgInt*>(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<CAction>*)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<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
+ if(aspect != 0.0)
+ {
+ hint.aspect = aspect;
+ hint.forced_aspect = true;
+ }
+ hint.software = true;
+ }
+
+ CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(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<CDVDDemuxVobsub> 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<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
+ return true; \
+ } \
+ } while(false)
+
+ CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(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<CDVDInputStream::IChannel*>(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<CDVDInputStream::IMenus*>(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<CDVDInputStream::IMenus*>(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<CDVDInputStreamTV*>(m_pInputStream)->CanRecord();
+ state.recording = static_cast<CDVDInputStreamTV*>(m_pInputStream)->IsRecording();
+ }
+
+ CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
+ if (pDisplayTime)
+ {
+ state.time = pDisplayTime->GetTime();
+ state.time_total = pDisplayTime->GetTotalTime();
+ }
+
+ if (dynamic_cast<CDVDInputStream::IMenus*>(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<CDVDInputStream::IChannel*>(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<CDemuxStreamAudio*>(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<CDemuxStreamVideo*>(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<CDemuxStreamVideo*>(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 0000000000..7896e95c6e
--- /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 <semaphore.h>
+#include <deque>
+
+#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<OMXSelectionStream> 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<OMXSelectionStream> 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<typename Compare> 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 0000000000..9bbadb7e7f
--- /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 <stdio.h>
+#include <unistd.h>
+#include <iomanip>
+
+#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 <iostream>
+#include <sstream>
+
+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<CDVDMsgDouble*>(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<CDVDMsgInt*>(pMsg)->m_value;
+ if (m_speed != DVD_PLAYSPEED_NORMAL)
+ {
+ m_syncclock = true;
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE))
+ {
+ m_silence = static_cast<CDVDMsgBool*>(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<COMXMsgAudioCodecChange*>(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 0000000000..51ac1c4b45
--- /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 <deque>
+#include <sys/types.h>
+
+#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 0000000000..cb975d308d
--- /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 <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#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<CDVDOverlayGroup*>(pOverlay)->m_overlays.begin()
+ , static_cast<CDVDOverlayGroup*>(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<CDVDMsgDouble*>(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<CDVDMsgInt*>(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<COMXMsgAudioCodecChange*>(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 0000000000..f4227d05de
--- /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 <deque>
+#include <sys/types.h>
+
+#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 0000000000..c0ac035d9b
--- /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 <sys/time.h>
+#include <inttypes.h>
+
+#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 0000000000..7bfca9f8ef
--- /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 <IL/OMX_Video.h>
+
+#include "BitstreamConverter.h"
+
+#include "OMXClock.h"
+
+#include "guilib/Geometry.h"
+#include "DVDDemuxers/DVDDemux.h"
+#include <string>
+
+#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 0000000000..6990f31df2
--- /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 <vector>
+
+// 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 0000000000..bea4c5a2c8
--- /dev/null
+++ b/xbmc/cores/omxplayer/omxplayer_advancedsettings.xml
@@ -0,0 +1,10 @@
+<advancedsettings>
+ <video>
+ <defaultplayer>omxplayer</defaultplayer>
+ <defaultdvdplayer>omxplayer</defaultdvdplayer>
+ </video>
+ <audio>
+ <defaultplayer>omxplayer</defaultplayer>
+ <streamsilence>false</streamsilence>
+ </audio>
+</advancedsettings>
diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h
index 486c09f0e1..c14a4c7e4c 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,12 +77,27 @@ public:
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 9a4506bbf9..96f2973823 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<CPlayerSelectionRule *>::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 69070e0908..0043ec6a5b 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 cc0ad48bd6..e2b72fdf51 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 c025fc4525..d622250069 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 a4118d6039..c3a29a8327 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 bddfa9abb1..931d8fafe9 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 dc8fbc4062..95bf9ab4d5 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 4c44c6ab56..f8557a5b30 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 adbee960d0..5cbf6ea47f 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 445b79c6c4..fa6f3e06d0 100644
--- a/xbmc/input/linux/LinuxInputDevices.h
+++ b/xbmc/input/linux/LinuxInputDevices.h
@@ -44,6 +44,7 @@ public:
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 40cfd0120c..3e4c94c540 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 0000000000..5cc20a7857
--- /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 <bcm_host.h>
+}
+
+#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 0000000000..a4b1ed6fbb
--- /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 <IL/OMX_Core.h>
+#include <IL/OMX_Component.h>
+#include <IL/OMX_Index.h>
+#include <IL/OMX_Image.h>
+#include <IL/OMX_Video.h>
+#include <IL/OMX_Broadcom.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+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 412357a74c..1e5e6b8cb0 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 0000000000..b8edf774bd
--- /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 0000000000..1327996dbb
--- /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 0000000000..938d79b07d
--- /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 <math.h>
+#include <sys/time.h>
+
+#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<ports.nPorts; j++)
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE portFormat;
+ OMX_INIT_STRUCTURE(portFormat);
+ portFormat.nPortIndex = ports.nStartPortNumber+j;
+
+ omx_err = OMX_GetParameter(m_handle, OMX_IndexParamPortDefinition, &portFormat);
+ if(omx_err != OMX_ErrorNone)
+ {
+ if(portFormat.bEnabled == OMX_FALSE)
+ continue;
+ }
+
+ omx_err = OMX_SendCommand(m_handle, OMX_CommandPortDisable, ports.nStartPortNumber+j, NULL);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "COMXCoreComponent::DisableAllPorts - Error disable port %d on component %s omx_err(0x%08x)",
+ (int)(ports.nStartPortNumber) + j, m_componentName.c_str(), (int)omx_err);
+ }
+ omx_err = WaitForCommand(OMX_CommandPortDisable, ports.nStartPortNumber+j);
+ if(omx_err != OMX_ErrorNone && omx_err != OMX_ErrorSameState)
+ {
+ UnLock();
+ return omx_err;
+ }
+ }
+ }
+ }
+
+ UnLock();
+
+ return OMX_ErrorNone;
+}
+
+void COMXCoreComponent::Remove(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2)
+{
+ for (std::vector<omx_event>::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<omx_event>::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<omx_event>::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<COMXCoreComponent*>(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<COMXCoreComponent*>(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<COMXCoreComponent*>(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<COMXCoreComponent*>(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<COMXCoreComponent*>(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<COMXCoreComponent*>(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 0000000000..c390b2662d
--- /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 <string>
+#include <queue>
+
+// TODO: should this be in configure
+#ifndef OMX_SKIP64BIT
+#define OMX_SKIP64BIT
+#endif
+
+#include "DllOMX.h"
+
+#include <semaphore.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// 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<omx_event> m_omx_events;
+
+ OMX_CALLBACKTYPE m_callbacks;
+
+ // OMXCore input buffers (demuxer packets)
+ pthread_mutex_t m_omx_input_mutex;
+ std::queue<OMX_BUFFERHEADERTYPE*> m_omx_input_avaliable;
+ std::vector<OMX_BUFFERHEADERTYPE*> 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<OMX_BUFFERHEADERTYPE*> m_omx_output_available;
+ std::vector<OMX_BUFFERHEADERTYPE*> 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 0000000000..3a07d1cae7
--- /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 0000000000..8d5b8a1479
--- /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 53831fc3f7..75dd3dabbe 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 ed08632ef5..07ac846baf 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<CPeripheral *> &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 2595a9cc97..ed62c99b10 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 0000000000..3cbc6e2d49
--- /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 <interface/vmcs_host/vc_cecservice.h>
+#include <interface/vchiq_arm/vchiq_if.h>
+}
+
+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 0000000000..f2b8877889
--- /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 0b7cf83d57..34d4c1a9ff 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 0980efb79e..88a2abf9ce 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 da952ba1ec..d9ea6d5f73 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 9726cf3a3f..22dcd90d4a 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 e777764c53..89acbc3265 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 8edd76c9bf..c8b8129e88 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<int,int> 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<int,int> 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<int,int> 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 584a2b3aeb..1999320381 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 90a87d40ea..5027117969 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 ca13609a96..041c9f0fd7 100644
--- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
@@ -78,7 +78,9 @@ void CGUIDialogVideoSettings::CreateSettings()
{
vector<pair<int, int> > 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<pair<int, int> > 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 7a1972fb4f..dac0f4bd10 100644
--- a/xbmc/video/windows/GUIWindowFullScreen.cpp
+++ b/xbmc/video/windows/GUIWindowFullScreen.cpp
@@ -1116,6 +1116,9 @@ void CGUIWindowFullScreen::RenderTTFSubtitles()
#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 303a5fe71e..be8874d088 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 e6b08e28e0..82571c97dc 100644
--- a/xbmc/windowing/WinSystem.h
+++ b/xbmc/windowing/WinSystem.h
@@ -114,6 +114,7 @@ protected:
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 2a42d859fc..a9fe48b850 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 4715c8ce88..559328cd40 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 7f0add58bc..c2d084150c 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 <unistd.h>
#include <string.h>
#include <stdio.h>
@@ -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 2f2a83bc55..497a90747b 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<CStdString> &resolutions)
+bool CWinEGLPlatformGeneric::ProbeDisplayResolutions(std::vector<RESOLUTION_INFO> &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 5aa657cabf..859f14ab73 100644
--- a/xbmc/windowing/egl/WinEGLPlatformGeneric.h
+++ b/xbmc/windowing/egl/WinEGLPlatformGeneric.h
@@ -27,6 +27,9 @@
#include <EGL/eglext.h>
#endif
+#include "guilib/gui3d.h"
+#include "guilib/Resolution.h"
+
class CWinEGLPlatformGeneric
{
public:
@@ -35,9 +38,9 @@ public:
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<CStdString> &resolutions);
+ virtual bool ProbeDisplayResolutions(std::vector<RESOLUTION_INFO> &resolutions);
virtual bool InitializeDisplay();
virtual bool UninitializeDisplay();
@@ -52,8 +55,13 @@ public:
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 @@ protected:
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 0000000000..9087a3d1cc
--- /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 <string>
+
+#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<RESOLUTION_INFO> &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<CWinEGLPlatformRaspberryPI*>(userdata);
+ callback->TvServiceCallback(reason, param1, param2);
+}
+
+void CWinEGLPlatformRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::vector<RESOLUTION_INFO> &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 0000000000..9ece1a8a2f
--- /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 <semaphore.h>
+
+#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<RESOLUTION_INFO> &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<RESOLUTION_INFO> &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<RESOLUTION_INFO> 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 b090ff526c..a1d67450ff 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<CStdString> resolutions;
+ CWinSystemBase::UpdateResolutions();
+
+ //std::vector<CStdString> resolutions;
+ std::vector<RESOLUTION_INFO> 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 ff3959af63..6bcdc573aa 100644
--- a/xbmc/windowing/egl/WinSystemGLES.h
+++ b/xbmc/windowing/egl/WinSystemGLES.h
@@ -56,6 +56,7 @@ public:
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 75c6677b13..fc38a28191 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;
}