diff --git a/configure.ac b/configure.ac
index 028915158e..f082b8c520 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1197,6 +1197,13 @@ if test "x$use_dbus" != "xno"; then
else
AC_MSG_NOTICE($dbus_disabled)
fi
+ #case "$host_vendor" != "apple"; determine availability of SDL or SDL2
+ PKG_CHECK_MODULES([SDL2], [sdl2],
+ [AC_DEFINE([HAVE_SDL_VERSION],[2],["SDL major version"])
+ AC_DEFINE([HAVE_SDL],[1],["Define to 1 if using sdl"])
+ INCLUDES="$INCLUDES $SDL2_CFLAGS"; LIBS="$LIBS $SDL2_LIBS"],
+ AC_MSG_RESULT("SDL enabled")
+ )
fi
XB_FIND_SONAME([ASS], [ass])
@@ -1225,6 +1232,16 @@ else
AC_MSG_RESULT($alsa_disabled)
fi
+# Steam Link
+AC_CHECK_HEADERS([SLVideo.h SLAudio.h], steamlink_found=yes,)
+if test "$steamlink_found" = "yes"; then
+ USE_STEAMLINK=1
+ use_pulse=no
+ AC_DEFINE([HAS_STEAMLINK], [], [Define if we are compiling with the Steam Link SDK])
+ LIBS="$LIBS -lSLVideo -lSLAudio"
+fi
+AC_SUBST(USE_STEAMLINK)
+
# PulseAudio
if test "x$use_pulse" != "xno"; then
if test "$host_vendor" = "apple" ; then
@@ -1615,7 +1632,7 @@ fi
if test "${USE_STATIC_FFMPEG}" = "1"; then
# get the libdir for static linking
- FFMPEG_LIBDIR=${pkg_cfg_prefix}$(PKG_CONFIG_SYSROOT_DIR="" ${PKG_CONFIG} --static --variable=libdir libavcodec)
+ FFMPEG_LIBDIR=$(${PKG_CONFIG} --static --variable=libdir libavcodec)
GNUTLS_ALL_LIBS=$(${PKG_CONFIG} --static --libs-only-l --silence-errors gnutls)
# check if static libs are available
diff --git a/system/addon-manifest.xml b/system/addon-manifest.xml
index 04cce30856..dff21e9eb9 100644
--- a/system/addon-manifest.xml
+++ b/system/addon-manifest.xml
@@ -28,7 +28,6 @@
screensaver.xbmc.builtin.black
screensaver.xbmc.builtin.dim
script.module.pil
- service.xbmc.versioncheck
skin.estuary
skin.estouchy
webinterface.default
diff --git a/tools/depends/.gitignore b/tools/depends/.gitignore
index c6c4d2515b..339a459ed4 100644
--- a/tools/depends/.gitignore
+++ b/tools/depends/.gitignore
@@ -18,6 +18,7 @@
/target/*/arm-linux-gnueabihf/*
/target/*/arm-linux-androideabi-*/*
/target/*/arm-linux-gnueabi/*
+/target/*/armv7a-cros-linux-gnueabi/*
/target/*/macosx*.*_x86_64-target/
/target/*/macosx*.*_x86_64-target/*
/target/*/macosx*.*_i386-target/
diff --git a/tools/depends/target/Makefile b/tools/depends/target/Makefile
index f0f0bf4f26..9378623af3 100644
--- a/tools/depends/target/Makefile
+++ b/tools/depends/target/Makefile
@@ -130,41 +130,41 @@ distclean::
for d in $(DEPENDS); do $(MAKE) -C $$d distclean; done
linux-system-libs-egl:
- [ -f $(PREFIX)/lib/pkgconfig/egl.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/egl.pc $(PREFIX)/lib/pkgconfig/egl.pc
- [ -f $(PREFIX)/lib/pkgconfig/damageproto.pc ] || ln -sf /usr/share/pkgconfig/damageproto.pc $(PREFIX)/lib/pkgconfig/damageproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/fixesproto.pc ] || ln -sf /usr/share/pkgconfig/fixesproto.pc $(PREFIX)/lib/pkgconfig/fixesproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/x11-xcb.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/x11-xcb.pc $(PREFIX)/lib/pkgconfig/x11-xcb.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-dri2.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-dri2.pc $(PREFIX)/lib/pkgconfig/xcb-dri2.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-dri3.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-dri3.pc $(PREFIX)/lib/pkgconfig/xcb-dri3.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-glx.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-glx.pc $(PREFIX)/lib/pkgconfig/xcb-glx.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-xfixes.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-xfixes.pc $(PREFIX)/lib/pkgconfig/xcb-xfixes.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-present.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-present.pc $(PREFIX)/lib/pkgconfig/xcb-present.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-randr.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-randr.pc $(PREFIX)/lib/pkgconfig/xcb-randr.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-render.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-render.pc $(PREFIX)/lib/pkgconfig/xcb-render.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-shape.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-shape.pc $(PREFIX)/lib/pkgconfig/xcb-shape.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb-sync.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-sync.pc $(PREFIX)/lib/pkgconfig/xcb-sync.pc
- [ -f $(PREFIX)/lib/pkgconfig/xdamage.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xdamage.pc $(PREFIX)/lib/pkgconfig/xdamage.pc
- [ -f $(PREFIX)/lib/pkgconfig/xf86vidmodeproto.pc ] || ln -sf /usr/share/pkgconfig/xf86vidmodeproto.pc $(PREFIX)/lib/pkgconfig/xf86vidmodeproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/xfixes.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xfixes.pc $(PREFIX)/lib/pkgconfig/xfixes.pc
- [ -f $(PREFIX)/lib/pkgconfig/xshmfence.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xshmfence.pc $(PREFIX)/lib/pkgconfig/xshmfence.pc
- [ -f $(PREFIX)/lib/pkgconfig/xxf86vm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xxf86vm.pc $(PREFIX)/lib/pkgconfig/xxf86vm.pc
+ [ -L $(PREFIX)/lib/pkgconfig/egl.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/egl.pc $(PREFIX)/lib/pkgconfig/egl.pc
+ [ -L $(PREFIX)/lib/pkgconfig/damageproto.pc ] || ln -sf /usr/share/pkgconfig/damageproto.pc $(PREFIX)/lib/pkgconfig/damageproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/fixesproto.pc ] || ln -sf /usr/share/pkgconfig/fixesproto.pc $(PREFIX)/lib/pkgconfig/fixesproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/x11-xcb.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/x11-xcb.pc $(PREFIX)/lib/pkgconfig/x11-xcb.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-dri2.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-dri2.pc $(PREFIX)/lib/pkgconfig/xcb-dri2.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-dri3.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-dri3.pc $(PREFIX)/lib/pkgconfig/xcb-dri3.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-glx.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-glx.pc $(PREFIX)/lib/pkgconfig/xcb-glx.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-xfixes.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-xfixes.pc $(PREFIX)/lib/pkgconfig/xcb-xfixes.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-present.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-present.pc $(PREFIX)/lib/pkgconfig/xcb-present.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-randr.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-randr.pc $(PREFIX)/lib/pkgconfig/xcb-randr.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-render.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-render.pc $(PREFIX)/lib/pkgconfig/xcb-render.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-shape.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-shape.pc $(PREFIX)/lib/pkgconfig/xcb-shape.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb-sync.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb-sync.pc $(PREFIX)/lib/pkgconfig/xcb-sync.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xdamage.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xdamage.pc $(PREFIX)/lib/pkgconfig/xdamage.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xf86vidmodeproto.pc ] || ln -sf /usr/share/pkgconfig/xf86vidmodeproto.pc $(PREFIX)/lib/pkgconfig/xf86vidmodeproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xfixes.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xfixes.pc $(PREFIX)/lib/pkgconfig/xfixes.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xshmfence.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xshmfence.pc $(PREFIX)/lib/pkgconfig/xshmfence.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xxf86vm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xxf86vm.pc $(PREFIX)/lib/pkgconfig/xxf86vm.pc
linux-system-libs: linux-system-libs-egl
- [ -f $(PREFIX)/lib/pkgconfig/x11.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/x11.pc $(PREFIX)/lib/pkgconfig/x11.pc
- [ -f $(PREFIX)/lib/pkgconfig/xproto.pc ] || ln -sf /usr/share/pkgconfig/xproto.pc $(PREFIX)/lib/pkgconfig/xproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/kbproto.pc ] || ln -sf /usr/share/pkgconfig/kbproto.pc $(PREFIX)/lib/pkgconfig/kbproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/xcb.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb.pc $(PREFIX)/lib/pkgconfig/xcb.pc
- [ -f $(PREFIX)/lib/pkgconfig/pthread-stubs.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/pthread-stubs.pc $(PREFIX)/lib/pkgconfig/pthread-stubs.pc
- [ -f $(PREFIX)/lib/pkgconfig/xau.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xau.pc $(PREFIX)/lib/pkgconfig/xau.pc
- [ -f $(PREFIX)/lib/pkgconfig/xdmcp.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xdmcp.pc $(PREFIX)/lib/pkgconfig/xdmcp.pc
- [ -f $(PREFIX)/lib/pkgconfig/xext.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xext.pc $(PREFIX)/lib/pkgconfig/xext.pc
- [ -f $(PREFIX)/lib/pkgconfig/xextproto.pc ] || ln -sf /usr/share/pkgconfig/xextproto.pc $(PREFIX)/lib/pkgconfig/xextproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/xrandr.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xrandr.pc $(PREFIX)/lib/pkgconfig/xrandr.pc
- [ -f $(PREFIX)/lib/pkgconfig/xrender.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xrender.pc $(PREFIX)/lib/pkgconfig/xrender.pc
- [ -f $(PREFIX)/lib/pkgconfig/randrproto.pc ] || ln -sf /usr/share/pkgconfig/randrproto.pc $(PREFIX)/lib/pkgconfig/randrproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/renderproto.pc ] || ln -sf /usr/share/pkgconfig/renderproto.pc $(PREFIX)/lib/pkgconfig/renderproto.pc
- [ -f $(PREFIX)/lib/pkgconfig/xt.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xt.pc $(PREFIX)/lib/pkgconfig/xt.pc
- [ -f $(PREFIX)/lib/pkgconfig/ice.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/ice.pc $(PREFIX)/lib/pkgconfig/ice.pc
- [ -f $(PREFIX)/lib/pkgconfig/sm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/sm.pc $(PREFIX)/lib/pkgconfig/sm.pc
- [ -f $(PREFIX)/lib/pkgconfig/xmu.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xmu.pc $(PREFIX)/lib/pkgconfig/xmu.pc
- [ -f $(PREFIX)/lib/pkgconfig/libdrm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/libdrm.pc $(PREFIX)/lib/pkgconfig/libdrm.pc
+ [ -L $(PREFIX)/lib/pkgconfig/x11.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/x11.pc $(PREFIX)/lib/pkgconfig/x11.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xproto.pc ] || ln -sf /usr/share/pkgconfig/xproto.pc $(PREFIX)/lib/pkgconfig/xproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/kbproto.pc ] || ln -sf /usr/share/pkgconfig/kbproto.pc $(PREFIX)/lib/pkgconfig/kbproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xcb.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xcb.pc $(PREFIX)/lib/pkgconfig/xcb.pc
+ [ -L $(PREFIX)/lib/pkgconfig/pthread-stubs.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/pthread-stubs.pc $(PREFIX)/lib/pkgconfig/pthread-stubs.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xau.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xau.pc $(PREFIX)/lib/pkgconfig/xau.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xdmcp.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xdmcp.pc $(PREFIX)/lib/pkgconfig/xdmcp.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xext.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xext.pc $(PREFIX)/lib/pkgconfig/xext.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xextproto.pc ] || ln -sf /usr/share/pkgconfig/xextproto.pc $(PREFIX)/lib/pkgconfig/xextproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xrandr.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xrandr.pc $(PREFIX)/lib/pkgconfig/xrandr.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xrender.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xrender.pc $(PREFIX)/lib/pkgconfig/xrender.pc
+ [ -L $(PREFIX)/lib/pkgconfig/randrproto.pc ] || ln -sf /usr/share/pkgconfig/randrproto.pc $(PREFIX)/lib/pkgconfig/randrproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/renderproto.pc ] || ln -sf /usr/share/pkgconfig/renderproto.pc $(PREFIX)/lib/pkgconfig/renderproto.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xt.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xt.pc $(PREFIX)/lib/pkgconfig/xt.pc
+ [ -L $(PREFIX)/lib/pkgconfig/ice.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/ice.pc $(PREFIX)/lib/pkgconfig/ice.pc
+ [ -L $(PREFIX)/lib/pkgconfig/sm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/sm.pc $(PREFIX)/lib/pkgconfig/sm.pc
+ [ -L $(PREFIX)/lib/pkgconfig/xmu.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/xmu.pc $(PREFIX)/lib/pkgconfig/xmu.pc
+ [ -L $(PREFIX)/lib/pkgconfig/libdrm.pc ] || ln -sf /usr/lib/$(HOST)/pkgconfig/libdrm.pc $(PREFIX)/lib/pkgconfig/libdrm.pc
diff --git a/tools/depends/target/ffmpeg/Makefile b/tools/depends/target/ffmpeg/Makefile
index eaf9fc4931..8dfbb48b3d 100644
--- a/tools/depends/target/ffmpeg/Makefile
+++ b/tools/depends/target/ffmpeg/Makefile
@@ -8,7 +8,7 @@ APPLY_PATCHES=no
# configuration settings
ffmpg_config = --prefix=$(PREFIX) --extra-version="kodi-$(VERSION)"
-ffmpg_config += --cc=$(CC) --cxx=$(CXX) --ar=$(AR) --ranlib=$(RANLIB)
+ffmpg_config += --cc="$(CC)" --cxx="$(CXX)" --ar=$(AR) --ranlib=$(RANLIB)
ffmpg_config += --disable-devices --disable-doc
ffmpg_config += --disable-ffplay --disable-ffmpeg --disable-sdl
ffmpg_config += --disable-ffprobe --disable-ffserver
diff --git a/tools/depends/target/libsdl2/Makefile b/tools/depends/target/libsdl2/Makefile
index 6c6eb5a26a..4c88e60e05 100644
--- a/tools/depends/target/libsdl2/Makefile
+++ b/tools/depends/target/libsdl2/Makefile
@@ -8,7 +8,7 @@ SOURCE=$(LIBNAME)-$(VERSION)
ARCHIVE=$(SOURCE).tar.gz
# configuration settings
-CONFIGURE=./configure --prefix=$(PREFIX) --disable-video-directfb
+CONFIGURE=./configure --prefix=$(PREFIX) --disable-video-directfb --without-x --disable-video-x11
ifneq ($(OS),linux)
CONFIGURE += --without-x --disable-video-x11
endif
diff --git a/tools/depends/target/openssl/Makefile b/tools/depends/target/openssl/Makefile
index def05255ed..dcae452bcf 100644
--- a/tools/depends/target/openssl/Makefile
+++ b/tools/depends/target/openssl/Makefile
@@ -33,7 +33,7 @@ $(TARBALLS_LOCATION)/$(ARCHIVE):
$(PLATFORM): $(TARBALLS_LOCATION)/$(ARCHIVE) $(DEPS)
rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); AR="$(AR)" CFLAGS="$(CFLAGS)" CC=$(CC) RANLIB=$(RANLIB) $(CONFIGURE)
+ cd $(PLATFORM); AR="$(AR)" CFLAGS="$(CFLAGS)" CC="$(CC)" RANLIB=$(RANLIB) $(CONFIGURE)
if test "$(OS)" = "osx"; then \
sed -ie "s|CC= /usr/bin/gcc-4.2|CC= $(CC)|" "$(PLATFORM)/Makefile"; \
sed -ie "s|CFLAG= |CFLAG=$(CFLAGS) |" "$(PLATFORM)/Makefile"; \
diff --git a/tools/depends/target/zlib/Makefile b/tools/depends/target/zlib/Makefile
index 551301ca57..2145116dea 100644
--- a/tools/depends/target/zlib/Makefile
+++ b/tools/depends/target/zlib/Makefile
@@ -7,7 +7,7 @@ VERSION=1.2.7
SOURCE=$(LIBNAME)-$(VERSION)
ARCHIVE=$(SOURCE).tar.gz
# configuration settings
-CONFIGURE= CC="$(CC)" CFLAGS="$(CFLAGS)" ./configure --prefix=$(PREFIX) --static
+CONFIGURE= CC="$(CC)" CFLAGS="$(CFLAGS) -fPIC" ./configure --prefix=$(PREFIX) --static
LIBDYLIB=$(PLATFORM)/$(LIBNAME).a
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index b8ff91b427..1b8a8c2e93 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -2238,7 +2238,7 @@ bool CApplication::OnAction(const CAction &action)
m_pPlayer->SetPlaySpeed(1);
return true;
}
- if (!m_pPlayer->IsPaused())
+ if (!m_pPlayer->IsPaused() && m_pPlayer->CanFFRW())
{
if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND)
{
diff --git a/xbmc/ApplicationPlayer.cpp b/xbmc/ApplicationPlayer.cpp
index 9163bae34b..13e6b79d7b 100644
--- a/xbmc/ApplicationPlayer.cpp
+++ b/xbmc/ApplicationPlayer.cpp
@@ -405,6 +405,12 @@ bool CApplicationPlayer::CanPause()
return (player && player->CanPause());
}
+bool CApplicationPlayer::CanFFRW()
+{
+ std::shared_ptr player = GetInternal();
+ return (player && player->CanFFRW());
+}
+
bool CApplicationPlayer::IsRecording() const
{
std::shared_ptr player = GetInternal();
diff --git a/xbmc/ApplicationPlayer.h b/xbmc/ApplicationPlayer.h
index a0567a6727..7d55035921 100644
--- a/xbmc/ApplicationPlayer.h
+++ b/xbmc/ApplicationPlayer.h
@@ -106,6 +106,7 @@ class CApplicationPlayer
bool CanPause();
bool CanRecord();
bool CanSeek();
+ bool CanFFRW();
void DoAudioWork();
void GetAudioCapabilities(std::vector &audioCaps);
int GetAudioStream();
diff --git a/xbmc/cores/AudioEngine/AESinkFactory.cpp b/xbmc/cores/AudioEngine/AESinkFactory.cpp
index 952461d29b..4ebed1fcbe 100644
--- a/xbmc/cores/AudioEngine/AESinkFactory.cpp
+++ b/xbmc/cores/AudioEngine/AESinkFactory.cpp
@@ -42,6 +42,9 @@
#if defined(HAS_PULSEAUDIO)
#include "Sinks/AESinkPULSE.h"
#endif
+ #if defined(HAS_STEAMLINK)
+ #include "Sinks/AESinkSteamLink.h"
+ #endif
#else
#pragma message("NOTICE: No audio sink for target platform. Audio output will not be available.")
#endif
@@ -83,6 +86,9 @@ void CAESinkFactory::ParseDevice(std::string &device, std::string &driver)
#if defined(HAS_PULSEAUDIO)
driver == "PULSE" ||
#endif
+ #if defined(HAS_STEAMLINK)
+ driver == "STEAMLINK" ||
+ #endif
#endif
driver == "PROFILER" ||
driver == "NULL")
@@ -133,6 +139,10 @@ IAESink *CAESinkFactory::TrySink(std::string &driver, std::string &device, AEAud
if (driver == "OSS")
sink = new CAESinkOSS();
#endif
+ #if defined(HAS_STEAMLINK)
+ if (driver == "STEAMLINK")
+ sink = new STEAMLINK::CAESinkSteamLink();
+ #endif
#endif
}
@@ -250,6 +260,10 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
if (envSink == "OSS")
CAESinkOSS::EnumerateDevicesEx(info.m_deviceInfoList, force);
#endif
+ #if defined(HAS_STEAMLINK)
+ if (envSink == "STEAMLINK")
+ STEAMLINK::CAESinkSteamLink::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ #endif
if(!info.m_deviceInfoList.empty())
{
@@ -290,6 +304,17 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
list.push_back(info);
#endif
+ #if defined(HAS_STEAMLINK)
+ info.m_deviceInfoList.clear();
+ info.m_sinkName = "STEAMLINK";
+ STEAMLINK::CAESinkSteamLink::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ if(!info.m_deviceInfoList.empty())
+ {
+ list.push_back(info);
+ return;
+ }
+ #endif
+
#endif
}
diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in
index edb6b110bf..7004f46aed 100644
--- a/xbmc/cores/AudioEngine/Makefile.in
+++ b/xbmc/cores/AudioEngine/Makefile.in
@@ -63,6 +63,11 @@ SRCS += Sinks/AESinkOSS.cpp
ifeq (@USE_PULSE@,1)
SRCS += Sinks/AESinkPULSE.cpp
endif
+ifeq (@USE_STEAMLINK@,1)
+SRCS += Sinks/AESinkSteamLink.cpp
+SRCS += Sinks/AESinkSteamLinkStream.cpp
+SRCS += Sinks/AESinkSteamLinkTranslator.cpp
+endif
endif
SRCS += Engines/ActiveAE/AudioDSPAddons/ActiveAEDSP.cpp
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.cpp
new file mode 100644
index 0000000000..63fc7d376d
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "AESinkSteamLink.h"
+#include "AESinkSteamLinkStream.h"
+#include "AESinkSteamLinkTranslator.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+
+// Steam Link audio API
+#include "SLAudio.h"
+
+#include
+#include
+
+#define SL_SAMPLE_RATE 48000
+#define SINK_FEED_MS 50 // Steam Link game streaming uses 10ms
+#define CACHE_TOTAL_MS 200
+#define INITIAL_ATTENUATION_TIME_SECS 6.0
+
+using namespace STEAMLINK;
+
+namespace
+{
+ void LogFunction(void *pContext, ESLAudioLog eLogLevel, const char *pszMessage)
+ {
+ int level = CAESinkSteamLinkTranslator::TranslateLogLevel(eLogLevel);
+ CLog::Log(level, "%s", pszMessage);
+ }
+}
+
+CAESinkSteamLink::CAESinkSteamLink() :
+ m_context(nullptr),
+ m_startTimeSecs(0.0),
+ m_framesSinceStart(0)
+{
+ SLAudio_SetLogLevel(k_ESLAudioLogDebug);
+ SLAudio_SetLogFunction(LogFunction, nullptr);
+}
+
+CAESinkSteamLink::~CAESinkSteamLink()
+{
+ Deinitialize();
+ SLAudio_SetLogFunction(nullptr, nullptr);
+}
+
+bool CAESinkSteamLink::Initialize(AEAudioFormat &format, std::string &device)
+{
+ bool bSuccess = false;
+
+ Deinitialize();
+
+ m_startTimeSecs = 0.0; // Set in first call to AddPackets()
+ m_framesSinceStart = 0;
+
+ format.m_dataFormat = AE_FMT_S16NE;
+ format.m_sampleRate = SL_SAMPLE_RATE;
+ format.m_frames = format.m_sampleRate * SINK_FEED_MS / 1000;
+ format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+
+ if (format.m_channelLayout.Count() == 0 || format.m_frameSize == 0)
+ return false;
+
+ CSLAudioContext* context = SLAudio_CreateContext();
+ if (!context)
+ {
+ CLog::Log(LOGERROR, "SteamLinkAudio: Failed to create context");
+ }
+ else
+ {
+ std::unique_ptr stream(new CAESinkSteamLinkStream(context, format.m_sampleRate, format.m_channelLayout.Count(), format.m_frames * format.m_frameSize));
+ if (stream->Open())
+ {
+ m_format = format;
+ m_context = context;
+ m_stream = std::move(stream);
+ bSuccess = true;
+ }
+ else
+ {
+ SLAudio_FreeContext(context);
+ }
+ }
+
+ return bSuccess;
+}
+
+void CAESinkSteamLink::Deinitialize()
+{
+ m_stream.reset();
+
+ if (m_context)
+ {
+ SLAudio_FreeContext(m_context);
+ m_context = nullptr;
+ }
+}
+
+double CAESinkSteamLink::GetCacheTotal()
+{
+ return CACHE_TOTAL_MS / 1000.0;
+}
+
+unsigned int CAESinkSteamLink::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
+{
+ if (offset >= frames)
+ return 0;
+
+ if (!m_stream)
+ return 0;
+
+ // Calculate frame count from given parameters
+ const unsigned int frameCount = frames - offset;
+
+ // Calculate start time and present time
+ const double nowSecs = static_cast(CurrentHostCounter()) / CurrentHostFrequency();
+
+ if (m_startTimeSecs == 0.0)
+ m_startTimeSecs = nowSecs;
+
+ double presentTimeSecs = m_startTimeSecs + static_cast(m_framesSinceStart) / m_format.m_sampleRate;
+
+ // Detect underrun
+ if (presentTimeSecs < nowSecs)
+ {
+ CLog::Log(LOGDEBUG, "SteamLinkAudio: Buffer underrun detected");
+ presentTimeSecs = m_startTimeSecs = nowSecs;
+ m_framesSinceStart = 0;
+ }
+
+ // Ensure space in the buffer
+ const double delaySecs = presentTimeSecs - nowSecs;
+
+ const double availableSecs = GetCacheTotal() - delaySecs;
+
+ const int sleepTimeUs = static_cast((SINK_FEED_MS - availableSecs * 1000.0) * 1000);
+
+ if (sleepTimeUs > 0)
+ usleep(sleepTimeUs);
+
+ // Create buffer and copy data
+ const size_t packetSize = frameCount * m_format.m_frameSize;
+ std::unique_ptr buffer(new uint8_t[packetSize]);
+
+ std::memcpy(buffer.get(), *data + offset * m_format.m_frameSize, packetSize);
+
+ // Attenuate if necessary
+ const double elapsedSinceStartSecs = (presentTimeSecs - m_startTimeSecs);
+ const bool bAttenuate = (elapsedSinceStartSecs < INITIAL_ATTENUATION_TIME_SECS);
+ if (bAttenuate)
+ {
+ double flVolume = elapsedSinceStartSecs / INITIAL_ATTENUATION_TIME_SECS;
+ AttenuateChunk(buffer.get(), packetSize, flVolume * flVolume);
+ }
+
+ // Add packet
+ if (!m_stream->AddPacket(std::move(buffer), packetSize, presentTimeSecs))
+ return 0;
+
+ m_framesSinceStart += frameCount;
+
+ return frameCount;
+}
+
+void CAESinkSteamLink::GetDelay(AEDelayStatus &status)
+{
+ double delaySecs = 0.0;
+
+ if (m_startTimeSecs != 0.0)
+ {
+ const double nowSecs = static_cast(CurrentHostCounter()) / CurrentHostFrequency();
+
+ double nextPresentTimeSecs = m_startTimeSecs + static_cast(m_framesSinceStart) / m_format.m_sampleRate;
+
+ if (nextPresentTimeSecs > nowSecs)
+ delaySecs = nextPresentTimeSecs - nowSecs;
+ }
+
+ status.SetDelay(delaySecs);
+}
+
+void CAESinkSteamLink::Drain()
+{
+ if (m_stream)
+ {
+ if (!m_stream->Flush())
+ m_stream.reset();
+ }
+
+ m_startTimeSecs = 0.0;
+ m_framesSinceStart = 0;
+}
+
+void CAESinkSteamLink::AttenuateChunk(uint8_t *pChunk, unsigned int size, double flVolume)
+{
+ int16_t *pData = reinterpret_cast(pChunk);
+ int nCount = size / sizeof(*pData);
+ while (nCount--)
+ {
+ *pData = static_cast(*pData * flVolume);
+ ++pData;
+ }
+}
+
+void CAESinkSteamLink::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force /* = false */)
+{
+ CAEDeviceInfo info;
+
+ info.m_deviceType = AE_DEVTYPE_PCM;
+ info.m_deviceName = "SteamLink";
+ info.m_displayName = "Steam Link Low Latency Audio";
+ info.m_displayNameExtra = "";
+ info.m_channels += AE_CH_FL;
+ info.m_channels += AE_CH_FR;
+ info.m_sampleRates.push_back(SL_SAMPLE_RATE);
+ info.m_dataFormats.push_back(AE_FMT_S16NE);
+
+ deviceInfoList.push_back(info);
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.h b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.h
new file mode 100644
index 0000000000..ffe053d2a3
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLink.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "cores/AudioEngine/Interfaces/AESink.h"
+#include "cores/AudioEngine/Utils/AEAudioFormat.h"
+#include "cores/AudioEngine/Utils/AEDeviceInfo.h"
+
+#include
+#include
+
+#define STEAM_LINK_SINK_NAME "SteamLinkAudio"
+
+struct CSLAudioContext;
+
+namespace STEAMLINK
+{
+class CAESinkSteamLinkStream;
+
+class CAESinkSteamLink : public IAESink
+{
+public:
+ virtual const char* GetName() override { return STEAM_LINK_SINK_NAME; }
+
+ CAESinkSteamLink();
+ virtual ~CAESinkSteamLink();
+
+ // implementation of IAESink
+ virtual bool Initialize(AEAudioFormat &format, std::string &device) override;
+ virtual void Deinitialize() override;
+ virtual double GetCacheTotal() override;
+ virtual unsigned int AddPackets(uint8_t **data, unsigned int frames, unsigned int offset) override;
+ virtual void GetDelay(AEDelayStatus &status) override;
+ virtual void Drain() override;
+
+ static void EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force = false);
+
+private:
+ void AttenuateChunk(uint8_t *pChunk, unsigned int size, double flVolume);
+
+ // Steam Link stuff
+ CSLAudioContext *m_context;
+ std::unique_ptr m_stream;
+
+ // AE stuff
+ AEAudioFormat m_format;
+ double m_startTimeSecs;
+ uint64_t m_framesSinceStart;
+};
+
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.cpp
new file mode 100644
index 0000000000..a213bd3c6d
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "AESinkSteamLinkStream.h"
+#include "AESinkSteamLink.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.h"
+#include "interfaces/AnnouncementManager.h"
+#include "threads/SingleLock.h"
+//#include "threads/SystemClock.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+
+// Steam Link audio API
+#include "SLAudio.h"
+
+#include
+#include
+#include
+
+using namespace STEAMLINK;
+
+#define MAX_AUDIO_DELAY_MS 100 // Skip packets if audio delay exceeds this value
+#define SL_INTRINSIC_DELAY_MS 250 // Observed audio delay while playing video
+
+CAESinkSteamLinkStream::CAESinkSteamLinkStream(CSLAudioContext* context, unsigned int sampleRateHz, unsigned int channels, unsigned int packetSize) :
+ CThread("SteamLinkAudio"),
+ m_context(context),
+ m_sampleRateHz(sampleRateHz),
+ m_channels(channels),
+ m_packetSize(packetSize),
+ m_stream(nullptr),
+ m_steamLinkBuffer(nullptr),
+ m_remainingBytes(0)
+{
+ ANNOUNCEMENT::CAnnouncementManager::GetInstance().AddAnnouncer(this);
+}
+
+CAESinkSteamLinkStream::~CAESinkSteamLinkStream()
+{
+ ANNOUNCEMENT::CAnnouncementManager::GetInstance().RemoveAnnouncer(this);
+
+ Close();
+}
+
+bool CAESinkSteamLinkStream::Open()
+{
+ Close();
+
+ CSingleLock lock(m_streamMutex);
+
+ bool bSuccess = false;
+
+ m_stream = SLAudio_CreateStream(m_context, m_sampleRateHz, m_channels, m_packetSize, true);
+
+ if (m_stream)
+ {
+ bSuccess = true;
+ lock.Leave();
+ Create(false);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "SteamLinkAudio: Failed to create stream");
+ }
+
+ return bSuccess;
+}
+
+void CAESinkSteamLinkStream::Close()
+{
+ StopThread();
+
+ CSingleLock lock(m_streamMutex);
+
+ if (m_stream)
+ {
+ SLAudio_FreeStream(m_stream);
+ m_stream = nullptr;
+ }
+}
+
+bool CAESinkSteamLinkStream::Flush()
+{
+ bool bOpen = false;
+
+ // Causes AddPacket() to return immediately
+ {
+ CSingleLock lock(m_streamMutex);
+ if (m_stream)
+ {
+ bOpen = true;
+ SLAudio_FreeStream(m_stream);
+ m_stream = nullptr;
+ }
+ }
+
+ // Clear queue
+ {
+ CSingleLock lock(m_queueMutex);
+ m_queue.clear();
+ }
+
+ // Reopen stream
+ bool bSuccess = true;
+ if (bOpen)
+ {
+ CSingleLock lock(m_streamMutex);
+ m_stream = SLAudio_CreateStream(m_context, m_sampleRateHz, m_channels, m_packetSize, true);
+ bSuccess = (m_stream != nullptr);
+ }
+
+ return bSuccess;
+}
+
+bool CAESinkSteamLinkStream::AddPacket(std::unique_ptr data, unsigned int size, double presentTimeSecs)
+{
+ {
+ CSingleLock lock(m_streamMutex);
+ if (m_stream == nullptr)
+ return true; // This might have been called during a Flush()
+ }
+
+ int delayMs = CSteamLinkVideo::GetDelayMs();
+
+ delayMs -= SL_INTRINSIC_DELAY_MS;
+
+ if (delayMs > 0)
+ presentTimeSecs += delayMs / 1000.0;
+
+ {
+ CSingleLock lock(m_queueMutex);
+ m_queue.emplace_back(std::move(data), size, presentTimeSecs);
+ }
+
+ // Notify thread that a packet is ready
+ m_queueEvent.Set();
+
+ return true;
+}
+
+void CAESinkSteamLinkStream::Process()
+{
+ AudioPacket packet;
+
+ while (!m_bStop)
+ {
+ if (packet.buffer || GetNextPacket(packet))
+ {
+ // Sleep until we're ready to show the frame
+ WaitUntilReady(packet.presentTimeSecs);
+
+ if (m_bStop)
+ break;
+
+ SendPacket(std::move(packet));
+
+ if (GetNextPacket(packet))
+ continue;
+ }
+
+ AbortableWait(m_queueEvent);
+ }
+
+ // Make sure we haven't left a Steam Link packet open
+ if (m_steamLinkBuffer != nullptr)
+ EndPacket();
+}
+
+void CAESinkSteamLinkStream::Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+{
+ if (flag == ANNOUNCEMENT::Player && strcmp(sender, "xbmc") == 0)
+ {
+ if (strcmp(message, "OnPlay") == 0 ||
+ strcmp(message, "OnPause") == 0 ||
+ strcmp(message, "OnStop") == 0 ||
+ strcmp(message, "OnSpeedChanged") == 0 ||
+ strcmp(message, "OnSeek") == 0)
+ {
+ Flush();
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "CAESinkSteamLinkStream: Unknown player announcement \"%s\"", message);
+ }
+ }
+}
+
+bool CAESinkSteamLinkStream::GetNextPacket(AudioPacket& packet)
+{
+ CSingleLock lock(m_queueMutex);
+
+ if (!m_queue.empty())
+ {
+ packet = std::move(m_queue.front());
+ m_queue.pop_front();
+ return true;
+ }
+
+ return false;
+}
+
+void CAESinkSteamLinkStream::WaitUntilReady(double targetTimeSecs)
+{
+ const double nowSecs = static_cast(CurrentHostCounter()) / CurrentHostFrequency();
+
+ const int sleepTimeMs = static_cast((targetTimeSecs - nowSecs) * 1000.0);
+
+ if (sleepTimeMs > 0)
+ Sleep(sleepTimeMs);
+}
+
+bool CAESinkSteamLinkStream::HasLatePacket(double nextPresentTimeSecs) const
+{
+ return std::find_if(m_queue.begin(), m_queue.end(),
+ [nextPresentTimeSecs](const AudioPacket& packet)
+ {
+ return packet.presentTimeSecs >= nextPresentTimeSecs;
+ }) != m_queue.end();
+}
+
+void CAESinkSteamLinkStream::ClearLatePackets(double nextPresentTimeSecs)
+{
+ while (HasLatePacket(nextPresentTimeSecs))
+ m_queue.pop_front();
+}
+
+void CAESinkSteamLinkStream::BeginPacket()
+{
+ m_steamLinkBuffer = static_cast(SLAudio_BeginFrame(m_stream));
+ m_remainingBytes = m_packetSize;
+}
+
+void CAESinkSteamLinkStream::EndPacket()
+{
+ SLAudio_SubmitFrame(m_stream);
+ m_steamLinkBuffer = nullptr;
+ m_remainingBytes = 0;
+}
+
+void CAESinkSteamLinkStream::SendPacket(AudioPacket packet)
+{
+ CSingleLock lock(m_streamMutex);
+
+ if (m_stream == nullptr)
+ return;
+
+ if (GetSLDelaySecs() > MAX_AUDIO_DELAY_MS)
+ {
+ // Flush() grabs the queue mutex, so don't hold the stream mutex
+ CSingleExit exit(m_streamMutex);
+ if (!Flush())
+ return;
+ }
+
+ if (m_stream == nullptr)
+ return;
+
+ unsigned int bytesWritten = 0;
+
+ // Loop until all bytes have been written
+ while (bytesWritten < packet.size)
+ {
+ if (m_steamLinkBuffer == nullptr)
+ BeginPacket();
+
+ const unsigned int bytesToWrite = std::min(m_remainingBytes, packet.size - bytesWritten);
+
+ // Sanity check (shouldn't happen)
+ if (bytesToWrite == 0 || m_remainingBytes == 0)
+ break;
+
+ const unsigned int bufferOffset = m_packetSize - m_remainingBytes;
+ std::memcpy(m_steamLinkBuffer + bufferOffset, packet.buffer.get() + bytesWritten, bytesToWrite);
+
+ m_remainingBytes -= bytesToWrite;
+ bytesWritten += bytesToWrite;
+
+ if (m_remainingBytes == 0)
+ EndPacket();
+ }
+}
+
+double CAESinkSteamLinkStream::GetSLDelaySecs()
+{
+ uint32_t queuedFrames = 0;
+
+ if (m_stream)
+ queuedFrames = SLAudio_GetQueuedAudioSamples(m_stream) / m_channels;
+
+ return static_cast(queuedFrames) / m_sampleRateHz;
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.h b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.h
new file mode 100644
index 0000000000..93a344d778
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkStream.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "interfaces/IAnnouncer.h"
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+#include "threads/Thread.h"
+
+#include
+#include
+#include
+
+struct CSLAudioContext;
+struct CSLAudioStream;
+
+namespace STEAMLINK
+{
+
+class CAESinkSteamLinkStream : public CThread,
+ public ANNOUNCEMENT::IAnnouncer
+{
+public:
+ CAESinkSteamLinkStream(CSLAudioContext* context, unsigned int sampleRateHz, unsigned int channels, unsigned int packetSize);
+ virtual ~CAESinkSteamLinkStream();
+
+ bool Open();
+ void Close();
+ bool Flush();
+ bool AddPacket(std::unique_ptr data, unsigned int size, double presentTimeSecs);
+
+ // implementation of IAnnouncer
+ virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data) override;
+
+protected:
+ // implementation of CThread
+ virtual void Process() override;
+
+private:
+ struct AudioPacket
+ {
+ AudioPacket() :
+ size(0),
+ presentTimeSecs(0.0)
+ {
+ }
+
+ AudioPacket(std::unique_ptr buffer, unsigned int size, double presentTimeSecs) :
+ buffer(std::move(buffer)),
+ size(size),
+ presentTimeSecs(presentTimeSecs)
+ {
+ }
+
+ std::unique_ptr buffer;
+ unsigned int size;
+ double presentTimeSecs;
+ };
+
+ bool GetNextPacket(AudioPacket& packet);
+
+ void WaitUntilReady(double targetTimeSecs);
+
+ bool HasLatePacket(double nextPresentTimeSecs) const;
+
+ void ClearLatePackets(double nextPresentTimeSecs);
+
+ void BeginPacket();
+ void EndPacket();
+
+ void SendPacket(AudioPacket packet);
+
+ double GetSLDelaySecs();
+
+ // Construction parameters
+ CSLAudioContext* const m_context;
+ const unsigned int m_sampleRateHz;
+ const unsigned int m_channels;
+ const unsigned int m_packetSize;
+
+ // Stream parameters
+ CSLAudioStream* m_stream;
+ CCriticalSection m_streamMutex;
+ std::deque m_queue;
+ CCriticalSection m_queueMutex;
+ CEvent m_queueEvent;
+ uint8_t* m_steamLinkBuffer;
+ unsigned int m_remainingBytes; // Bytes remaining in the Steam Link audio buffer
+ XbmcThreads::EndTime m_videoDelay;
+};
+
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.cpp
new file mode 100644
index 0000000000..77270c62f1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "AESinkSteamLinkTranslator.h"
+#include "commons/ilog.h"
+
+using namespace STEAMLINK;
+
+int CAESinkSteamLinkTranslator::TranslateLogLevel(ESLAudioLog logLevel)
+{
+ switch (logLevel)
+ {
+ case k_ESLAudioLogDebug: return LOGDEBUG;
+ case k_ESLAudioLogInfo: return LOGINFO;
+ case k_ESLAudioLogWarning: return LOGWARNING;
+ case k_ESLAudioLogError: return LOGERROR;
+ break;
+ default:
+ break;
+ }
+
+ return LOGDEBUG;
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.h b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.h
new file mode 100644
index 0000000000..b999858380
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkSteamLinkTranslator.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "SLAudio.h"
+
+namespace STEAMLINK
+{
+
+class CAESinkSteamLinkTranslator
+{
+public:
+ static int TranslateLogLevel(ESLAudioLog logLevel);
+};
+
+}
diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h
index a1ea99f2e1..32a5fc9c9a 100644
--- a/xbmc/cores/IPlayer.h
+++ b/xbmc/cores/IPlayer.h
@@ -246,6 +246,7 @@ class IPlayer
virtual bool HasRDS() const { return false; }
virtual bool IsPassthrough() const { return false;}
virtual bool CanSeek() {return true;}
+ virtual bool CanFFRW() { return true; }
virtual void Seek(bool bPlus = true, bool bLargeStep = false, bool bChapterOverride = false) = 0;
virtual bool SeekScene(bool bPlus = true) {return false;}
virtual void SeekPercentage(float fPercent = 0){}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.cpp
index 9717412256..cade549d5b 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.cpp
@@ -41,6 +41,9 @@
#include "Video/DVDVideoCodecAndroidMediaCodec.h"
#include "platform/android/activity/AndroidFeatures.h"
#endif
+#if defined(HAS_STEAMLINK)
+#include "Video/SteamLinkVideo.h"
+#endif
#include "Audio/DVDAudioCodecFFmpeg.h"
#include "Audio/DVDAudioCodecPassthrough.h"
#include "Overlay/DVDOverlayCodecSSA.h"
@@ -141,7 +144,9 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, CProces
return pCodec;
#endif
-#if defined(HAS_IMXVPU)
+#if defined(HAS_STEAMLINK)
+ pCodec = OpenCodec(new STEAMLINK::CSteamLinkVideo(processInfo), hint, options);
+#elif defined(HAS_IMXVPU)
pCodec = OpenCodec(new CDVDVideoCodecIMX(processInfo), hint, options);
#elif defined(TARGET_ANDROID)
pCodec = OpenCodec(new CDVDVideoCodecAndroidMediaCodec(processInfo), hint, options);
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
index a2da9de437..118ea83c5a 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
@@ -51,6 +51,7 @@ class CDVDMediaCodecInfo;
class CDVDVideoCodecIMXBuffer;
class CMMALBuffer;
class CDVDAmlogicInfo;
+namespace STEAMLINK { class CSteamLinkBuffer; }
// should be entirely filled by all codecs
@@ -100,6 +101,10 @@ struct DVDVideoPicture
CDVDAmlogicInfo *amlcodec;
};
+ struct {
+ STEAMLINK::CSteamLinkBuffer *steamlink;
+ };
+
};
unsigned int iFlags;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/VideoPlayer/DVDCodecs/Video/Makefile.in
index 62d44cea58..574bf6c633 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/Makefile.in
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/Makefile.in
@@ -34,6 +34,13 @@ ifeq (@USE_MMAL@,1)
SRCS += MMALCodec.cpp MMALFFmpeg.cpp
endif
+ifeq (@USE_STEAMLINK@,1)
+SRCS += SteamLinkTranslator.cpp
+SRCS += SteamLinkVideo.cpp
+SRCS += SteamLinkVideoBuffer.cpp
+SRCS += SteamLinkVideoStream.cpp
+endif
+
LIB=Video.a
include @abs_top_srcdir@/Makefile.include
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.cpp
new file mode 100644
index 0000000000..9a800b130a
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "SteamLinkTranslator.h"
+#include "commons/ilog.h"
+
+using namespace STEAMLINK;
+
+bool CSteamLinkTranslator::TranslateFormat(AVCodecID format, ESLVideoFormat& slFormat)
+{
+ switch (format)
+ {
+ case AV_CODEC_ID_H264:
+ slFormat = k_ESLVideoFormatH264;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+const char* CSteamLinkTranslator::TranslateFormatToString(ESLVideoFormat format)
+{
+ switch (format)
+ {
+ case k_ESLVideoFormatH264: return "h264";
+ default:
+ break;
+ }
+
+ return "";
+}
+
+int CSteamLinkTranslator::TranslateLogLevel(ESLVideoLog logLevel)
+{
+ switch (logLevel)
+ {
+ case k_ESLVideoLogDebug: return LOGDEBUG;
+ case k_ESLVideoLogInfo: return LOGINFO;
+ case k_ESLVideoLogWarning: return LOGWARNING;
+ case k_ESLVideoLogError: return LOGERROR;
+ break;
+ default:
+ break;
+ }
+
+ return LOGDEBUG;
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.h
new file mode 100644
index 0000000000..1a0d5b68c5
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkTranslator.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "SLVideo.h"
+#include "libavcodec/avcodec.h"
+
+namespace STEAMLINK
+{
+
+class CSteamLinkTranslator
+{
+public:
+ static bool TranslateFormat(AVCodecID format, ESLVideoFormat& slFormat);
+
+ static const char* TranslateFormatToString(ESLVideoFormat format);
+
+ static int TranslateLogLevel(ESLVideoLog logLevel);
+};
+
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.cpp
new file mode 100644
index 0000000000..4776983a07
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.cpp
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "SteamLinkVideo.h"
+#include "SteamLinkTranslator.h"
+#include "SteamLinkVideoStream.h"
+#include "cores/VideoPlayer/DVDClock.h"
+#include "cores/VideoPlayer/DVDStreamInfo.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFormats.h"
+#include "settings/AdvancedSettings.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+
+// Steam Link video API
+#include "SLVideo.h"
+
+#include
+#include
+#include "SteamLinkTranslator.h"
+
+using namespace STEAMLINK;
+
+namespace
+{
+ void LogFunction(void *pContext, ESLVideoLog eLogLevel, const char *pszMessage)
+ {
+ int level = CSteamLinkTranslator::TranslateLogLevel(eLogLevel);
+ CLog::Log(level, "%s", pszMessage);
+ }
+}
+
+CSteamLinkVideo* CSteamLinkVideo::m_globalVideo = nullptr;
+unsigned int CSteamLinkVideo::m_globalInstances = 0;
+CCriticalSection CSteamLinkVideo::m_globalLock;
+
+CSteamLinkVideo::CSteamLinkVideo(CProcessInfo& processInfo) :
+ CDVDVideoCodec(processInfo),
+ m_context(nullptr),
+ m_stream(nullptr),
+ m_bufferSize(0),
+ m_dts(0.0),
+ m_sps_pps_size(0),
+ m_convert_bitstream(false)
+{
+ // Set global parameters
+ {
+ CSingleLock lock(m_globalLock);
+
+ if (m_globalVideo == nullptr)
+ m_globalVideo = this;
+ else
+ CLog::Log(LOGERROR, "%s: Already a global video instance!!!", GetName());
+
+ if (m_globalInstances++ == 0)
+ {
+ SLVideo_SetLogLevel(g_advancedSettings.CanLogComponent(LOGVIDEO) ? k_ESLVideoLogDebug : k_ESLVideoLogError);
+ SLVideo_SetLogFunction(LogFunction, nullptr);
+ }
+ }
+}
+
+CSteamLinkVideo::~CSteamLinkVideo()
+{
+ Dispose();
+
+ // Unset global parameters
+ {
+ CSingleLock lock(m_globalLock);
+
+ if (m_globalVideo == this)
+ m_globalVideo = nullptr;
+
+ if (m_globalInstances > 0 && --m_globalInstances == 0)
+ SLVideo_SetLogFunction(nullptr, nullptr);
+ }
+}
+
+bool CSteamLinkVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+{
+ if (hints.software)
+ return false;
+
+ bool bSuccess = false;
+
+ Dispose();
+
+ CSLVideoContext* context = SLVideo_CreateContext();
+ if (!context)
+ {
+ CLog::Log(LOGERROR, "%s: Failed to create context", GetName());
+ }
+ else
+ {
+ std::unique_ptr stream;
+
+ ESLVideoFormat slFormat;
+ if (!CSteamLinkTranslator::TranslateFormat(hints.codec, slFormat))
+ {
+ if (g_advancedSettings.CanLogComponent(LOGVIDEO))
+ CLog::Log(LOGDEBUG, "%s: Codec not supported", GetName());
+ }
+ else
+ {
+ if (hints.extrasize < 7 || hints.extradata == nullptr)
+ {
+ if (g_advancedSettings.CanLogComponent(LOGVIDEO))
+ CLog::Log(LOGNOTICE, "%s: avcC data too small or missing", GetName());
+ }
+ else
+ {
+ // valid avcC data (bitstream) always starts with the value 1 (version)
+ if (*static_cast(hints.extradata) == 1)
+ m_convert_bitstream = bitstream_convert_init(hints.extradata, hints.extrasize);
+
+ if (hints.fpsrate > 0 && hints.fpsscale > 0)
+ stream.reset(new CSteamLinkVideoStream(context, k_ESLVideoFormatH264, hints.fpsrate, hints.fpsscale));
+ else
+ stream.reset(new CSteamLinkVideoStream(context, k_ESLVideoFormatH264));
+
+ if (!stream->Open())
+ stream.reset();
+ }
+ }
+
+ if (!stream)
+ {
+ if (g_advancedSettings.CanLogComponent(LOGVIDEO))
+ CLog::Log(LOGNOTICE, "%s: Failed to create stream", GetName());
+ SLVideo_FreeContext(context);
+ }
+ else
+ {
+ // Success
+ m_context = context;
+ {
+ CSingleLock lock(m_streamLock);
+ m_stream = std::move(stream);
+ }
+ bSuccess = true;
+
+ // Initialize buffer for DVDVideoPicture
+ memset(&m_videoPictureBuffer, 0, sizeof(m_videoPictureBuffer));
+ m_videoPictureBuffer.dts = DVD_NOPTS_VALUE;
+ m_videoPictureBuffer.pts = DVD_NOPTS_VALUE;
+ m_videoPictureBuffer.format = RENDER_FMT_STEAMLINK;
+ m_videoPictureBuffer.color_range = 0;
+ m_videoPictureBuffer.color_matrix = 4; // FCC
+ m_videoPictureBuffer.iFlags = DVP_FLAG_ALLOCATED;
+ m_videoPictureBuffer.iWidth = hints.width;
+ m_videoPictureBuffer.iHeight = hints.height;
+ m_videoPictureBuffer.steamlink = nullptr;
+
+ m_videoPictureBuffer.iDisplayWidth = m_videoPictureBuffer.iWidth;
+ m_videoPictureBuffer.iDisplayHeight = m_videoPictureBuffer.iHeight;
+
+ m_processInfo.SetVideoDecoderName(GetName(), true);
+ m_processInfo.SetVideoPixelFormat(CSteamLinkTranslator::TranslateFormatToString(slFormat));
+ m_processInfo.SetVideoDimensions(hints.width, hints.height);
+ m_processInfo.SetVideoFps(static_cast(hints.fpsrate) / static_cast(hints.fpsscale));
+ m_processInfo.SetVideoDAR(hints.aspect);
+
+ if (g_advancedSettings.CanLogComponent(LOGVIDEO))
+ {
+ CLog::Log(LOGINFO, "%s: Opened codec %s", GetName(), CSteamLinkTranslator::TranslateFormatToString(slFormat));
+
+ int width = 0;
+ int height = 0;
+ SLVideo_GetDisplayResolution(m_context, &width, &height);
+
+ CLog::Log(LOGDEBUG, "%s: Display resolution = %d x %d", GetName(), width, height);
+ }
+ }
+ }
+
+ return bSuccess;
+}
+
+void CSteamLinkVideo::Dispose()
+{
+ if (m_stream)
+ {
+ m_stream->Close();
+ {
+ CSingleLock lock(m_streamLock);
+ m_stream.reset();
+ }
+ }
+
+ if (m_context)
+ {
+ SLVideo_FreeContext(m_context);
+ m_context = nullptr;
+ }
+
+ 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;
+ }
+ m_convert_bitstream = false;
+ }
+}
+
+int CSteamLinkVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
+{
+ if (pData == nullptr || iSize <= 0)
+ return VC_BUFFER;
+
+ if (!m_stream)
+ return VC_ERROR;
+
+ m_buffer.reset();
+ m_bufferSize = 0;
+ m_dts = dts;
+
+ if (m_convert_bitstream)
+ {
+ // convert demuxer packet from bitstream to bytestream (AnnexB)
+ uint8_t *bytestream_buff = nullptr;
+ int bytestream_size = 0;
+
+ bitstream_convert(pData, iSize, &bytestream_buff, &bytestream_size);
+ if (bytestream_buff != nullptr && (bytestream_size > 0))
+ {
+ m_buffer.reset(bytestream_buff);
+ m_bufferSize = bytestream_size;
+ }
+ }
+ else
+ {
+ uint8_t *bytestream_buff = static_cast(malloc(iSize));
+ if (bytestream_buff)
+ {
+ memcpy(bytestream_buff, pData, iSize);
+ m_buffer.reset(bytestream_buff);
+ m_bufferSize = iSize;
+ }
+ }
+
+ return VC_PICTURE;
+}
+
+void CSteamLinkVideo::Reset(void)
+{
+ if (!m_stream)
+ return;
+
+ if (!m_stream->Flush())
+ {
+ CSingleLock lock(m_streamLock);
+ m_stream.reset();
+ if (g_advancedSettings.CanLogComponent(LOGVIDEO))
+ CLog::Log(LOGERROR, "%s: Failed to flush stream", GetName());
+ }
+}
+
+bool CSteamLinkVideo::GetPicture(DVDVideoPicture *pDvdVideoPicture)
+{
+ if (!m_stream)
+ return false;
+
+ *pDvdVideoPicture = m_videoPictureBuffer;
+
+ // Steam link video accepts decode packet, so use dts for timing
+ pDvdVideoPicture->pts = m_dts;
+ pDvdVideoPicture->steamlink = new CSteamLinkBuffer(std::move(m_buffer), m_bufferSize, m_stream);
+
+ return true;
+}
+
+bool CSteamLinkVideo::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
+{
+ delete pDvdVideoPicture->steamlink;
+ pDvdVideoPicture->steamlink = nullptr;
+
+ return true;
+}
+
+std::shared_ptr CSteamLinkVideo::GetStream()
+{
+ std::shared_ptr stream;
+
+ {
+ CSingleLock lock(m_streamLock);
+ stream = m_stream;
+ }
+
+ return stream;
+}
+
+bool CSteamLinkVideo::IsPlayingVideo()
+{
+ std::shared_ptr stream;
+
+ {
+ CSingleLock lock(m_globalLock);
+
+ if (m_globalVideo != nullptr)
+ stream = m_globalVideo->GetStream();
+ }
+
+ return stream.get() != nullptr;
+}
+
+unsigned int CSteamLinkVideo::GetDelayMs()
+{
+ std::shared_ptr stream;
+
+ {
+ CSingleLock lock(m_globalLock);
+
+ if (m_globalVideo != nullptr)
+ stream = m_globalVideo->GetStream();
+ }
+
+ return stream ? stream->GetDelayMs() : 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+bool CSteamLinkVideo::bitstream_convert_init(void *in_extradata, int in_extrasize)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet
+ // and Licensed GPL 2.1 or greater
+
+ m_sps_pps_size = 0;
+ m_sps_pps_context.sps_pps_data = NULL;
+
+ // nothing to filter
+ if (!in_extradata || in_extrasize < 6)
+ return false;
+
+ uint16_t unit_size;
+ uint32_t total_size = 0;
+ uint8_t *out = NULL, unit_nb, sps_done = 0;
+ const uint8_t *extradata = (uint8_t*)in_extradata + 4;
+ static const uint8_t nalu_header[4] = {0, 0, 0, 1};
+
+ // retrieve length coded size
+ m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1;
+ if (m_sps_pps_context.length_size == 3)
+ return false;
+
+ // retrieve sps and pps unit(s)
+ unit_nb = *extradata++ & 0x1f; // number of sps unit(s)
+ if (!unit_nb)
+ {
+ unit_nb = *extradata++; // number of pps unit(s)
+ sps_done++;
+ }
+ while (unit_nb--)
+ {
+ unit_size = extradata[0] << 8 | extradata[1];
+ total_size += unit_size + 4;
+ if ( (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize) )
+ {
+ free(out);
+ return false;
+ }
+ uint8_t* new_out = (uint8_t*)realloc(out, total_size);
+ if (new_out)
+ {
+ out = new_out;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "bitstream_convert_init failed - %s : could not realloc the buffer out", __FUNCTION__);
+ free(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 CSteamLinkVideo::bitstream_convert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet
+ // and Licensed GPL 2.1 or greater
+
+ uint8_t *buf = pData;
+ uint32_t buf_size = iSize;
+ uint8_t unit_type;
+ int32_t nal_size;
+ uint32_t cumul_size = 0;
+ const uint8_t *buf_end = buf + buf_size;
+
+ do
+ {
+ if (buf + m_sps_pps_context.length_size > buf_end)
+ goto fail;
+
+ if (m_sps_pps_context.length_size == 1)
+ nal_size = buf[0];
+ else if (m_sps_pps_context.length_size == 2)
+ nal_size = buf[0] << 8 | buf[1];
+ else
+ nal_size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+
+ buf += m_sps_pps_context.length_size;
+ unit_type = *buf & 0x1f;
+
+ if (buf + nal_size > buf_end || nal_size < 0)
+ goto fail;
+
+ // prepend only to the first type 5 NAL unit of an IDR picture
+ if (m_sps_pps_context.first_idr && unit_type == 5)
+ {
+ bitstream_alloc_and_copy(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
+ {
+ bitstream_alloc_and_copy(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;
+ }
+
+ if (!*poutbuf)
+ goto fail;
+
+ 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 CSteamLinkVideo::bitstream_alloc_and_copy(
+ uint8_t **poutbuf, int *poutbuf_size,
+ const uint8_t *sps_pps, uint32_t sps_pps_size,
+ const uint8_t *in, uint32_t in_size)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet
+ // and Licensed GPL 2.1 or greater
+
+ #define CHD_WB32(p, d) { \
+ ((uint8_t*)(p))[3] = (d); \
+ ((uint8_t*)(p))[2] = (d) >> 8; \
+ ((uint8_t*)(p))[1] = (d) >> 16; \
+ ((uint8_t*)(p))[0] = (d) >> 24; }
+
+ uint32_t offset = *poutbuf_size;
+ uint8_t nal_header_size = 4;//offset ? 3 : 4;
+
+ *poutbuf_size += sps_pps_size + nal_header_size + in_size;
+ *poutbuf = static_cast(realloc(*poutbuf, *poutbuf_size));
+
+ if (!*poutbuf)
+ return;
+
+ if (sps_pps)
+ memcpy(*poutbuf + offset, sps_pps, sps_pps_size);
+
+ memcpy(*poutbuf + offset + sps_pps_size + nal_header_size, in, in_size);
+ if (true || !offset)
+ {
+ CHD_WB32(*poutbuf + offset + 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/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.h
new file mode 100644
index 0000000000..ebe42c795a
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideo.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "DVDVideoCodec.h"
+#include "SteamLinkVideoBuffer.h"
+#include "threads/CriticalSection.h"
+
+#include
+#include
+#include
+
+#define STEAMLINK_VIDEO_CODEC_NAME "SteamLinkVideo"
+
+struct CSLVideoContext;
+struct CSLVideoStream;
+
+namespace STEAMLINK
+{
+
+class CSteamLinkVideoStream;
+
+class CSteamLinkVideo : public CDVDVideoCodec
+{
+public:
+ CSteamLinkVideo(CProcessInfo& processInfo);
+ virtual ~CSteamLinkVideo();
+
+ // implementation of CDVDVideoCodec
+ virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) override;
+ virtual int Decode(uint8_t* pData, int iSize, double dts, double pts) override;
+ virtual void Reset() override;
+ virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture) override;
+ virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture) override;
+ virtual void SetDropState(bool bDrop) override { }
+ virtual const char* GetName() override { return STEAMLINK_VIDEO_CODEC_NAME; }
+
+ // Access global stream instance
+ static bool IsPlayingVideo();
+ static unsigned int GetDelayMs();
+
+private:
+ void Dispose();
+
+ std::shared_ptr GetStream();
+
+ // Steam Link data
+ CSLVideoContext* m_context;
+ std::shared_ptr m_stream;
+ CCriticalSection m_streamLock;
+
+ // VideoPlayer data
+ DVDVideoPicture m_videoPictureBuffer;
+ SteamLinkUniqueBuffer m_buffer;
+ size_t m_bufferSize;
+ float m_dts;
+
+ // bitstream to bytestream (Annex B) conversion support.
+ bool bitstream_convert_init(void *in_extradata, int in_extrasize);
+ bool bitstream_convert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size);
+ static void bitstream_alloc_and_copy(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 bitstream_ctx
+ {
+ uint8_t length_size;
+ uint8_t first_idr;
+ uint8_t *sps_pps_data;
+ uint32_t size;
+
+ bitstream_ctx()
+ {
+ length_size = 0;
+ first_idr = 0;
+ sps_pps_data = NULL;
+ size = 0;
+ }
+
+ } bitstream_ctx;
+
+ uint32_t m_sps_pps_size;
+ bitstream_ctx m_sps_pps_context;
+ bool m_convert_bitstream;
+
+ // Global instance
+ static CSteamLinkVideo* m_globalVideo;
+ static unsigned int m_globalInstances;
+ static CCriticalSection m_globalLock;
+};
+
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.cpp
new file mode 100644
index 0000000000..08fa22feaf
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "SteamLinkVideoBuffer.h"
+
+#include
+
+using namespace STEAMLINK;
+
+// --- SteamLinkBufferFree -----------------------------------------------------
+
+void SteamLinkBufferFree::operator()(void* x)
+{
+ free(x);
+}
+
+// --- CSteamLinkBuffer --------------------------------------------------------
+
+CSteamLinkBuffer::CSteamLinkBuffer(SteamLinkUniqueBuffer buffer, size_t size, std::shared_ptr stream) :
+ buffer(std::move(buffer)),
+ size(size),
+ stream(std::move(stream))
+{
+}
+
+CSteamLinkBuffer::CSteamLinkBuffer(CSteamLinkBuffer&& other) :
+ buffer(std::move(other.buffer)),
+ size(other.size),
+ stream(std::move(other.stream))
+{
+ other.size = 0;
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.h
new file mode 100644
index 0000000000..bb1fa33bff
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include
+#include
+#include
+
+namespace STEAMLINK
+{
+
+class CSteamLinkVideoStream;
+
+struct SteamLinkBufferFree
+{
+ void operator()(void* x);
+};
+
+typedef std::unique_ptr SteamLinkUniqueBuffer;
+
+class CSteamLinkBuffer
+{
+public:
+ CSteamLinkBuffer(SteamLinkUniqueBuffer buffer, size_t size, std::shared_ptr stream);
+
+ CSteamLinkBuffer(CSteamLinkBuffer&& other);
+
+ SteamLinkUniqueBuffer buffer;
+ size_t size;
+ std::shared_ptr stream;
+};
+
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.cpp
new file mode 100644
index 0000000000..d140db41ee
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "SteamLinkVideoStream.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+
+using namespace STEAMLINK;
+
+CSteamLinkVideoStream::CSteamLinkVideoStream(CSLVideoContext *pContext,
+ ESLVideoFormat eFormat,
+ unsigned int fpsRate /* = 0 */,
+ unsigned int fpsScale /* = 0 */) :
+ m_pContext(pContext),
+ m_eFormat(eFormat),
+ m_fpsRate(fpsRate),
+ m_fpsScale(fpsScale),
+ m_stream(nullptr)
+{
+}
+
+bool CSteamLinkVideoStream::Open()
+{
+ CSingleLock lock(m_streamMutex);
+
+ m_stream = SLVideo_CreateStream(m_pContext, m_eFormat, false);
+
+ if (m_stream != nullptr)
+ {
+ if (m_fpsRate > 0 && m_fpsScale > 0)
+ SLVideo_SetStreamTargetFramerate(m_stream, m_fpsRate, m_fpsScale);
+
+ return true;
+ }
+
+ return false;
+}
+
+void CSteamLinkVideoStream::Close()
+{
+ CSingleLock lock(m_streamMutex);
+
+ if (m_stream)
+ {
+ SLVideo_FreeStream(m_stream);
+ m_stream = nullptr;
+ }
+
+ m_delay.SetExpired();
+}
+
+bool CSteamLinkVideoStream::Flush()
+{
+ CSingleLock lock(m_streamMutex);
+
+ Close();
+
+ return Open();
+}
+
+bool CSteamLinkVideoStream::WriteData(const uint8_t* data, size_t size)
+{
+ uint32_t delayMs = 0;
+
+ {
+ CSingleLock lock(m_streamMutex);
+
+ if (m_stream)
+ {
+ if (SLVideo_BeginFrame(m_stream, size) != 0)
+ {
+ CLog::Log(LOGERROR, "SteamLinkVideo: Failed to begin frame of size %u", size);
+ return false;
+ }
+
+ if (SLVideo_WriteFrameData(m_stream, const_cast(data), size) != 0)
+ {
+ CLog::Log(LOGERROR, "SteamLinkVideo: Error writing data of size %u", size);
+ return false;
+ }
+
+ if (SLVideo_SubmitFrame(m_stream) != 0)
+ {
+ CLog::Log(LOGERROR, "SteamLinkVideo: Error submitting frame of size %u", size);
+ return false;
+ }
+
+ delayMs = SLVideo_GetQueuedVideoMS(m_stream);
+ }
+ }
+
+ {
+ CSingleLock lock(m_delayMutex);
+
+ if (delayMs > 0)
+ {
+ //CLog::Log(LOGERROR, "CSteamLinkVideoStream - Delay = %u ms", delayMs);
+ m_delay.Set(delayMs);
+ }
+ else
+ ;//CLog::Log(LOGERROR, "CSteamLinkVideoStream - No delay!!!"); //! @todo
+ }
+
+ return true;
+}
+
+void CSteamLinkVideoStream::SetSpeed(float fps)
+{
+ CSingleLock lock(m_streamMutex);
+
+ //! @todo
+ CLog::Log(LOGERROR, "CSteamLinkVideoStream - SetSpeed() not implemented!");
+}
+
+unsigned int CSteamLinkVideoStream::GetDelayMs()
+{
+ CSingleLock lock(m_delayMutex);
+
+ return m_delay.MillisLeft();
+}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.h
new file mode 100644
index 0000000000..af5b3249bb
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ * http://kodi.tv
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+
+// Steam Link video API
+#include "SLVideo.h"
+
+#include
+
+namespace STEAMLINK
+{
+
+class CSteamLinkVideoStream
+{
+public:
+ CSteamLinkVideoStream(CSLVideoContext *pContext,
+ ESLVideoFormat format,
+ unsigned int fpsRate = 0,
+ unsigned int fpsScale = 0);
+
+ ~CSteamLinkVideoStream() { Close(); }
+
+ bool Open();
+ void Close();
+
+ bool Flush();
+
+ bool WriteData(const uint8_t* data, size_t size);
+
+ void SetSpeed(float speed);
+
+ unsigned int GetDelayMs();
+
+private:
+ // Construction parameters
+ CSLVideoContext * const m_pContext;
+ const ESLVideoFormat m_eFormat;
+ const unsigned int m_fpsRate;
+ const unsigned int m_fpsScale;
+
+ // Stream parameters
+ CSLVideoStream *m_stream;
+ CCriticalSection m_streamMutex;
+ XbmcThreads::EndTime m_delay;;
+ CCriticalSection m_delayMutex;
+};
+
+}
diff --git a/xbmc/cores/VideoPlayer/IVideoPlayer.h b/xbmc/cores/VideoPlayer/IVideoPlayer.h
index 0b676c9b61..520131f9cf 100644
--- a/xbmc/cores/VideoPlayer/IVideoPlayer.h
+++ b/xbmc/cores/VideoPlayer/IVideoPlayer.h
@@ -111,6 +111,7 @@ class IDVDStreamPlayerVideo : public IDVDStreamPlayer
virtual int GetDecoderBufferSize() { return 0; }
virtual int GetDecoderFreeSpace() = 0;
virtual bool IsEOS() { return false; };
+ virtual bool CanFFRW() { return true; }
};
class CDVDAudioCodec;
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
index bd11cd8a76..e779fc857c 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
@@ -3141,6 +3141,12 @@ bool CVideoPlayer::CanSeek()
return m_State.canseek;
}
+bool CVideoPlayer::CanFFRW()
+{
+ CSingleLock lock(m_StateSection);
+ return m_State.canffrw;
+}
+
void CVideoPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
{
if (!m_State.canseek)
@@ -5046,6 +5052,9 @@ void CVideoPlayer::UpdatePlayState(double timeout)
state.canseek = m_pInputStream->CanSeek();
}
+ state.canffrw = m_VideoPlayerVideo->CanFFRW();
+
+
if (m_Edl.HasCut())
{
state.time = (double) m_Edl.RemoveCutTime(llrint(state.time));
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.h b/xbmc/cores/VideoPlayer/VideoPlayer.h
index 41ff6b4c86..497100e6e2 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.h
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.h
@@ -131,6 +131,7 @@ struct SPlayerState
bool recording; // are we currently recording
bool canpause; // pvr: can pause the current playing item
bool canseek; // pvr: can seek in the current playing item
+ bool canffrw;
bool caching;
int64_t cache_bytes; // number of bytes current's cached
@@ -302,6 +303,7 @@ class CVideoPlayer : public IPlayer, public CThread, public IVideoPlayer, public
virtual bool HasRDS() const;
virtual bool IsPassthrough() const;
virtual bool CanSeek();
+ virtual bool CanFFRW() override;
virtual void Seek(bool bPlus, bool bLargeStep, bool bChapterOverride);
virtual bool SeekScene(bool bPlus = true);
virtual void SeekPercentage(float iPercent);
diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
index 89db27cce0..73315f6100 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
@@ -654,6 +654,11 @@ void CVideoPlayerVideo::SetSpeed(int speed)
m_speed = speed;
}
+bool CVideoPlayerVideo::CanFFRW()
+{
+ return m_renderManager.CanFFRW();
+}
+
void CVideoPlayerVideo::Flush(bool sync)
{
/* flush using message as this get's called from VideoPlayer thread */
diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.h b/xbmc/cores/VideoPlayer/VideoPlayerVideo.h
index 0d4100e58e..c77c9f8ea3 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.h
+++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.h
@@ -91,6 +91,7 @@ class CVideoPlayerVideo : public CThread, public IDVDStreamPlayerVideo
int GetVideoBitrate();
std::string GetStereoMode();
void SetSpeed(int iSpeed);
+ virtual bool CanFFRW() override;
// classes
CDVDOverlayContainer* m_pOverlayContainer;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
index f9b3bfb4eb..09df778fde 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
@@ -389,7 +389,7 @@ void CBaseRenderer::ManageRenderArea()
else if(stereo_view == RENDER_STEREO_VIEW_RIGHT) stereo_view = RENDER_STEREO_VIEW_LEFT;
}
- if (m_format != RENDER_FMT_BYPASS)
+ if (IsGuiLayer())
{
switch(stereo_mode)
{
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
index c7294cea37..ef8830b84f 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
@@ -92,6 +92,7 @@ class CBaseRenderer
virtual void ReleaseBuffer(int idx) { }
virtual bool NeedBuffer(int idx) { return false; }
virtual bool IsGuiLayer() { return true; }
+ virtual bool CanFFRW() { return true; }
// Render info, can be called before configure
virtual CRenderInfo GetRenderInfo() { return CRenderInfo(); }
virtual void Update() = 0;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/Makefile.in b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/Makefile.in
index 2f916de794..82067204f3 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/Makefile.in
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/Makefile.in
@@ -26,6 +26,10 @@ ifeq ($(findstring osx,@ARCH@),osx)
SRCS += RendererVTBGL.cpp
endif
+ifeq (@USE_STEAMLINK@,1)
+SRCS += RendererSteamLink.cpp
+endif
+
LIB=HwDecRender.a
include ../../../../../Makefile.include
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.cpp
new file mode 100644
index 0000000000..8d17599bca
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "RendererSteamLink.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoBuffer.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/SteamLinkVideoStream.h"
+#include "utils/log.h"
+
+// Steam Link video API
+#include "SLVideo.h"
+
+#include
+
+#include
+
+using namespace STEAMLINK;
+
+void CRendererSteamLink::AddVideoPictureHW(DVDVideoPicture &picture, int index)
+{
+ if (picture.steamlink)
+ {
+ YUVBUFFER &buf = m_buffers[index];
+ buf.hwDec = new CSteamLinkBuffer(std::move(*picture.steamlink));
+ }
+}
+
+void CRendererSteamLink::PreInit()
+{
+ CLinuxRendererGLES::PreInit();
+
+ m_formats.clear();
+ m_formats.push_back(RENDER_FMT_STEAMLINK);
+}
+
+void CRendererSteamLink::ReleaseBuffer(int idx)
+{
+ YUVBUFFER &buf = m_buffers[idx];
+
+ delete static_cast(buf.hwDec);
+ buf.hwDec = nullptr;
+}
+
+bool CRendererSteamLink::LoadShadersHook()
+{
+ CLog::Log(LOGNOTICE, "CRendererSteamLink: Using STEAMLINK render method");
+ m_textureTarget = GL_TEXTURE_2D;
+ m_renderMethod = RENDER_BYPASS;
+ return true;
+}
+
+bool CRendererSteamLink::RenderUpdateVideoHook(bool clear, DWORD flags /* = 0 */, DWORD alpha /* = 255 */)
+{
+ ManageRenderArea();
+
+ CSteamLinkBuffer* pBuffer = static_cast(m_buffers[m_iYV12RenderBuffer].hwDec);
+
+ if (pBuffer && pBuffer->buffer)
+ {
+ CSteamLinkBuffer buffer(std::move(*pBuffer));
+
+ buffer.stream->WriteData(buffer.buffer.get(), buffer.size);
+ }
+
+ return true;
+}
+
+int CRendererSteamLink::GetImageHook(YV12Image *image, int source /* = AUTOSOURCE */, bool readonly /* = false */)
+{
+ return source;
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.h
new file mode 100644
index 0000000000..58bd18d9e0
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererSteamLink.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "system.h" // for HAS_GLES, needed for LinuxRendererGLES.h
+
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
+
+namespace STEAMLINK
+{
+
+class CRendererSteamLink : public CLinuxRendererGLES
+{
+public:
+ CRendererSteamLink() = default;
+ virtual ~CRendererSteamLink() = default;
+
+ // implementation of CBaseRenderer via CLinuxRendererGLES
+ virtual void AddVideoPictureHW(DVDVideoPicture &picture, int index) override;
+ virtual bool IsPictureHW(DVDVideoPicture &picture) override { return true; }
+ virtual void PreInit() override;
+ virtual void ReleaseBuffer(int idx) override;
+ virtual bool IsGuiLayer() override { return false; }
+ virtual bool CanFFRW() override { return false; }
+ virtual bool SupportsMultiPassRendering() override { return false; }
+ virtual bool Supports(ERENDERFEATURE feature) override { return false; }
+ virtual bool Supports(ESCALINGMETHOD method) override { return false; }
+
+protected:
+ // implementation of CLinuxRendererGLES
+ virtual bool LoadShadersHook() override;
+ virtual bool RenderUpdateVideoHook(bool clear, DWORD flags = 0, DWORD alpha = 255) override;
+ virtual int GetImageHook(YV12Image *image, int source = -1, bool readonly = false) override;
+};
+
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
index f478fb0c02..98a39c39ce 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
@@ -710,7 +710,7 @@ inline void CLinuxRendererGLES::ReorderDrawPoints()
bool CLinuxRendererGLES::CreateTexture(int index)
{
- if (m_format == RENDER_FMT_BYPASS)
+ if (!IsGuiLayer())
{
CreateBYPASSTexture(index);
return true;
@@ -732,7 +732,7 @@ bool CLinuxRendererGLES::CreateTexture(int index)
void CLinuxRendererGLES::DeleteTexture(int index)
{
- if (m_format == RENDER_FMT_BYPASS)
+ if (!IsGuiLayer())
{
DeleteBYPASSTexture(index);
}
@@ -750,7 +750,7 @@ void CLinuxRendererGLES::DeleteTexture(int index)
bool CLinuxRendererGLES::UploadTexture(int index)
{
// Now that we now the render method, setup texture function handlers
- if (m_format == RENDER_FMT_BYPASS)
+ if (!IsGuiLayer())
{
UploadBYPASSTexture(index);
return true;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/RenderFormats.h b/xbmc/cores/VideoPlayer/VideoRenderers/RenderFormats.h
index 39f62fa616..6d2c2d0df0 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/RenderFormats.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/RenderFormats.h
@@ -45,6 +45,7 @@ enum ERenderFormat {
RENDER_FMT_IMXMAP,
RENDER_FMT_MMAL,
RENDER_FMT_AML,
+ RENDER_FMT_STEAMLINK,
};
struct CRenderInfo
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
index 420b5b5d8e..0acc08eecb 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
@@ -69,6 +69,10 @@
#include "HwDecRender/RendererMediaCodecSurface.h"
#endif
+#if defined(HAS_STEAMLINK)
+ #include "HwDecRender/RendererSteamLink.h"
+#endif
+
#if defined(TARGET_POSIX)
#include "linux/XTimeUtils.h"
#endif
@@ -111,6 +115,7 @@ static std::string GetRenderFormatName(ERenderFormat format)
case RENDER_FMT_IMXMAP: return "IMXMAP";
case RENDER_FMT_MMAL: return "MMAL";
case RENDER_FMT_AML: return "AMLCODEC";
+ case RENDER_FMT_STEAMLINK: return "STEAMLINK";
case RENDER_FMT_NONE: return "NONE";
}
return "UNKNOWN";
@@ -580,6 +585,12 @@ void CRenderManager::CreateRenderer()
{
#if defined(HAS_LIBAMCODEC)
m_pRenderer = new CRendererAML;
+#endif
+ }
+ else if (m_format == RENDER_FMT_STEAMLINK)
+ {
+#if defined(HAS_STEAMLINK)
+ m_pRenderer = new STEAMLINK::CRendererSteamLink;
#endif
}
else if (m_format != RENDER_FMT_NONE)
@@ -1358,6 +1369,14 @@ void CRenderManager::DiscardBuffer()
m_presentevent.notifyAll();
}
+bool CRenderManager::CanFFRW()
+{
+ if (m_pRenderer)
+ return m_pRenderer->CanFFRW();
+
+ return true;
+}
+
bool CRenderManager::GetStats(int &lateframes, double &pts, int &queued, int &discard)
{
CSingleLock lock(m_presentlock);
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.h b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.h
index d5340877bc..d1e2117360 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.h
@@ -156,6 +156,8 @@ class CRenderManager
void SetDelay(int delay) { m_videoDelay = delay; };
int GetDelay() { return m_videoDelay; };
+ bool CanFFRW();
+
protected:
void PresentSingle(bool clear, DWORD flags, DWORD alpha);
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.cpp
new file mode 100644
index 0000000000..d623fac060
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "SteamLinkRenderer.h"
+#include "utils/log.h"
+
+#include
+
+using namespace STEAMLINK;
+
+bool CSteamLinkRenderer::LoadShadersHook()
+{
+ CLog::Log(LOGNOTICE, "CSteamLinkRenderer: Using STEAMLINK render method");
+ m_textureTarget = GL_TEXTURE_2D;
+ m_renderMethod = RENDER_BYPASS;
+ return false;
+}
+
+int CSteamLinkRenderer::GetImageHook(YV12Image *image, int source /* = AUTOSOURCE */, bool readonly /* = false */)
+{
+ return source;
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.h b/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.h
new file mode 100644
index 0000000000..9f609b9800
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/SteamLinkRenderer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Team Kodi
+ * Copyright (C) 2016 Valve Corporation
+ *
+ * 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 this Program; see the file COPYING. If not, see
+ * .
+ *
+ */
+#pragma once
+
+#include "system.h" // for HAS_GLES, needed for LinuxRendererGLES.h
+
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
+
+namespace STEAMLINK
+{
+
+class CSteamLinkRenderer : public CLinuxRendererGLES
+{
+public:
+ CSteamLinkRenderer() { }
+ virtual ~CSteamLinkRenderer() { }
+
+ // implementation of CBaseRenderer via CLinuxRendererGLES
+ virtual bool IsGuiLayer() override { return false; }
+ virtual bool SupportsMultiPassRendering() override { return false; }
+ virtual bool Supports(ERENDERFEATURE feature) override { return false; }
+ virtual bool Supports(ESCALINGMETHOD method) override { return false; }
+
+protected:
+ // implementation of CLinuxRendererGLES
+ bool LoadShadersHook();
+ bool RenderHook(int index) { return true; }
+ bool RenderUpdateVideoHook(bool clear, DWORD flags = 0, DWORD alpha = 255) { return true; }
+ int GetImageHook(YV12Image *image, int source = -1, bool readonly = false);
+};
+
+}
diff --git a/xbmc/windowing/WinEventsSDL.cpp b/xbmc/windowing/WinEventsSDL.cpp
index b0658d1ff3..22ea6cad6c 100644
--- a/xbmc/windowing/WinEventsSDL.cpp
+++ b/xbmc/windowing/WinEventsSDL.cpp
@@ -36,7 +36,7 @@
#include "platform/darwin/osx/CocoaInterface.h"
#endif
-#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !defined(TARGET_ANDROID)
+#ifdef HAVE_X11
#include
#include
#include "input/XBMC_keysym.h"
@@ -45,7 +45,7 @@
using namespace KODI::MESSAGING;
-#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
+#ifdef HAVE_X11
// The following chunk of code is Linux specific. For keys that have
// with keysym.sym set to zero it checks the scan code, and sets the sym
// for some known scan codes. This is mostly the multimedia keys.
@@ -215,6 +215,276 @@ static uint16_t SymFromScancode(uint16_t scancode)
}
#endif // End of checks for keysym.sym == 0
+#if HAVE_SDL_VERSION == 2
+// The keysyms changed from SDL 1 to SDL 2, so create a mapping
+static std::map s_keysymTable;
+
+static void InitializeKeysymLookup()
+{
+ s_keysymTable[SDLK_RETURN] = XBMCK_RETURN;
+ s_keysymTable[SDLK_ESCAPE] = XBMCK_ESCAPE;
+ s_keysymTable[SDLK_BACKSPACE] = XBMCK_BACKSPACE;
+ s_keysymTable[SDLK_TAB] = XBMCK_TAB;
+ s_keysymTable[SDLK_SPACE] = XBMCK_SPACE;
+ s_keysymTable[SDLK_EXCLAIM] = XBMCK_EXCLAIM;
+ s_keysymTable[SDLK_QUOTEDBL] = XBMCK_QUOTEDBL;
+ s_keysymTable[SDLK_HASH] = XBMCK_HASH;
+ s_keysymTable[SDLK_PERCENT] = XBMCK_PERCENT;
+ s_keysymTable[SDLK_DOLLAR] = XBMCK_DOLLAR;
+ s_keysymTable[SDLK_AMPERSAND] = XBMCK_AMPERSAND;
+ s_keysymTable[SDLK_QUOTE] = XBMCK_QUOTE;
+ s_keysymTable[SDLK_LEFTPAREN] = XBMCK_LEFTPAREN;
+ s_keysymTable[SDLK_RIGHTPAREN] = XBMCK_RIGHTPAREN;
+ s_keysymTable[SDLK_ASTERISK] = XBMCK_ASTERISK;
+ s_keysymTable[SDLK_PLUS] = XBMCK_PLUS;
+ s_keysymTable[SDLK_COMMA] = XBMCK_COMMA;
+ s_keysymTable[SDLK_MINUS] = XBMCK_MINUS;
+ s_keysymTable[SDLK_PERIOD] = XBMCK_PERIOD;
+ s_keysymTable[SDLK_SLASH] = XBMCK_SLASH;
+ s_keysymTable[SDLK_0] = XBMCK_0;
+ s_keysymTable[SDLK_1] = XBMCK_1;
+ s_keysymTable[SDLK_2] = XBMCK_2;
+ s_keysymTable[SDLK_3] = XBMCK_3;
+ s_keysymTable[SDLK_4] = XBMCK_4;
+ s_keysymTable[SDLK_5] = XBMCK_5;
+ s_keysymTable[SDLK_6] = XBMCK_6;
+ s_keysymTable[SDLK_7] = XBMCK_7;
+ s_keysymTable[SDLK_8] = XBMCK_8;
+ s_keysymTable[SDLK_9] = XBMCK_9;
+ s_keysymTable[SDLK_COLON] = XBMCK_COLON;
+ s_keysymTable[SDLK_SEMICOLON] = XBMCK_SEMICOLON;
+ s_keysymTable[SDLK_LESS] = XBMCK_LESS;
+ s_keysymTable[SDLK_EQUALS] = XBMCK_EQUALS;
+ s_keysymTable[SDLK_GREATER] = XBMCK_GREATER;
+ s_keysymTable[SDLK_QUESTION] = XBMCK_QUESTION;
+ s_keysymTable[SDLK_AT] = XBMCK_AT;
+ s_keysymTable[SDLK_LEFTBRACKET] = XBMCK_LEFTBRACKET;
+ s_keysymTable[SDLK_BACKSLASH] = XBMCK_BACKSLASH;
+ s_keysymTable[SDLK_RIGHTBRACKET] = XBMCK_RIGHTBRACKET;
+ s_keysymTable[SDLK_CARET] = XBMCK_CARET;
+ s_keysymTable[SDLK_UNDERSCORE] = XBMCK_UNDERSCORE;
+ s_keysymTable[SDLK_BACKQUOTE] = XBMCK_BACKQUOTE;
+ s_keysymTable[SDLK_a] = XBMCK_a;
+ s_keysymTable[SDLK_b] = XBMCK_b;
+ s_keysymTable[SDLK_c] = XBMCK_c;
+ s_keysymTable[SDLK_d] = XBMCK_d;
+ s_keysymTable[SDLK_e] = XBMCK_e;
+ s_keysymTable[SDLK_f] = XBMCK_f;
+ s_keysymTable[SDLK_g] = XBMCK_g;
+ s_keysymTable[SDLK_h] = XBMCK_h;
+ s_keysymTable[SDLK_i] = XBMCK_i;
+ s_keysymTable[SDLK_j] = XBMCK_j;
+ s_keysymTable[SDLK_k] = XBMCK_k;
+ s_keysymTable[SDLK_l] = XBMCK_l;
+ s_keysymTable[SDLK_m] = XBMCK_m;
+ s_keysymTable[SDLK_n] = XBMCK_n;
+ s_keysymTable[SDLK_o] = XBMCK_o;
+ s_keysymTable[SDLK_p] = XBMCK_p;
+ s_keysymTable[SDLK_q] = XBMCK_q;
+ s_keysymTable[SDLK_r] = XBMCK_r;
+ s_keysymTable[SDLK_s] = XBMCK_s;
+ s_keysymTable[SDLK_t] = XBMCK_t;
+ s_keysymTable[SDLK_u] = XBMCK_u;
+ s_keysymTable[SDLK_v] = XBMCK_v;
+ s_keysymTable[SDLK_w] = XBMCK_w;
+ s_keysymTable[SDLK_x] = XBMCK_x;
+ s_keysymTable[SDLK_y] = XBMCK_y;
+ s_keysymTable[SDLK_z] = XBMCK_z;
+ s_keysymTable[SDLK_CAPSLOCK] = XBMCK_CAPSLOCK;
+ s_keysymTable[SDLK_F1] = XBMCK_F1;
+ s_keysymTable[SDLK_F2] = XBMCK_F2;
+ s_keysymTable[SDLK_F3] = XBMCK_F3;
+ s_keysymTable[SDLK_F4] = XBMCK_F4;
+ s_keysymTable[SDLK_F5] = XBMCK_F5;
+ s_keysymTable[SDLK_F6] = XBMCK_F6;
+ s_keysymTable[SDLK_F7] = XBMCK_F7;
+ s_keysymTable[SDLK_F8] = XBMCK_F8;
+ s_keysymTable[SDLK_F9] = XBMCK_F9;
+ s_keysymTable[SDLK_F10] = XBMCK_F10;
+ s_keysymTable[SDLK_F11] = XBMCK_F11;
+ s_keysymTable[SDLK_F12] = XBMCK_F12;
+ s_keysymTable[SDLK_PRINTSCREEN] = XBMCK_PRINT;
+ s_keysymTable[SDLK_SCROLLLOCK] = XBMCK_SCROLLOCK;
+ s_keysymTable[SDLK_PAUSE] = XBMCK_PAUSE;
+ s_keysymTable[SDLK_INSERT] = XBMCK_INSERT;
+ s_keysymTable[SDLK_HOME] = XBMCK_HOME;
+ s_keysymTable[SDLK_PAGEUP] = XBMCK_PAGEUP;
+ s_keysymTable[SDLK_DELETE] = XBMCK_DELETE;
+ s_keysymTable[SDLK_END] = XBMCK_END;
+ s_keysymTable[SDLK_PAGEDOWN] = XBMCK_PAGEDOWN;
+ s_keysymTable[SDLK_RIGHT] = XBMCK_RIGHT;
+ s_keysymTable[SDLK_LEFT] = XBMCK_LEFT;
+ s_keysymTable[SDLK_DOWN] = XBMCK_DOWN;
+ s_keysymTable[SDLK_UP] = XBMCK_UP;
+ s_keysymTable[SDLK_NUMLOCKCLEAR] = XBMCK_NUMLOCK;
+ s_keysymTable[SDLK_KP_DIVIDE] = XBMCK_KP_DIVIDE;
+ s_keysymTable[SDLK_KP_MULTIPLY] = XBMCK_KP_MULTIPLY;
+ s_keysymTable[SDLK_KP_MINUS] = XBMCK_KP_MINUS;
+ s_keysymTable[SDLK_KP_PLUS] = XBMCK_KP_PLUS;
+ s_keysymTable[SDLK_KP_ENTER] = XBMCK_KP_ENTER;
+ s_keysymTable[SDLK_KP_1] = XBMCK_KP1;
+ s_keysymTable[SDLK_KP_2] = XBMCK_KP2;
+ s_keysymTable[SDLK_KP_3] = XBMCK_KP3;
+ s_keysymTable[SDLK_KP_4] = XBMCK_KP4;
+ s_keysymTable[SDLK_KP_5] = XBMCK_KP5;
+ s_keysymTable[SDLK_KP_6] = XBMCK_KP6;
+ s_keysymTable[SDLK_KP_7] = XBMCK_KP7;
+ s_keysymTable[SDLK_KP_8] = XBMCK_KP8;
+ s_keysymTable[SDLK_KP_9] = XBMCK_KP9;
+ s_keysymTable[SDLK_KP_0] = XBMCK_KP0;
+ s_keysymTable[SDLK_KP_PERIOD] = XBMCK_KP_PERIOD;
+ s_keysymTable[SDLK_APPLICATION] = XBMCK_LAUNCH_APP1;
+ s_keysymTable[SDLK_POWER] = XBMCK_POWER;
+ s_keysymTable[SDLK_KP_EQUALS] = XBMCK_KP_EQUALS;
+ s_keysymTable[SDLK_F13] = XBMCK_F13;
+ s_keysymTable[SDLK_F14] = XBMCK_F14;
+ s_keysymTable[SDLK_F15] = XBMCK_F15;
+/*
+ s_keysymTable[SDLK_F16] = XBMCK_F16;
+ s_keysymTable[SDLK_F17] = XBMCK_F17;
+ s_keysymTable[SDLK_F18] = XBMCK_F18;
+ s_keysymTable[SDLK_F19] = XBMCK_F19;
+ s_keysymTable[SDLK_F20] = XBMCK_F20;
+ s_keysymTable[SDLK_F21] = XBMCK_F21;
+ s_keysymTable[SDLK_F22] = XBMCK_F22;
+ s_keysymTable[SDLK_F23] = XBMCK_F23;
+ s_keysymTable[SDLK_F24] = XBMCK_F24;
+ s_keysymTable[SDLK_EXECUTE] = XBMCK_EXECUTE;
+*/
+ s_keysymTable[SDLK_HELP] = XBMCK_HELP;
+ s_keysymTable[SDLK_MENU] = XBMCK_MENU;
+/*
+ s_keysymTable[SDLK_SELECT] = XBMCK_SELECT;
+ s_keysymTable[SDLK_STOP] = XBMCK_STOP;
+ s_keysymTable[SDLK_AGAIN] = XBMCK_AGAIN;
+ s_keysymTable[SDLK_UNDO] = XBMCK_UNDO;
+ s_keysymTable[SDLK_CUT] = XBMCK_CUT;
+ s_keysymTable[SDLK_COPY] = XBMCK_COPY;
+ s_keysymTable[SDLK_PASTE] = XBMCK_PASTE;
+ s_keysymTable[SDLK_FIND] = XBMCK_FIND;
+*/
+ s_keysymTable[SDLK_MUTE] = XBMCK_VOLUME_MUTE;
+ s_keysymTable[SDLK_VOLUMEUP] = XBMCK_VOLUME_UP;
+ s_keysymTable[SDLK_VOLUMEDOWN] = XBMCK_VOLUME_DOWN;
+/*
+ s_keysymTable[SDLK_KP_COMMA] = XBMCK_KP_COMMA;
+ s_keysymTable[SDLK_KP_EQUALSAS400] = XBMCK_KP_EQUALSAS400;
+ s_keysymTable[SDLK_ALTERASE] = XBMCK_ALTERASE;
+*/
+ s_keysymTable[SDLK_SYSREQ] = XBMCK_SYSREQ;
+/*
+ s_keysymTable[SDLK_CANCEL] = XBMCK_CANCEL;
+*/
+ s_keysymTable[SDLK_CLEAR] = XBMCK_CLEAR;
+/*
+ s_keysymTable[SDLK_PRIOR] = XBMCK_PRIOR;
+ s_keysymTable[SDLK_RETURN2] = XBMCK_RETURN2;
+ s_keysymTable[SDLK_SEPARATOR] = XBMCK_SEPARATOR;
+ s_keysymTable[SDLK_OUT] = XBMCK_OUT;
+ s_keysymTable[SDLK_OPER] = XBMCK_OPER;
+ s_keysymTable[SDLK_CLEARAGAIN] = XBMCK_CLEARAGAIN;
+ s_keysymTable[SDLK_CRSEL] = XBMCK_CRSEL;
+ s_keysymTable[SDLK_EXSEL] = XBMCK_EXSEL;
+ s_keysymTable[SDLK_KP_00] = XBMCK_KP_00;
+ s_keysymTable[SDLK_KP_000] = XBMCK_KP_000;
+ s_keysymTable[SDLK_THOUSANDSSEPARATOR] = XBMCK_THOUSANDSSEPARATOR;
+ s_keysymTable[SDLK_DECIMALSEPARATOR] = XBMCK_DECIMALSEPARATOR;
+ s_keysymTable[SDLK_CURRENCYUNIT] = XBMCK_CURRENCYUNIT;
+ s_keysymTable[SDLK_CURRENCYSUBUNIT] = XBMCK_CURRENCYSUBUNIT;
+ s_keysymTable[SDLK_KP_LEFTPAREN] = XBMCK_KP_LEFTPAREN;
+ s_keysymTable[SDLK_KP_RIGHTPAREN] = XBMCK_KP_RIGHTPAREN;
+ s_keysymTable[SDLK_KP_LEFTBRACE] = XBMCK_KP_LEFTBRACE;
+ s_keysymTable[SDLK_KP_RIGHTBRACE] = XBMCK_KP_RIGHTBRACE;
+ s_keysymTable[SDLK_KP_TAB] = XBMCK_KP_TAB;
+ s_keysymTable[SDLK_KP_BACKSPACE] = XBMCK_KP_BACKSPACE;
+ s_keysymTable[SDLK_KP_A] = XBMCK_KP_A;
+ s_keysymTable[SDLK_KP_B] = XBMCK_KP_B;
+ s_keysymTable[SDLK_KP_C] = XBMCK_KP_C;
+ s_keysymTable[SDLK_KP_D] = XBMCK_KP_D;
+ s_keysymTable[SDLK_KP_E] = XBMCK_KP_E;
+ s_keysymTable[SDLK_KP_F] = XBMCK_KP_F;
+ s_keysymTable[SDLK_KP_XOR] = XBMCK_KP_XOR;
+ s_keysymTable[SDLK_KP_POWER] = XBMCK_KP_POWER;
+ s_keysymTable[SDLK_KP_PERCENT] = XBMCK_KP_PERCENT;
+ s_keysymTable[SDLK_KP_LESS] = XBMCK_KP_LESS;
+ s_keysymTable[SDLK_KP_GREATER] = XBMCK_KP_GREATER;
+ s_keysymTable[SDLK_KP_AMPERSAND] = XBMCK_KP_AMPERSAND;
+ s_keysymTable[SDLK_KP_DBLAMPERSAND] = XBMCK_KP_DBLAMPERSAND;
+ s_keysymTable[SDLK_KP_VERTICALBAR] = XBMCK_KP_VERTICALBAR;
+ s_keysymTable[SDLK_KP_DBLVERTICALBAR] = XBMCK_KP_DBLVERTICALBAR;
+ s_keysymTable[SDLK_KP_COLON] = XBMCK_KP_COLON;
+ s_keysymTable[SDLK_KP_HASH] = XBMCK_KP_HASH;
+ s_keysymTable[SDLK_KP_SPACE] = XBMCK_KP_SPACE;
+ s_keysymTable[SDLK_KP_AT] = XBMCK_KP_AT;
+ s_keysymTable[SDLK_KP_EXCLAM] = XBMCK_KP_EXCLAM;
+ s_keysymTable[SDLK_KP_MEMSTORE] = XBMCK_KP_MEMSTORE;
+ s_keysymTable[SDLK_KP_MEMRECALL] = XBMCK_KP_MEMRECALL;
+ s_keysymTable[SDLK_KP_MEMCLEAR] = XBMCK_KP_MEMCLEAR;
+ s_keysymTable[SDLK_KP_MEMADD] = XBMCK_KP_MEMADD;
+ s_keysymTable[SDLK_KP_MEMSUBTRACT] = XBMCK_KP_MEMSUBTRACT;
+ s_keysymTable[SDLK_KP_MEMMULTIPLY] = XBMCK_KP_MEMMULTIPLY;
+ s_keysymTable[SDLK_KP_MEMDIVIDE] = XBMCK_KP_MEMDIVIDE;
+ s_keysymTable[SDLK_KP_PLUSMINUS] = XBMCK_KP_PLUSMINUS;
+ s_keysymTable[SDLK_KP_CLEAR] = XBMCK_KP_CLEAR;
+ s_keysymTable[SDLK_KP_CLEARENTRY] = XBMCK_KP_CLEARENTRY;
+ s_keysymTable[SDLK_KP_BINARY] = XBMCK_KP_BINARY;
+ s_keysymTable[SDLK_KP_OCTAL] = XBMCK_KP_OCTAL;
+ s_keysymTable[SDLK_KP_DECIMAL] = XBMCK_KP_DECIMAL;
+ s_keysymTable[SDLK_KP_HEXADECIMAL] = XBMCK_KP_HEXADECIMAL;
+*/
+ s_keysymTable[SDLK_LCTRL] = XBMCK_LCTRL;
+ s_keysymTable[SDLK_LSHIFT] = XBMCK_LSHIFT;
+ s_keysymTable[SDLK_LALT] = XBMCK_LALT;
+ s_keysymTable[SDLK_LGUI] = XBMCK_LSUPER;
+ s_keysymTable[SDLK_RCTRL] = XBMCK_RCTRL;
+ s_keysymTable[SDLK_RSHIFT] = XBMCK_RSHIFT;
+ s_keysymTable[SDLK_RALT] = XBMCK_RALT;
+ s_keysymTable[SDLK_RGUI] = XBMCK_RSUPER;
+ s_keysymTable[SDLK_MODE] = XBMCK_MODE;
+ s_keysymTable[SDLK_AUDIONEXT] = XBMCK_MEDIA_NEXT_TRACK;
+ s_keysymTable[SDLK_AUDIOPREV] = XBMCK_MEDIA_PREV_TRACK;
+ s_keysymTable[SDLK_AUDIOSTOP] = XBMCK_MEDIA_STOP;
+ s_keysymTable[SDLK_AUDIOPLAY] = XBMCK_MEDIA_PLAY_PAUSE;
+ s_keysymTable[SDLK_AUDIOMUTE] = XBMCK_VOLUME_MUTE;
+ s_keysymTable[SDLK_MEDIASELECT] = XBMCK_LAUNCH_MEDIA_SELECT;
+ s_keysymTable[SDLK_WWW] = XBMCK_BROWSER_HOME;
+ s_keysymTable[SDLK_MAIL] = XBMCK_LAUNCH_MAIL;
+ s_keysymTable[SDLK_CALCULATOR] = XBMCK_LAUNCH_APP1;
+ s_keysymTable[SDLK_COMPUTER] = XBMCK_LAUNCH_FILE_BROWSER;
+ s_keysymTable[SDLK_AC_SEARCH] = XBMCK_BROWSER_SEARCH;
+ s_keysymTable[SDLK_AC_HOME] = XBMCK_BROWSER_HOME;
+ s_keysymTable[SDLK_AC_BACK] = XBMCK_BROWSER_BACK;
+ s_keysymTable[SDLK_AC_FORWARD] = XBMCK_BROWSER_FORWARD;
+ s_keysymTable[SDLK_AC_STOP] = XBMCK_BROWSER_STOP;
+ s_keysymTable[SDLK_AC_REFRESH] = XBMCK_BROWSER_REFRESH;
+ s_keysymTable[SDLK_AC_BOOKMARKS] = XBMCK_BROWSER_FAVORITES;
+/*
+ s_keysymTable[SDLK_BRIGHTNESSDOWN] = XBMCK_BRIGHTNESSDOWN;
+ s_keysymTable[SDLK_BRIGHTNESSUP] = XBMCK_BRIGHTNESSUP;
+ s_keysymTable[SDLK_DISPLAYSWITCH] = XBMCK_DISPLAYSWITCH;
+ s_keysymTable[SDLK_KBDILLUMTOGGLE] = XBMCK_KBDILLUMTOGGLE;
+ s_keysymTable[SDLK_KBDILLUMDOWN] = XBMCK_KBDILLUMDOWN;
+ s_keysymTable[SDLK_KBDILLUMUP] = XBMCK_KBDILLUMUP;
+*/
+ s_keysymTable[SDLK_EJECT] = XBMCK_EJECT;
+ s_keysymTable[SDLK_SLEEP] = XBMCK_SLEEP;
+}
+
+static XBMCKey LookupKeysym(SDL_Keycode keycode)
+{
+ if (s_keysymTable.size() == 0) {
+ InitializeKeysymLookup();
+ }
+
+ std::map::iterator it = s_keysymTable.find(keycode);
+ if (it != s_keysymTable.end()) {
+ return it->second;
+ }
+ return XBMCK_UNKNOWN;
+}
+
+#endif // SDL 2.0
+
bool CWinEventsSDL::MessagePump()
{
SDL_Event event;
@@ -229,6 +499,7 @@ bool CWinEventsSDL::MessagePump()
CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
break;
+#if HAVE_SDL_VERSION == 1
case SDL_ACTIVEEVENT:
//If the window was inconified or restored
if( event.active.state & SDL_APPACTIVE )
@@ -270,7 +541,7 @@ bool CWinEventsSDL::MessagePump()
mod |= XBMCKMOD_LSUPER;
newEvent.key.keysym.mod = (XBMCMod) mod;
-#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
+#ifdef HAVE_X11
// If the keysym.sym is zero try to get it from the scan code
if (newEvent.key.keysym.sym == 0)
newEvent.key.keysym.sym = (XBMCKey) SymFromScancode(newEvent.key.keysym.scancode);
@@ -353,6 +624,7 @@ bool CWinEventsSDL::MessagePump()
ret |= g_application.OnEvent(newEvent);
break;
}
+
case SDL_VIDEORESIZE:
{
// Under newer osx versions sdl is so fucked up that it even fires resize events
@@ -372,6 +644,161 @@ bool CWinEventsSDL::MessagePump()
g_windowManager.MarkDirty();
break;
}
+
+ case SDL_VIDEOEXPOSE:
+ {
+ g_windowManager.MarkDirty();
+ break;
+ }
+#endif // SDL 1.2
+
+#if HAVE_SDL_VERSION == 2
+ case SDL_WINDOWEVENT:
+ {
+ switch( event.window.event )
+ {
+ case SDL_WINDOWEVENT_MINIMIZED:
+ if (g_application.GetRenderGUI())
+ {
+ g_application.SetRenderGUI(false);
+ g_Windowing.NotifyAppActiveChange(g_application.GetRenderGUI());
+ }
+ break;
+ case SDL_WINDOWEVENT_RESTORED:
+ if (!g_application.GetRenderGUI())
+ {
+ g_application.SetRenderGUI(true);
+ g_Windowing.NotifyAppActiveChange(g_application.GetRenderGUI());
+ }
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ g_application.m_AppFocused = true;
+ g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused);
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ g_application.m_AppFocused = false;
+ g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused);
+ break;
+ }
+ break;
+ }
+
+ case SDL_KEYDOWN:
+ {
+ // process any platform specific shortcuts before handing off to XBMC
+#ifdef TARGET_DARWIN_OSX
+ if (ProcessOSXShortcuts(event))
+ {
+ ret = true;
+ break;
+ }
+#endif
+
+ // See if there's a text event associated with this keypress
+ // This won't catch multi-character composed input, but it's better than nothing
+ uint16_t unicode = 0;
+ SDL_Event next_event;
+ if (SDL_PeepEvents(&next_event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) > 0 &&
+ next_event.type == SDL_TEXTINPUT)
+ {
+ SDL_PeepEvents(&next_event, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT);
+ if (next_event.text.text[0] < 0xC0)
+ unicode = next_event.text.text[0];
+ else
+ {
+ Uint16 *text16 = SDL_iconv_utf8_ucs2(next_event.text.text);
+ if (text16)
+ {
+ unicode = text16[0];
+ SDL_free(text16);
+ }
+ }
+ }
+
+ XBMC_Event newEvent;
+ newEvent.type = XBMC_KEYDOWN;
+ newEvent.key.keysym.scancode = event.key.keysym.scancode;
+ newEvent.key.keysym.sym = LookupKeysym(event.key.keysym.sym);
+ newEvent.key.keysym.unicode = unicode;
+ newEvent.key.state = event.key.state;
+
+ // Check if the Windows keys are down because SDL doesn't flag this.
+ uint16_t mod = event.key.keysym.mod;
+ const uint8_t* keystate = SDL_GetKeyboardState(NULL);
+ if (keystate[SDL_SCANCODE_LGUI] || keystate[SDL_SCANCODE_RGUI])
+ mod |= XBMCKMOD_LSUPER;
+ newEvent.key.keysym.mod = (XBMCMod) mod;
+
+#ifdef HAVE_X11
+ // If the keysym.sym is zero try to get it from the scan code
+ if (newEvent.key.keysym.sym == 0)
+ newEvent.key.keysym.sym = (XBMCKey) SymFromScancode(newEvent.key.keysym.scancode);
+#endif
+
+ // don't handle any more messages in the queue until we've handled keydown,
+ // if a keyup is in the queue it will reset the keypress before it is handled.
+ ret |= g_application.OnEvent(newEvent);
+ break;
+ }
+
+ case SDL_KEYUP:
+ {
+ XBMC_Event newEvent;
+ newEvent.type = XBMC_KEYUP;
+ newEvent.key.keysym.scancode = event.key.keysym.scancode;
+ newEvent.key.keysym.sym = LookupKeysym(event.key.keysym.sym);
+ newEvent.key.keysym.mod =(XBMCMod) event.key.keysym.mod;
+ newEvent.key.keysym.unicode = 0;
+ newEvent.key.state = event.key.state;
+
+ ret |= g_application.OnEvent(newEvent);
+ break;
+ }
+
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ XBMC_Event newEvent;
+ newEvent.type = XBMC_MOUSEBUTTONDOWN;
+ newEvent.button.button = event.button.button;
+ newEvent.button.state = event.button.state;
+ newEvent.button.which = event.button.which;
+ newEvent.button.x = event.button.x;
+ newEvent.button.y = event.button.y;
+
+ ret |= g_application.OnEvent(newEvent);
+ break;
+ }
+
+ case SDL_MOUSEBUTTONUP:
+ {
+ XBMC_Event newEvent;
+ newEvent.type = XBMC_MOUSEBUTTONUP;
+ newEvent.button.button = event.button.button;
+ newEvent.button.state = event.button.state;
+ newEvent.button.which = event.button.which;
+ newEvent.button.x = event.button.x;
+ newEvent.button.y = event.button.y;
+
+ ret |= g_application.OnEvent(newEvent);
+ break;
+ }
+
+ case SDL_MOUSEMOTION:
+ {
+ XBMC_Event newEvent;
+ newEvent.type = XBMC_MOUSEMOTION;
+ newEvent.motion.xrel = event.motion.xrel;
+ newEvent.motion.yrel = event.motion.yrel;
+ newEvent.motion.state = event.motion.state;
+ newEvent.motion.which = event.motion.which;
+ newEvent.motion.x = event.motion.x;
+ newEvent.motion.y = event.motion.y;
+
+ ret |= g_application.OnEvent(newEvent);
+ break;
+ }
+#endif // SDL 2.0
+
case SDL_USEREVENT:
{
XBMC_Event newEvent;
@@ -380,9 +807,6 @@ bool CWinEventsSDL::MessagePump()
ret |= g_application.OnEvent(newEvent);
break;
}
- case SDL_VIDEOEXPOSE:
- g_windowManager.MarkDirty();
- break;
}
memset(&event, 0, sizeof(SDL_Event));
}
@@ -392,11 +816,16 @@ bool CWinEventsSDL::MessagePump()
size_t CWinEventsSDL::GetQueueSize()
{
- int ret;
- SDL_Event event;
+ int ret = 0;
- if (-1 == (ret = SDL_PeepEvents(&event, 0, SDL_PEEKEVENT, ~0)))
+#if HAVE_SDL_VERSION == 1
+ if (-1 == (ret = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, ~0)))
+ ret = 0;
+#endif
+#if HAVE_SDL_VERSION == 2
+ if (-1 == (ret = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)))
ret = 0;
+#endif
return ret;
}
diff --git a/xbmc/windowing/WinEventsSDL.h b/xbmc/windowing/WinEventsSDL.h
index 6a23658e5a..154e588c29 100644
--- a/xbmc/windowing/WinEventsSDL.h
+++ b/xbmc/windowing/WinEventsSDL.h
@@ -28,7 +28,7 @@
#if HAVE_SDL_VERSION == 1
#include
#elif HAVE_SDL_VERSION == 2
-#include
+#include
#endif
#include "WinEvents.h"
diff --git a/xbmc/windowing/egl/EGLNativeTypeSDL.cpp b/xbmc/windowing/egl/EGLNativeTypeSDL.cpp
new file mode 100644
index 0000000000..d3523cb93a
--- /dev/null
+++ b/xbmc/windowing/egl/EGLNativeTypeSDL.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011-2013 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, see
+ * .
+ *
+ */
+#include "system.h"
+
+#include "EGLNativeTypeSDL.h"
+#include "guilib/gui3d.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+
+#if HAVE_SDL_VERSION == 2
+
+CEGLNativeTypeSDL::CEGLNativeTypeSDL()
+{
+ m_window = NULL;
+
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
+ return;
+ }
+
+ SDL_DisplayMode mode;
+ SDL_GetDesktopDisplayMode(0, &mode);
+
+ m_window = SDL_CreateWindow("SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, mode.w, mode.h, SDL_WINDOW_FULLSCREEN|SDL_WINDOW_HIDDEN);
+ if (!m_window) {
+ return;
+ }
+
+ SDL_VERSION(&m_windowInfo.version);
+ if (SDL_GetWindowWMInfo(m_window, &m_windowInfo) < 0 || m_windowInfo.subsystem != SDL_SYSWM_VIVANTE) {
+ SDL_DestroyWindow(m_window);
+ m_window = NULL;
+ }
+
+ // XBMC draws its own cursor
+ SDL_ShowCursor(0);
+}
+
+CEGLNativeTypeSDL::~CEGLNativeTypeSDL()
+{
+ if (m_window) {
+ SDL_DestroyWindow(m_window);
+ }
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+bool CEGLNativeTypeSDL::CheckCompatibility()
+{
+ return (m_window != NULL);
+}
+
+void CEGLNativeTypeSDL::Initialize()
+{
+}
+
+void CEGLNativeTypeSDL::Destroy()
+{
+}
+
+bool CEGLNativeTypeSDL::CreateNativeDisplay()
+{
+ return true;
+}
+
+bool CEGLNativeTypeSDL::CreateNativeWindow()
+{
+ SDL_ShowWindow(m_window);
+ return true;
+}
+
+bool CEGLNativeTypeSDL::GetNativeDisplay(XBNativeDisplayType **nativeDisplay) const
+{
+ if (!nativeDisplay)
+ return false;
+ *nativeDisplay = (XBNativeDisplayType*) &m_windowInfo.info.vivante.display;
+ return true;
+}
+
+bool CEGLNativeTypeSDL::GetNativeWindow(XBNativeDisplayType **nativeWindow) const
+{
+ if (!nativeWindow)
+ return false;
+ *nativeWindow = (XBNativeWindowType*) &m_windowInfo.info.vivante.window;
+ return true;
+}
+
+bool CEGLNativeTypeSDL::DestroyNativeDisplay()
+{
+ return true;
+}
+
+bool CEGLNativeTypeSDL::DestroyNativeWindow()
+{
+ SDL_HideWindow(m_window);
+ return true;
+}
+
+bool CEGLNativeTypeSDL::GetNativeResolution(RESOLUTION_INFO *res) const
+{
+ SDL_GetWindowSize(m_window, &res->iWidth, &res->iHeight);
+ res->fRefreshRate = 59.94f;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ res->iScreen = 0;
+ res->bFullScreen = true;
+ res->iSubtitles = static_cast(0.965 * res->iHeight);
+ res->fPixelRatio = 1.0f;
+ res->iScreenWidth = res->iWidth;
+ res->iScreenHeight = res->iHeight;
+ res->strMode = StringUtils::Format("%dx%d @ %.2fp",
+ res->iScreenWidth,
+ res->iScreenHeight,
+ res->fRefreshRate);
+ return true;
+}
+
+bool CEGLNativeTypeSDL::SetNativeResolution(const RESOLUTION_INFO &res)
+{
+ return true;
+}
+
+bool CEGLNativeTypeSDL::ProbeResolutions(std::vector &resolutions)
+{
+ RESOLUTION_INFO res;
+ if (!GetNativeResolution(&res))
+ return false;
+
+ resolutions.push_back(res);
+ return true;
+}
+
+bool CEGLNativeTypeSDL::GetPreferredResolution(RESOLUTION_INFO *res) const
+{
+ return GetNativeResolution(res);
+}
+
+bool CEGLNativeTypeSDL::ShowWindow(bool show)
+{
+ if (show)
+ SDL_ShowWindow(m_window);
+ else
+ SDL_HideWindow(m_window);
+ return true;
+}
+
+#endif // SDL 2.0
diff --git a/xbmc/windowing/egl/EGLNativeTypeSDL.h b/xbmc/windowing/egl/EGLNativeTypeSDL.h
new file mode 100644
index 0000000000..387a8485e8
--- /dev/null
+++ b/xbmc/windowing/egl/EGLNativeTypeSDL.h
@@ -0,0 +1,60 @@
+#pragma once
+
+/*
+ * Copyright (C) 2011-2013 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, see
+ * .
+ *
+ */
+
+#if HAVE_SDL_VERSION == 2
+
+#include "EGLNativeType.h"
+#include "SDL2/SDL.h"
+#include "SDL2/SDL_syswm.h"
+
+class CEGLNativeTypeSDL : public CEGLNativeType
+{
+public:
+ CEGLNativeTypeSDL();
+ virtual ~CEGLNativeTypeSDL();
+ virtual std::string GetNativeName() const { return "SDL"; };
+ virtual bool CheckCompatibility();
+ virtual void Initialize();
+ virtual void Destroy();
+ virtual int GetQuirks() { return EGL_QUIRK_NONE; };
+
+ virtual bool CreateNativeDisplay();
+ virtual bool CreateNativeWindow();
+ virtual bool GetNativeDisplay(XBNativeDisplayType **nativeDisplay) const;
+ virtual bool GetNativeWindow(XBNativeWindowType **nativeWindow) const;
+
+ virtual bool DestroyNativeWindow();
+ virtual bool DestroyNativeDisplay();
+
+ virtual bool GetNativeResolution(RESOLUTION_INFO *res) const;
+ virtual bool SetNativeResolution(const RESOLUTION_INFO &res);
+ virtual bool ProbeResolutions(std::vector &resolutions);
+ virtual bool GetPreferredResolution(RESOLUTION_INFO *res) const;
+
+ virtual bool ShowWindow(bool show);
+
+private:
+ SDL_Window *m_window;
+ SDL_SysWMinfo m_windowInfo;
+};
+
+#endif // SDL 2.0
diff --git a/xbmc/windowing/egl/EGLWrapper.cpp b/xbmc/windowing/egl/EGLWrapper.cpp
index 30f57577d6..6422bfbc5b 100644
--- a/xbmc/windowing/egl/EGLWrapper.cpp
+++ b/xbmc/windowing/egl/EGLWrapper.cpp
@@ -37,6 +37,9 @@
#if defined(TARGET_LINUX) && defined(HAS_LIBAMCODEC)
#include "EGLNativeTypeAmlogic.h"
#endif
+#if HAVE_SDL_VERSION == 2
+#include "EGLNativeTypeSDL.h"
+#endif
#include "EGLWrapper.h"
#define CheckError() m_result = eglGetError(); if(m_result != EGL_SUCCESS) CLog::Log(LOGERROR, "EGL error in %s: %x",__FUNCTION__, m_result);
@@ -101,6 +104,8 @@ bool CEGLWrapper::Initialize(const std::string &implementation)
(nativeGuess = CreateEGLNativeType(implementation))
#elif defined(TARGET_LINUX) && defined(HAS_LIBAMCODEC)
(nativeGuess = CreateEGLNativeType(implementation))
+#elif HAVE_SDL_VERSION == 2
+ (nativeGuess = CreateEGLNativeType(implementation))
#endif
)
{
diff --git a/xbmc/windowing/egl/Makefile.in b/xbmc/windowing/egl/Makefile.in
index 68f7862dfe..02a3ba5485 100644
--- a/xbmc/windowing/egl/Makefile.in
+++ b/xbmc/windowing/egl/Makefile.in
@@ -1,6 +1,7 @@
INCLUDES=-I.
SRCS = WinSystemEGL.cpp
+SRCS+= EGLNativeTypeSDL.cpp
SRCS+= EGLNativeTypeAmlogic.cpp
ifeq (@USE_ANDROID@,1)
SRCS+= EGLNativeTypeAndroid.cpp