aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--Kodi.xcodeproj/project.pbxproj58
-rw-r--r--Makefile.include.in2
-rw-r--r--addons/resource.language.en_gb/resources/strings.po13
-rw-r--r--addons/skin.confluence/720p/DialogExtendedProgressBar.xml2
-rw-r--r--addons/skin.confluence/720p/DialogVolumeBar.xml2
-rw-r--r--addons/skin.confluence/720p/MyPVRChannels.xml13
-rw-r--r--addons/skin.confluence/720p/VideoOSD.xml4
-rw-r--r--addons/skin.confluence/720p/ViewsVideoLibrary.xml4
-rw-r--r--addons/skin.confluence/720p/script-cu-lrclyrics-main.xml2
-rw-r--r--addons/skin.confluence/720p/script-globalsearch-main.xml1
-rw-r--r--addons/skin.confluence/media/VideoWindowFO.pngbin0 -> 152 bytes
m---------addons/skin.re-touched0
-rw-r--r--docs/README.android2
-rw-r--r--system/keyboardlayouts/czech.xml27
-rw-r--r--system/settings/settings.xml5
-rw-r--r--tools/android/packaging/xbmc/AndroidManifest.xml.in12
-rw-r--r--tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in15
-rw-r--r--tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java.in22
-rw-r--r--tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java.in3
-rw-r--r--xbmc/Application.cpp6
-rw-r--r--xbmc/GUIInfoManager.cpp12
-rw-r--r--xbmc/android/activity/AndroidKey.h4
-rw-r--r--xbmc/android/activity/JNIMainActivity.cpp12
-rw-r--r--xbmc/android/activity/JNIMainActivity.h2
-rw-r--r--xbmc/android/activity/XBMCApp.cpp114
-rw-r--r--xbmc/android/activity/XBMCApp.h5
-rw-r--r--xbmc/android/jni/ApplicationInfo.cpp2
-rw-r--r--xbmc/android/jni/ApplicationInfo.h3
-rw-r--r--xbmc/android/jni/Context.cpp6
-rw-r--r--xbmc/android/jni/DisplayMetrics.cpp50
-rw-r--r--xbmc/android/jni/DisplayMetrics.h42
-rw-r--r--xbmc/android/jni/Intent.cpp15
-rw-r--r--xbmc/android/jni/Intent.h4
-rw-r--r--xbmc/android/jni/KeyEvent.cpp68
-rw-r--r--xbmc/android/jni/KeyEvent.h68
-rw-r--r--xbmc/android/jni/Makefile.in4
-rw-r--r--xbmc/android/jni/PackageItemInfo.cpp30
-rw-r--r--xbmc/android/jni/PackageItemInfo.h36
-rw-r--r--xbmc/android/jni/PackageManager.cpp14
-rw-r--r--xbmc/android/jni/PackageManager.h3
-rw-r--r--xbmc/android/jni/Resources.cpp33
-rw-r--r--xbmc/android/jni/Resources.h37
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp17
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h2
-rw-r--r--xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp103
-rw-r--r--xbmc/cores/VideoRenderers/WinRenderer.cpp121
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp9
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp7
-rw-r--r--xbmc/filesystem/AndroidAppDirectory.cpp1
-rw-r--r--xbmc/filesystem/AndroidAppFile.cpp75
-rw-r--r--xbmc/filesystem/AndroidAppFile.h19
-rw-r--r--xbmc/filesystem/MusicDatabaseDirectory.cpp2
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory.cpp1
-rw-r--r--xbmc/guilib/GUIToggleButtonControl.cpp32
-rw-r--r--xbmc/guilib/GUIToggleButtonControl.h5
-rw-r--r--xbmc/guilib/GraphicContext.h4
-rw-r--r--xbmc/guilib/Texture.cpp15
-rw-r--r--xbmc/interfaces/json-rpc/AudioLibrary.cpp107
-rw-r--r--xbmc/interfaces/json-rpc/AudioLibrary.h1
-rw-r--r--xbmc/interfaces/json-rpc/schema/version.txt2
-rw-r--r--xbmc/music/Album.cpp14
-rw-r--r--xbmc/music/Album.h5
-rw-r--r--xbmc/music/MusicDatabase.cpp448
-rw-r--r--xbmc/music/MusicDatabase.h5
-rw-r--r--xbmc/music/Song.cpp14
-rw-r--r--xbmc/music/Song.h5
-rw-r--r--xbmc/music/tags/MusicInfoTag.cpp8
-rw-r--r--xbmc/music/tags/TagLoaderTagLib.cpp399
-rw-r--r--xbmc/music/tags/TagLoaderTagLib.h27
-rw-r--r--xbmc/music/tags/test/TestTagLoaderTagLib.cpp148
-rw-r--r--xbmc/pvr/PVRGUIInfo.cpp6
-rw-r--r--xbmc/pvr/PVRManager.cpp2
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp10
-rw-r--r--xbmc/rendering/dx/RenderSystemDX.cpp4
-rw-r--r--xbmc/settings/AdvancedSettings.cpp2
-rw-r--r--xbmc/settings/AdvancedSettings.h4
-rw-r--r--xbmc/settings/Settings.cpp1
-rw-r--r--xbmc/settings/Settings.h1
-rw-r--r--xbmc/settings/dialogs/GUIDialogAudioDSPSettings.cpp2
-rw-r--r--xbmc/threads/Thread.cpp2
-rw-r--r--xbmc/threads/platform/win/Win32Exception.cpp4
-rw-r--r--xbmc/utils/CharsetConverter.h4
-rw-r--r--xbmc/utils/GlobalsHandling.h10
-rw-r--r--xbmc/utils/LabelFormatter.cpp2
-rw-r--r--xbmc/video/VideoDatabase.cpp2
-rw-r--r--xbmc/windowing/egl/EGLNativeTypeAndroid.cpp8
-rw-r--r--xbmc/windows/GUIWindowScreensaverDim.cpp7
-rw-r--r--xbmc/windows/GUIWindowScreensaverDim.h1
89 files changed, 1786 insertions, 645 deletions
diff --git a/.gitignore b/.gitignore
index f13941f566..4d36744355 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ testMain
*.kdev4
# gedit
*~
+# CLion
+/.idea
# generated files etc
config.cache
@@ -406,14 +408,16 @@ lib/cpluff/stamp-h1
/skin/Confluence/BUILD
#/tools/android
-/tools/android/packaging/xbmc/AndroidManifest.xml
/tools/android/packaging/Makefile
+/tools/android/packaging/xbmc/activity_main.xml
+/tools/android/packaging/xbmc/AndroidManifest.xml
/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java
/tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java
/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCBroadcastReceiver.java
/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java
/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnFrameAvailableListener.java
/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCSettingsContentObserver.java
+/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java
/tools/android/packaging/xbmc/strings.xml
#/tools/depends
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj
index 01c2e44f31..8fb3ba756a 100644
--- a/Kodi.xcodeproj/project.pbxproj
+++ b/Kodi.xcodeproj/project.pbxproj
@@ -139,8 +139,9 @@
1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D638126161E211E003603ED /* PeripheralImon.cpp */; };
1DAFDB7C16DFDCA7007F8C68 /* PeripheralBusCEC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DAFDB7A16DFDCA7007F8C68 /* PeripheralBusCEC.cpp */; };
1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; };
- 2A7B2BDC1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; settings = {ASSET_TAGS = (); }; };
- 2A7B2BDD1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; settings = {ASSET_TAGS = (); }; };
+ 2A1A5A6C1BDEAA6D0084702D /* NOTE in Resources */ = {isa = PBXBuildFile; fileRef = 2A1A5A5D1BDEAA6D0084702D /* NOTE */; };
+ 2A7B2BDC1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; };
+ 2A7B2BDD1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; };
2F4564D51970129A00396109 /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F4564D31970129A00396109 /* GUIFontCache.cpp */; };
2F4564D61970129A00396109 /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F4564D31970129A00396109 /* GUIFontCache.cpp */; };
32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; };
@@ -284,8 +285,8 @@
7C2612711825B6340086E04D /* DatabaseQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */; };
7C2612721825B6340086E04D /* DatabaseQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */; };
7C2D6AE40F35453E00DD2E85 /* SpecialProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */; };
- 7C3018311BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C30182F1BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp */; settings = {ASSET_TAGS = (); }; };
- 7C3018321BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C30182F1BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp */; settings = {ASSET_TAGS = (); }; };
+ 7C3018311BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C30182F1BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp */; };
+ 7C3018321BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C30182F1BCC2DE200B1FE6F /* MusicInfoTagLoaderFFmpeg.cpp */; };
7C4458BD161E203800A905F6 /* Screenshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C4458BB161E203800A905F6 /* Screenshot.cpp */; };
7C45DBE910F325C400D4BBF3 /* DAVDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C45DBE710F325C400D4BBF3 /* DAVDirectory.cpp */; };
7C4705AE12EF584C00369E51 /* AddonInstaller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C4705AC12EF584C00369E51 /* AddonInstaller.cpp */; };
@@ -2491,6 +2492,26 @@
1DAFDB7B16DFDCA7007F8C68 /* PeripheralBusCEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralBusCEC.h; sourceTree = "<group>"; };
1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = "<group>"; };
1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = "<group>"; };
+ 2A1A5A581BDEAA6D0084702D /* kodi_adsp_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kodi_adsp_dll.h; sourceTree = "<group>"; };
+ 2A1A5A591BDEAA6D0084702D /* kodi_adsp_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kodi_adsp_types.h; sourceTree = "<group>"; };
+ 2A1A5A5A1BDEAA6D0084702D /* kodi_audiodec_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kodi_audiodec_dll.h; sourceTree = "<group>"; };
+ 2A1A5A5B1BDEAA6D0084702D /* kodi_audiodec_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kodi_audiodec_types.h; sourceTree = "<group>"; };
+ 2A1A5A5C1BDEAA6D0084702D /* kodi_audioengine_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kodi_audioengine_types.h; sourceTree = "<group>"; };
+ 2A1A5A5D1BDEAA6D0084702D /* NOTE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NOTE; sourceTree = "<group>"; };
+ 2A1A5A5E1BDEAA6D0084702D /* xbmc_addon_cpp_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_addon_cpp_dll.h; sourceTree = "<group>"; };
+ 2A1A5A5F1BDEAA6D0084702D /* xbmc_addon_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_addon_dll.h; sourceTree = "<group>"; };
+ 2A1A5A601BDEAA6D0084702D /* xbmc_addon_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_addon_types.h; sourceTree = "<group>"; };
+ 2A1A5A611BDEAA6D0084702D /* xbmc_audioenc_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_audioenc_dll.h; sourceTree = "<group>"; };
+ 2A1A5A621BDEAA6D0084702D /* xbmc_audioenc_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_audioenc_types.h; sourceTree = "<group>"; };
+ 2A1A5A631BDEAA6D0084702D /* xbmc_codec_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_codec_types.h; sourceTree = "<group>"; };
+ 2A1A5A641BDEAA6D0084702D /* xbmc_epg_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_epg_types.h; sourceTree = "<group>"; };
+ 2A1A5A651BDEAA6D0084702D /* xbmc_pvr_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_pvr_dll.h; sourceTree = "<group>"; };
+ 2A1A5A661BDEAA6D0084702D /* xbmc_pvr_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_pvr_types.h; sourceTree = "<group>"; };
+ 2A1A5A671BDEAA6D0084702D /* xbmc_scr_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_scr_dll.h; sourceTree = "<group>"; };
+ 2A1A5A681BDEAA6D0084702D /* xbmc_scr_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_scr_types.h; sourceTree = "<group>"; };
+ 2A1A5A691BDEAA6D0084702D /* xbmc_stream_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = xbmc_stream_utils.hpp; sourceTree = "<group>"; };
+ 2A1A5A6A1BDEAA6D0084702D /* xbmc_vis_dll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_vis_dll.h; sourceTree = "<group>"; };
+ 2A1A5A6B1BDEAA6D0084702D /* xbmc_vis_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmc_vis_types.h; sourceTree = "<group>"; };
2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRSettings.cpp; sourceTree = "<group>"; };
2A7B2BDE1BD6F18B00044BCD /* PVRSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRSettings.h; sourceTree = "<group>"; };
2F4564D31970129A00396109 /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = "<group>"; };
@@ -4874,6 +4895,7 @@
18B49FF01152BEEB001AF8A6 /* addons */ = {
isa = PBXGroup;
children = (
+ 2A1A5A571BDEAA6D0084702D /* include */,
7C226E3C1BA5F61C00185CE0 /* AddonCallbacksAudioEngine.cpp */,
7C226E3D1BA5F61C00185CE0 /* AddonCallbacksAudioEngine.h */,
7C973CE31B50378E0002A874 /* AddonCallbacksAudioDSP.cpp */,
@@ -5288,6 +5310,33 @@
path = virtual;
sourceTree = "<group>";
};
+ 2A1A5A571BDEAA6D0084702D /* include */ = {
+ isa = PBXGroup;
+ children = (
+ 2A1A5A581BDEAA6D0084702D /* kodi_adsp_dll.h */,
+ 2A1A5A591BDEAA6D0084702D /* kodi_adsp_types.h */,
+ 2A1A5A5A1BDEAA6D0084702D /* kodi_audiodec_dll.h */,
+ 2A1A5A5B1BDEAA6D0084702D /* kodi_audiodec_types.h */,
+ 2A1A5A5C1BDEAA6D0084702D /* kodi_audioengine_types.h */,
+ 2A1A5A5D1BDEAA6D0084702D /* NOTE */,
+ 2A1A5A5E1BDEAA6D0084702D /* xbmc_addon_cpp_dll.h */,
+ 2A1A5A5F1BDEAA6D0084702D /* xbmc_addon_dll.h */,
+ 2A1A5A601BDEAA6D0084702D /* xbmc_addon_types.h */,
+ 2A1A5A611BDEAA6D0084702D /* xbmc_audioenc_dll.h */,
+ 2A1A5A621BDEAA6D0084702D /* xbmc_audioenc_types.h */,
+ 2A1A5A631BDEAA6D0084702D /* xbmc_codec_types.h */,
+ 2A1A5A641BDEAA6D0084702D /* xbmc_epg_types.h */,
+ 2A1A5A651BDEAA6D0084702D /* xbmc_pvr_dll.h */,
+ 2A1A5A661BDEAA6D0084702D /* xbmc_pvr_types.h */,
+ 2A1A5A671BDEAA6D0084702D /* xbmc_scr_dll.h */,
+ 2A1A5A681BDEAA6D0084702D /* xbmc_scr_types.h */,
+ 2A1A5A691BDEAA6D0084702D /* xbmc_stream_utils.hpp */,
+ 2A1A5A6A1BDEAA6D0084702D /* xbmc_vis_dll.h */,
+ 2A1A5A6B1BDEAA6D0084702D /* xbmc_vis_types.h */,
+ );
+ path = include;
+ sourceTree = "<group>";
+ };
38F4E56013CCCB3B00664821 /* platform */ = {
isa = PBXGroup;
children = (
@@ -8871,6 +8920,7 @@
buildActionMask = 2147483647;
files = (
E49910B5174D0E2A00741B6D /* Default-568h@2x.png in Resources */,
+ 2A1A5A6C1BDEAA6D0084702D /* NOTE in Resources */,
E49910B6174D0E2A00741B6D /* InfoPlist.strings in Resources */,
DFFA440119104C1300C3923B /* AppIcon29x29.png in Resources */,
DFFA440219104C1300C3923B /* AppIcon29x29@2x.png in Resources */,
diff --git a/Makefile.include.in b/Makefile.include.in
index 471933991c..510a6e0a12 100644
--- a/Makefile.include.in
+++ b/Makefile.include.in
@@ -1,5 +1,5 @@
AR=@AR@
-ARFLAGS=crus
+ARFLAGS=crs
RM=rm -rf
SHELL=@SHELL@
ARCH=@ARCH@
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 318b89a663..8499b60804 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -11961,7 +11961,12 @@ msgctxt "#20470"
msgid "Use movie sets for single movies"
msgstr ""
-#empty strings from id 20471 to 21329
+#: system/settings/settings.xml
+msgctxt "#20471"
+msgid "Show empty TV shows"
+msgstr ""
+
+#empty strings from id 20472 to 21329
#up to 21329 is reserved for the video db !! !
#: system/settings/settings.xml
@@ -15844,7 +15849,11 @@ msgctxt "#36162"
msgid "Enable VideoToolbox hardware decoding of video files."
msgstr ""
-#empty string with id 36163
+#. Description of setting "Videos -> Library -> Show empty TV shows" with label #20471
+#: system/settings/settings.xml
+msgctxt "#36163"
+msgid "Show TV shows with no episodes when browsing the video library."
+msgstr ""
#. Description of setting "Videos -> Playback -> Adjust display refresh rate" with label #170
#: system/settings/settings.xml
diff --git a/addons/skin.confluence/720p/DialogExtendedProgressBar.xml b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml
index 6b1d7b4070..c6ed2bba3e 100644
--- a/addons/skin.confluence/720p/DialogExtendedProgressBar.xml
+++ b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml
@@ -5,7 +5,7 @@
<animation effect="slide" start="0,0" end="0,-70" delay="300" time="75">WindowClose</animation>
<controls>
<control type="group">
- <depth>DepthDialog+</depth>
+ <depth>DepthHeader</depth>
<left>720</left>
<top>0</top>
<animation effect="slide" end="0,-80" time="150" condition="Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)">conditional</animation>
diff --git a/addons/skin.confluence/720p/DialogVolumeBar.xml b/addons/skin.confluence/720p/DialogVolumeBar.xml
index 30f38b9cb5..6104c0d029 100644
--- a/addons/skin.confluence/720p/DialogVolumeBar.xml
+++ b/addons/skin.confluence/720p/DialogVolumeBar.xml
@@ -5,7 +5,7 @@
<animation effect="slide" start="0,0" end="0,-40" delay="300" time="75">WindowClose</animation>
<controls>
<control type="group">
- <depth>DepthMax</depth>
+ <depth>DepthOSD</depth>
<left>820</left>
<top>0</top>
<control type="image">
diff --git a/addons/skin.confluence/720p/MyPVRChannels.xml b/addons/skin.confluence/720p/MyPVRChannels.xml
index fa3c4e6030..d4106801e9 100644
--- a/addons/skin.confluence/720p/MyPVRChannels.xml
+++ b/addons/skin.confluence/720p/MyPVRChannels.xml
@@ -82,6 +82,19 @@
<height>400</height>
<texture border="5">button-nofocus.png</texture>
</control>
+ <control type="button" id="61">
+ <left>-1</left>
+ <top>-1</top>
+ <width>692</width>
+ <height>402</height>
+ <texturefocus border="3">VideoWindowFO.png</texturefocus>
+ <texturenofocus>-</texturenofocus>
+ <font>-</font>
+ <pulseonselect>no</pulseonselect>
+ <onleft>70</onleft>
+ <onclick>Fullscreen</onclick>
+ <visible>Player.HasVideo</visible>
+ </control>
<control type="image">
<left>5</left>
<top>5</top>
diff --git a/addons/skin.confluence/720p/VideoOSD.xml b/addons/skin.confluence/720p/VideoOSD.xml
index c6733dc0b3..83c505b5ee 100644
--- a/addons/skin.confluence/720p/VideoOSD.xml
+++ b/addons/skin.confluence/720p/VideoOSD.xml
@@ -177,7 +177,7 @@
<visible>VideoPlayer.HasMenu + !VideoPlayer.Content(LiveTV)</visible>
</control>
<control type="image" id="2300">
- <width>165</width>
+ <width>160</width>
<texture>-</texture>
<visible>VideoPlayer.Content(LiveTV)</visible>
</control>
@@ -294,6 +294,7 @@
<animation effect="slide" start="0,0" end="0,80" time="0" condition="![VideoPlayer.HasSubtitles + VideoPlayer.SubtitlesEnabled]">Conditional</animation>
<animation effect="slide" start="0,0" end="0,40" time="0" condition="!VideoPlayer.HasSubtitles">Conditional</animation>
<animation effect="slide" start="0,0" end="55,0" time="0" condition="![VideoPlayer.HasMenu | VideoPlayer.Content(LiveTV)]">Conditional</animation>
+ <animation effect="slide" start="0,0" end="55,0" time="0" condition="!VideoPlayer.HasMenu + VideoPlayer.Content(LiveTV)">Conditional</animation>
<right>145</right>
<bottom>45</bottom>
<width>256</width>
@@ -413,6 +414,7 @@
<depth>DepthOSD+</depth>
<visible>videoplayer.isstereoscopic + ![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(OSDAudioDSPSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)] + [Control.HasFocus(255) | ControlGroup(500).HasFocus | Control.HasFocus(520)]</visible>
<animation effect="fade" time="150">VisibleChange</animation>
+ <animation effect="slide" start="0,0" end="55,0" time="0" condition="![VideoPlayer.HasMenu | VideoPlayer.Content(LiveTV)]">Conditional</animation>
<animation effect="slide" start="0,0" end="55,0" time="0" condition="VideoPlayer.Content(LiveTV)">Conditional</animation>
<right>200</right>
<bottom>45</bottom>
diff --git a/addons/skin.confluence/720p/ViewsVideoLibrary.xml b/addons/skin.confluence/720p/ViewsVideoLibrary.xml
index b3259d4ccd..9609cdf7c0 100644
--- a/addons/skin.confluence/720p/ViewsVideoLibrary.xml
+++ b/addons/skin.confluence/720p/ViewsVideoLibrary.xml
@@ -1767,6 +1767,7 @@
<include>VideoTypeHackFlaggingConditions</include>
</control>
<control type="image">
+ <depth>DepthContent+</depth>
<left>560</left>
<top>140</top>
<width>180</width>
@@ -1816,6 +1817,7 @@
<autoscroll time="2000" delay="3000" repeat="5000">Skin.HasSetting(AutoScroll)</autoscroll>
</control>
<control type="image">
+ <depth>DepthContent+</depth>
<left>560</left>
<top>140</top>
<width>180</width>
@@ -1865,6 +1867,7 @@
<autoscroll time="2000" delay="3000" repeat="5000">Skin.HasSetting(AutoScroll)</autoscroll>
</control>
<control type="image">
+ <depth>DepthContent+</depth>
<left>560</left>
<top>140</top>
<width>180</width>
@@ -1929,6 +1932,7 @@
<include>VideoTypeHackFlaggingConditions</include>
</control>
<control type="image">
+ <depth>DepthContent+</depth>
<left>420</left>
<top>140</top>
<width>330</width>
diff --git a/addons/skin.confluence/720p/script-cu-lrclyrics-main.xml b/addons/skin.confluence/720p/script-cu-lrclyrics-main.xml
index 682826911c..06f29d3ec7 100644
--- a/addons/skin.confluence/720p/script-cu-lrclyrics-main.xml
+++ b/addons/skin.confluence/720p/script-cu-lrclyrics-main.xml
@@ -7,7 +7,7 @@
<include>dialogeffect</include>
<controls>
<control type="group">
- <depth>DepthOSDPopout</depth>
+ <depth>DepthSideBlade</depth>
<control type="image">
<description>background image</description>
<left>0</left>
diff --git a/addons/skin.confluence/720p/script-globalsearch-main.xml b/addons/skin.confluence/720p/script-globalsearch-main.xml
index 9809708348..d93c7eee19 100644
--- a/addons/skin.confluence/720p/script-globalsearch-main.xml
+++ b/addons/skin.confluence/720p/script-globalsearch-main.xml
@@ -4,6 +4,7 @@
<left>0</left>
<top>0</top>
</coordinates>
+ <depth>DepthDialog</depth>
<controls>
<control type="group" id="100">
<include>VisibleFadeEffect</include>
diff --git a/addons/skin.confluence/media/VideoWindowFO.png b/addons/skin.confluence/media/VideoWindowFO.png
new file mode 100644
index 0000000000..a04571dab8
--- /dev/null
+++ b/addons/skin.confluence/media/VideoWindowFO.png
Binary files differ
diff --git a/addons/skin.re-touched b/addons/skin.re-touched
-Subproject db5f915d02c315d5d23f7de8173f9ec0646cf15
+Subproject b0f5ec5f3ca9725f500236fad70d4939087b64e
diff --git a/docs/README.android b/docs/README.android
index 9c5cab683f..752ecd7924 100644
--- a/docs/README.android
+++ b/docs/README.android
@@ -13,7 +13,7 @@ TOC
1. Introduction
-----------------------------------------------------------------------------
-We currently recommend Ubuntu "Precise Pangolin" (12.04) 64Bit. This is what our continuous
+We currently recommend Ubuntu "Trusty Tahr" (14.04) 64Bit. This is what our continuous
integration system "jenkins" is using.
Additionally, building from OSX Snow Leopard or Mavericks (others on your own risk ;) ) is working (see 3.1).
diff --git a/system/keyboardlayouts/czech.xml b/system/keyboardlayouts/czech.xml
new file mode 100644
index 0000000000..4c31abd631
--- /dev/null
+++ b/system/keyboardlayouts/czech.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Please use English language names instead.
+Default font lacks support for all characters
+-->
+<keyboardlayouts>
+ <layout language="Czech" layout="QWERTZ">
+ <keyboard>
+ <row>ťěščřžýáíéó(</row>
+ <row>qwertzuiopú)</row>
+ <row>asdfghjklůď_</row>
+ <row>/yxcvbnmň:.-</row>
+ </keyboard>
+ <keyboard modifiers="shift">
+ <row>ŤĚŠČŘŽÝÁÍÉÓ(</row>
+ <row>QWERTZUIOPÚ)</row>
+ <row>ASDFGHJKLŮĎ_</row>
+ <row>\YXCVBNMŇ:.-</row>
+ </keyboard>
+ <keyboard modifiers="symbol,shift+symbol">
+ <row>1234567890%</row>
+ <row>+@#$~^&amp;*{}=</row>
+ <row>[]()/"'`;!</row>
+ <row>\|&lt;&gt;,.?:-_</row>
+ </keyboard>
+ </layout>
+</keyboardlayouts>
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index af504a8ab4..5ac282c200 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -481,6 +481,11 @@
<default>false</default>
<control type="toggle" />
</setting>
+ <setting id="videolibrary.showemptytvshows" type="boolean" label="20471" help="36163">
+ <level>1</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
</group>
<group id="2">
<setting id="videolibrary.updateonstartup" type="boolean" label="22000" help="36146">
diff --git a/tools/android/packaging/xbmc/AndroidManifest.xml.in b/tools/android/packaging/xbmc/AndroidManifest.xml.in
index c10630b99c..924fe1fbd1 100644
--- a/tools/android/packaging/xbmc/AndroidManifest.xml.in
+++ b/tools/android/packaging/xbmc/AndroidManifest.xml.in
@@ -82,6 +82,18 @@
android:name="android.app.lib_name"
android:value="@APP_NAME_LC@" />
</activity>
+
+ <receiver android:name=".XBMCBroadcastReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.BATTERY_CHANGED" />
+ <action android:name="android.intent.action.DREAMING_STOPPED" />
+ <action android:name="android.intent.action.SCREEN_ON" />
+ <action android:name="android.intent.action.HEADSET_PLUG" />
+ <action android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </receiver>
+
</application>
</manifest><!-- END_INCLUDE(manifest) -->
diff --git a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
index 8b1848396a..d2399b43c9 100644
--- a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
+++ b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
@@ -1,6 +1,7 @@
package org.xbmc.@APP_NAME_LC@;
import android.app.NativeActivity;
+import android.content.ComponentName;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
@@ -61,6 +62,20 @@ public class Main extends NativeActivity
}
});
}
+
+ public void registerMediaButtonEventReceiver()
+ {
+ Log.d(TAG, "registerMediaButtonEventReceiver");
+ AudioManager manager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ manager.registerMediaButtonEventReceiver(new ComponentName(getPackageName(), XBMCBroadcastReceiver.class.getName()));
+ }
+
+ public void unregisterMediaButtonEventReceiver()
+ {
+ Log.d(TAG, "unregisterMediaButtonEventReceiver");
+ AudioManager manager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ manager.unregisterMediaButtonEventReceiver(new ComponentName(getPackageName(), XBMCBroadcastReceiver.class.getName()));
+ }
@Override
public void onCreate(Bundle savedInstanceState)
diff --git a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java.in b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java.in
index 6dc74d4070..47282b4c61 100644
--- a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java.in
+++ b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Splash.java.in
@@ -683,15 +683,23 @@ public class Splash extends Activity {
mStateMachine.sendEmptyMessage(Checking);
String curArch = "";
- try {
+ String curArch2 = "";
+ try
+ {
curArch = Build.CPU_ABI.substring(0,3);
- } catch (IndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e)
+ {
mErrorMsg = "Error! Unexpected architecture: " + Build.CPU_ABI;
Log.e(TAG, mErrorMsg);
mState = InError;
- }
+ }
+ try
+ {
+ curArch2 = Build.CPU_ABI2.substring(0,3);
+ } catch (IndexOutOfBoundsException e) {}
- if (mState != InError) {
+ if (mState != InError)
+ {
// Check if we are on the proper arch
// Read the properties
@@ -701,8 +709,10 @@ public class Splash extends Activity {
Properties properties = new Properties();
properties.load(xbmcprop);
- if (!curArch.equalsIgnoreCase(properties.getProperty("native_arch"))) {
- mErrorMsg = "This @APP_NAME@ package is not compatible with your device (" + curArch + " vs. " + properties.getProperty("native_arch") +").\nPlease check the <a href=\"http://wiki.kodi.tv/index.php?title=XBMC_for_Android_specific_FAQ\">Kodi Android wiki</a> for more information.";
+ if (!curArch.equalsIgnoreCase(properties.getProperty("native_arch"))
+ && !curArch2.equalsIgnoreCase(properties.getProperty("native_arch")))
+ {
+ mErrorMsg = "This @APP_NAME@ package is not compatible with your device (device=" + curArch + " vs. package=" + properties.getProperty("native_arch") +").\nPlease check the <a href=\"http://wiki.kodi.tv/index.php?title=XBMC_for_Android_specific_FAQ\">Kodi Android wiki</a> for more information.";
Log.e(TAG, mErrorMsg);
mState = InError;
}
diff --git a/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java.in b/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java.in
index 432a7cdb01..2980bf5190 100644
--- a/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java.in
+++ b/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCVideoView.java.in
@@ -46,6 +46,9 @@ public class XBMCVideoView extends SurfaceView implements
*/
public void clearSurface()
{
+ if (!mHasHolder)
+ return;
+
// Have to go EGL to allow reuse of surface
final int EGL_OPENGL_ES2_BIT = 4;
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 191071d570..f2abf2a916 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -1581,15 +1581,9 @@ bool CApplication::Load(const TiXmlNode *settings)
const TiXmlElement *audioElement = settings->FirstChildElement("audio");
if (audioElement != NULL)
{
-#ifndef TARGET_ANDROID
XMLUtils::GetBoolean(audioElement, "mute", m_muted);
if (!XMLUtils::GetFloat(audioElement, "fvolumelevel", m_volumeLevel, VOLUME_MINIMUM, VOLUME_MAXIMUM))
m_volumeLevel = VOLUME_MAXIMUM;
-#else
- // Use system volume settings
- m_volumeLevel = CXBMCApp::GetSystemVolume();
- m_muted = (m_volumeLevel == 0);
-#endif
}
return true;
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index 41552c0507..2df79cf8f5 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -4548,7 +4548,7 @@ int CGUIInfoManager::GetPlayTimeRemaining() const
float CGUIInfoManager::GetSeekPercent() const
{
- if (g_infoManager.GetTotalPlayTime() == 0)
+ if (GetTotalPlayTime() == 0)
return 0.0f;
float percentPlayTime = static_cast<float>(GetPlayTime()) / GetTotalPlayTime() * 0.1f;
@@ -5224,8 +5224,10 @@ std::string CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, std::
case LISTITEM_USER_RATING:
{
std::string strUserRating;
- if (item->GetVideoInfoTag()->m_iUserRating > 0)
+ if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iUserRating > 0)
strUserRating = StringUtils::Format("%i", item->GetVideoInfoTag()->m_iUserRating);
+ else if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetUserrating() > '0')
+ strUserRating.assign(1, item->GetMusicInfoTag()->GetUserrating());
return strUserRating;
}
break;
@@ -5894,9 +5896,9 @@ bool CGUIInfoManager::GetItemBool(const CGUIListItem *item, int condition) const
{
CEpgInfoTagPtr epgTag = pItem->GetEPGInfoTag();
- // Check if the tag has a currently active recording associated
- if (epgTag->HasRecording() && epgTag->IsActive())
- return true;
+ // Check if the tag has a currently recording timer associated
+ if (epgTag->HasTimer())
+ return epgTag->Timer()->IsRecording();
// Search all timers for something that matches the tag
CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(pItem);
diff --git a/xbmc/android/activity/AndroidKey.h b/xbmc/android/activity/AndroidKey.h
index 7ded4e63d2..81f056c25c 100644
--- a/xbmc/android/activity/AndroidKey.h
+++ b/xbmc/android/activity/AndroidKey.h
@@ -32,6 +32,6 @@ public:
~CAndroidKey() {};
bool onKeyboardEvent(AInputEvent *event);
- void XBMC_Key(uint8_t code, uint16_t key, uint16_t modifiers, uint16_t unicode, bool up);
- void XBMC_JoyButton(uint8_t id, uint8_t button, bool up);
+
+ static void XBMC_Key(uint8_t code, uint16_t key, uint16_t modifiers, uint16_t unicode, bool up);
};
diff --git a/xbmc/android/activity/JNIMainActivity.cpp b/xbmc/android/activity/JNIMainActivity.cpp
index 1074d0124b..3cdbdec0d3 100644
--- a/xbmc/android/activity/JNIMainActivity.cpp
+++ b/xbmc/android/activity/JNIMainActivity.cpp
@@ -93,3 +93,15 @@ void CJNIMainActivity::setVideoViewSurfaceRect(int l, int t, int r, int b)
call_method<void>(m_context,
"setVideoViewSurfaceRect", "(IIII)V", l, t, r, b);
}
+
+void CJNIMainActivity::registerMediaButtonEventReceiver()
+{
+ call_method<void>(m_context,
+ "registerMediaButtonEventReceiver", "()V");
+}
+
+void CJNIMainActivity::unregisterMediaButtonEventReceiver()
+{
+ call_method<void>(m_context,
+ "unregisterMediaButtonEventReceiver", "()V");
+}
diff --git a/xbmc/android/activity/JNIMainActivity.h b/xbmc/android/activity/JNIMainActivity.h
index 300ea81af7..c03ca86461 100644
--- a/xbmc/android/activity/JNIMainActivity.h
+++ b/xbmc/android/activity/JNIMainActivity.h
@@ -36,6 +36,8 @@ public:
static void _callNative(JNIEnv *env, jobject context, jlong funcAddr, jlong variantAddr);
static void runNativeOnUiThread(void (*callback)(CVariant *), CVariant *variant);
+ static void registerMediaButtonEventReceiver();
+ static void unregisterMediaButtonEventReceiver();
CJNISurface getVideoViewSurface();
void clearVideoView();
diff --git a/xbmc/android/activity/XBMCApp.cpp b/xbmc/android/activity/XBMCApp.cpp
index 638f4f8c71..f8021ff8b8 100644
--- a/xbmc/android/activity/XBMCApp.cpp
+++ b/xbmc/android/activity/XBMCApp.cpp
@@ -65,8 +65,6 @@
#include "android/jni/System.h"
#include "android/jni/ApplicationInfo.h"
#include "android/jni/StatFs.h"
-#include "android/jni/BitmapDrawable.h"
-#include "android/jni/Bitmap.h"
#include "android/jni/CharSequence.h"
#include "android/jni/URI.h"
#include "android/jni/Cursor.h"
@@ -78,6 +76,8 @@
#endif
#include "android/jni/Window.h"
#include "android/jni/WindowManager.h"
+#include "android/jni/KeyEvent.h"
+#include "AndroidKey.h"
#include "CompileInfo.h"
@@ -156,13 +156,6 @@ void CXBMCApp::onStart()
void CXBMCApp::onResume()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
- CJNIIntentFilter intentFilter;
- intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
- intentFilter.addAction("android.intent.action.DREAMING_STOPPED");
- intentFilter.addAction("android.intent.action.SCREEN_ON");
- intentFilter.addAction("android.intent.action.HEADSET_PLUG");
- intentFilter.addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED");
- registerReceiver(*this, intentFilter);
if (!g_application.IsInScreenSaver())
EnableWakeLock(true);
@@ -172,6 +165,8 @@ void CXBMCApp::onResume()
CJNIAudioManager audioManager(getSystemService("audio"));
m_headsetPlugged = audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn();
+ unregisterMediaButtonEventReceiver();
+
// Clear the applications cache. We could have installed/deinstalled apps
{
CSingleLock lock(m_applicationsMutex);
@@ -182,8 +177,13 @@ void CXBMCApp::onResume()
void CXBMCApp::onPause()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
-
- unregisterReceiver(*this);
+ if (g_application.m_pPlayer->IsPlaying())
+ {
+ if (g_application.m_pPlayer->IsPlayingVideo())
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_STOP)));
+ else
+ registerMediaButtonEventReceiver();
+ }
#if defined(HAS_LIBAMCODEC)
if (aml_permissions())
@@ -254,7 +254,7 @@ void CXBMCApp::onCreateWindow(ANativeWindow* window)
void CXBMCApp::onResizeWindow()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
- m_window=NULL;
+ m_window = NULL;
m_windowCreated.Reset();
// no need to do anything because we are fixed in fullscreen landscape mode
}
@@ -267,6 +267,7 @@ void CXBMCApp::onDestroyWindow()
if (!m_exiting)
{
XBMC_DestroyDisplay();
+ m_window = NULL;
XBMC_Pause(true);
}
}
@@ -412,9 +413,6 @@ void CXBMCApp::run()
void CXBMCApp::XBMC_Pause(bool pause)
{
android_printf("XBMC_Pause(%s)", pause ? "true" : "false");
- // Only send the PAUSE action if we are pausing XBMC and video is currently playing
- if (pause && g_application.m_pPlayer->IsPlayingVideo() && !g_application.m_pPlayer->IsPaused())
- CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PAUSE)));
}
void CXBMCApp::XBMC_Stop()
@@ -531,16 +529,17 @@ std::vector<androidPackage> CXBMCApp::GetApplications()
CJNIList<CJNIApplicationInfo> packageList = GetPackageManager().getInstalledApplications(CJNIPackageManager::GET_ACTIVITIES);
int numPackages = packageList.size();
for (int i = 0; i < numPackages; i++)
- {
- androidPackage newPackage;
- newPackage.packageName = packageList.get(i).packageName;
- newPackage.packageLabel = GetPackageManager().getApplicationLabel(packageList.get(i)).toString();
- CJNIIntent intent = GetPackageManager().getLaunchIntentForPackage(newPackage.packageName);
+ {
+ CJNIIntent intent = GetPackageManager().getLaunchIntentForPackage(packageList.get(i).packageName);
if (!intent && CJNIBuild::SDK_INT >= 21)
- intent = GetPackageManager().getLeanbackLaunchIntentForPackage(newPackage.packageName);
+ intent = GetPackageManager().getLeanbackLaunchIntentForPackage(packageList.get(i).packageName);
if (!intent)
continue;
+ androidPackage newPackage;
+ newPackage.packageName = packageList.get(i).packageName;
+ newPackage.packageLabel = GetPackageManager().getApplicationLabel(packageList.get(i)).toString();
+ newPackage.icon = packageList.get(i).icon;
m_applications.push_back(newPackage);
}
}
@@ -548,34 +547,6 @@ std::vector<androidPackage> CXBMCApp::GetApplications()
return m_applications;
}
-bool CXBMCApp::GetIconSize(const string &packageName, int *width, int *height)
-{
- JNIEnv* env = xbmc_jnienv();
- AndroidBitmapInfo info;
- CJNIBitmapDrawable drawable = (CJNIBitmapDrawable)GetPackageManager().getApplicationIcon(packageName);
- CJNIBitmap icon(drawable.getBitmap());
- AndroidBitmap_getInfo(env, icon.get_raw(), &info);
- *width = info.width;
- *height = info.height;
- return true;
-}
-
-bool CXBMCApp::GetIcon(const string &packageName, void* buffer, unsigned int bufSize)
-{
- void *bitmapBuf = NULL;
- JNIEnv* env = xbmc_jnienv();
- CJNIBitmapDrawable drawable = (CJNIBitmapDrawable)GetPackageManager().getApplicationIcon(packageName);
- CJNIBitmap bitmap(drawable.getBitmap());
- AndroidBitmap_lockPixels(env, bitmap.get_raw(), &bitmapBuf);
- if (bitmapBuf)
- {
- memcpy(buffer, bitmapBuf, bufSize);
- AndroidBitmap_unlockPixels(env, bitmap.get_raw());
- return true;
- }
- return false;
-}
-
bool CXBMCApp::HasLaunchIntent(const string &package)
{
return GetPackageManager().getLaunchIntentForPackage(package) != NULL;
@@ -769,6 +740,35 @@ void CXBMCApp::onReceive(CJNIIntent intent)
CAEFactory::DeviceChange();
}
}
+ else if (action == "android.intent.action.MEDIA_BUTTON")
+ {
+ CJNIKeyEvent keyevt = (CJNIKeyEvent)intent.getParcelableExtra(CJNIIntent::EXTRA_KEY_EVENT);
+
+ int keycode = keyevt.getKeyCode();
+ bool up = (keyevt.getAction() == CJNIKeyEvent::ACTION_UP);
+
+ CLog::Log(LOGINFO, "Got MEDIA_BUTTON intent: %d, up:%s", keycode, up ? "true" : "false");
+ if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_RECORD)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_RECORD, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_EJECT)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_EJECT, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_FAST_FORWARD)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_FASTFORWARD, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_NEXT)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_NEXT_TRACK, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PAUSE)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PLAY)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PLAY_PAUSE)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PREVIOUS)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PREV_TRACK, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_REWIND)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_REWIND, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_STOP)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_STOP, 0, 0, up);
+ }
}
void CXBMCApp::onNewIntent(CJNIIntent intent)
@@ -783,16 +783,22 @@ void CXBMCApp::onNewIntent(CJNIIntent intent)
void CXBMCApp::onVolumeChanged(int volume)
{
- CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(
- new CAction(ACTION_VOLUME_SET, static_cast<float>(volume))));
+ // System volume was used; Reset Kodi volume to 100% if it'not, already
+ if (g_application.GetVolume(false) != 1.0)
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(
+ new CAction(ACTION_VOLUME_SET, static_cast<float>(CXBMCApp::GetMaxSystemVolume()))));
}
void CXBMCApp::onAudioFocusChange(int focusChange)
{
CXBMCApp::android_printf("Audio Focus changed: %d", focusChange);
- if (focusChange == CJNIAudioManager::AUDIOFOCUS_LOSS && g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
- CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(
- new CAction(ACTION_PAUSE)));
+ if (focusChange == CJNIAudioManager::AUDIOFOCUS_LOSS)
+ {
+ unregisterMediaButtonEventReceiver();
+
+ if (g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PAUSE)));
+ }
}
void CXBMCApp::SetupEnv()
diff --git a/xbmc/android/activity/XBMCApp.h b/xbmc/android/activity/XBMCApp.h
index 8d4e213c1b..36240fe79c 100644
--- a/xbmc/android/activity/XBMCApp.h
+++ b/xbmc/android/activity/XBMCApp.h
@@ -48,12 +48,13 @@ struct androidIcon
unsigned int width;
unsigned int height;
void *pixels;
-};
+};
struct androidPackage
{
std::string packageName;
std::string packageLabel;
+ int icon;
};
class CXBMCApp : public IActivityHandler, public CJNIMainActivity, public CJNIBroadcastReceiver, public CJNIAudioManagerAudioFocusChangeListener
@@ -96,8 +97,6 @@ public:
static bool StartActivity(const std::string &package, const std::string &intent = std::string(), const std::string &dataType = std::string(), const std::string &dataURI = std::string());
static std::vector <androidPackage> GetApplications();
- static bool GetIconSize(const std::string &packageName, int *width, int *height);
- static bool GetIcon(const std::string &packageName, void* buffer, unsigned int bufSize);
/*!
* \brief If external storage is available, it returns the path for the external storage (for the specified type)
diff --git a/xbmc/android/jni/ApplicationInfo.cpp b/xbmc/android/jni/ApplicationInfo.cpp
index da7ec9d3f6..4f97940b53 100644
--- a/xbmc/android/jni/ApplicationInfo.cpp
+++ b/xbmc/android/jni/ApplicationInfo.cpp
@@ -23,7 +23,7 @@
using namespace jni;
-CJNIApplicationInfo::CJNIApplicationInfo(const jhobject &object) : CJNIBase(object)
+CJNIApplicationInfo::CJNIApplicationInfo(const jhobject &object) : CJNIPackageItemInfo(object)
,sourceDir( jcast<std::string>(get_field<jhstring>(m_object, "sourceDir")))
,publicSourceDir( jcast<std::string>(get_field<jhstring>(m_object, "publicSourceDir")))
,dataDir( jcast<std::string>(get_field<jhstring>(m_object, "dataDir")))
diff --git a/xbmc/android/jni/ApplicationInfo.h b/xbmc/android/jni/ApplicationInfo.h
index 0c4678d758..edf6ccfdde 100644
--- a/xbmc/android/jni/ApplicationInfo.h
+++ b/xbmc/android/jni/ApplicationInfo.h
@@ -20,8 +20,9 @@
*/
#include "JNIBase.h"
+#include "PackageItemInfo.h"
-class CJNIApplicationInfo : public CJNIBase
+class CJNIApplicationInfo : public CJNIPackageItemInfo
{
public:
CJNIApplicationInfo(const jni::jhobject &object);
diff --git a/xbmc/android/jni/Context.cpp b/xbmc/android/jni/Context.cpp
index c45686b59a..8b9b5d79f6 100644
--- a/xbmc/android/jni/Context.cpp
+++ b/xbmc/android/jni/Context.cpp
@@ -45,6 +45,9 @@
#include "Window.h"
#include "View.h"
#include "Build.h"
+#include "DisplayMetrics.h"
+#include "Intent.h"
+#include "KeyEvent.h"
#include <android/native_activity.h>
@@ -84,6 +87,9 @@ void CJNIContext::PopulateStaticFields()
CJNIMediaFormat::PopulateStaticFields();
CJNIView::PopulateStaticFields();
CJNIBuild::PopulateStaticFields();
+ CJNIDisplayMetrics::PopulateStaticFields();
+ CJNIIntent::PopulateStaticFields();
+ CJNIKeyEvent::PopulateStaticFields();
}
CJNIPackageManager CJNIContext::GetPackageManager()
diff --git a/xbmc/android/jni/DisplayMetrics.cpp b/xbmc/android/jni/DisplayMetrics.cpp
new file mode 100644
index 0000000000..0a2c39685d
--- /dev/null
+++ b/xbmc/android/jni/DisplayMetrics.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "DisplayMetrics.h"
+#include "jutils/jutils-details.hpp"
+
+using namespace jni;
+const char *CJNIDisplayMetrics::m_classname = "android/util/DisplayMetrics";
+
+int CJNIDisplayMetrics::DENSITY_DEFAULT(-1);
+int CJNIDisplayMetrics::DENSITY_HIGH(-1);
+int CJNIDisplayMetrics::DENSITY_LOW(-1);
+int CJNIDisplayMetrics::DENSITY_MEDIUM(-1);
+int CJNIDisplayMetrics::DENSITY_TV(-1);
+int CJNIDisplayMetrics::DENSITY_XHIGH(-1);
+int CJNIDisplayMetrics::DENSITY_XXHIGH(-1);
+int CJNIDisplayMetrics::DENSITY_XXXHIGH(-1);
+
+void CJNIDisplayMetrics::PopulateStaticFields()
+{
+ jhclass clazz = find_class(m_classname);
+
+ DENSITY_DEFAULT = get_static_field<jint>(clazz, "DENSITY_DEFAULT");
+ DENSITY_HIGH = get_static_field<jint>(clazz, "DENSITY_HIGH");
+ DENSITY_LOW = get_static_field<jint>(clazz, "DENSITY_LOW");
+ DENSITY_MEDIUM = get_static_field<jint>(clazz, "DENSITY_MEDIUM");
+ DENSITY_TV = get_static_field<jint>(clazz, "DENSITY_TV");
+ DENSITY_XHIGH = get_static_field<jint>(clazz, "DENSITY_XHIGH");
+ if(CJNIBase::GetSDKVersion() >= 16)
+ DENSITY_XXHIGH = get_static_field<jint>(clazz, "DENSITY_XXHIGH");
+ if(CJNIBase::GetSDKVersion() >= 18)
+ DENSITY_XXXHIGH = get_static_field<jint>(clazz, "DENSITY_XXXHIGH");
+}
diff --git a/xbmc/android/jni/DisplayMetrics.h b/xbmc/android/jni/DisplayMetrics.h
new file mode 100644
index 0000000000..578ef54b46
--- /dev/null
+++ b/xbmc/android/jni/DisplayMetrics.h
@@ -0,0 +1,42 @@
+#pragma once
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JNIBase.h"
+
+class CJNIDisplayMetrics
+{
+public:
+ static int DENSITY_DEFAULT;
+ static int DENSITY_HIGH;
+ static int DENSITY_LOW;
+ static int DENSITY_MEDIUM;
+ static int DENSITY_TV;
+ static int DENSITY_XHIGH;
+ static int DENSITY_XXHIGH;
+ static int DENSITY_XXXHIGH;
+
+ static void PopulateStaticFields();
+
+private:
+ CJNIDisplayMetrics();
+ ~CJNIDisplayMetrics() {};
+ static const char *m_classname;
+};
diff --git a/xbmc/android/jni/Intent.cpp b/xbmc/android/jni/Intent.cpp
index eb7eeec8d2..264a629d8c 100644
--- a/xbmc/android/jni/Intent.cpp
+++ b/xbmc/android/jni/Intent.cpp
@@ -24,6 +24,14 @@
using namespace jni;
+std::string CJNIIntent::EXTRA_KEY_EVENT;
+
+void CJNIIntent::PopulateStaticFields()
+{
+ jhclass clazz = find_class("android/content/Intent");
+ EXTRA_KEY_EVENT = jcast<std::string>(get_static_field<jhstring>(clazz,"EXTRA_KEY_EVENT"));
+}
+
CJNIIntent::CJNIIntent(const std::string &action) : CJNIBase("android/content/Intent")
{
if(action.empty())
@@ -72,6 +80,13 @@ std::string CJNIIntent::getStringExtra(const std::string &name) const
jcast<jhstring>(name)));
}
+jni::jhobject CJNIIntent::getParcelableExtra(const std::string &name) const
+{
+ return call_method<jhobject>(m_object,
+ "getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;",
+ jcast<jhstring>(name));
+}
+
bool CJNIIntent::hasExtra(const std::string &name) const
{
return call_method<jboolean>(m_object,
diff --git a/xbmc/android/jni/Intent.h b/xbmc/android/jni/Intent.h
index c187642c88..6855fba284 100644
--- a/xbmc/android/jni/Intent.h
+++ b/xbmc/android/jni/Intent.h
@@ -36,6 +36,7 @@ public:
int getIntExtra(const std::string &name, int defaultValue) const;
std::string getStringExtra(const std::string &name) const;
+ jni::jhobject getParcelableExtra(const std::string &name) const;
bool hasExtra(const std::string &name) const;
bool hasCategory(const std::string &category) const;
@@ -53,4 +54,7 @@ public:
void setPackage(const std::string &packageName);
void setType(const std::string &type);
CJNIURI getData() const;
+
+ static void PopulateStaticFields();
+ static std::string EXTRA_KEY_EVENT;
};
diff --git a/xbmc/android/jni/KeyEvent.cpp b/xbmc/android/jni/KeyEvent.cpp
new file mode 100644
index 0000000000..55c38c824d
--- /dev/null
+++ b/xbmc/android/jni/KeyEvent.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "KeyEvent.h"
+
+#include "jutils/jutils-details.hpp"
+
+using namespace jni;
+
+int CJNIKeyEvent::ACTION_DOWN;
+int CJNIKeyEvent::ACTION_UP;
+
+int CJNIKeyEvent::KEYCODE_MEDIA_RECORD;
+int CJNIKeyEvent::KEYCODE_MEDIA_EJECT;
+int CJNIKeyEvent::KEYCODE_MEDIA_FAST_FORWARD;
+int CJNIKeyEvent::KEYCODE_MEDIA_NEXT ;
+int CJNIKeyEvent::KEYCODE_MEDIA_PAUSE;
+int CJNIKeyEvent::KEYCODE_MEDIA_PLAY;
+int CJNIKeyEvent::KEYCODE_MEDIA_PLAY_PAUSE;
+int CJNIKeyEvent::KEYCODE_MEDIA_PREVIOUS;
+int CJNIKeyEvent::KEYCODE_MEDIA_REWIND;
+int CJNIKeyEvent::KEYCODE_MEDIA_STOP;
+
+void CJNIKeyEvent::PopulateStaticFields()
+{
+ jhclass clazz = find_class("android/view/KeyEvent");
+ ACTION_DOWN = (get_static_field<int>(clazz, "ACTION_DOWN"));
+ ACTION_UP = (get_static_field<int>(clazz, "ACTION_UP"));
+ KEYCODE_MEDIA_RECORD = (get_static_field<int>(clazz, "KEYCODE_MEDIA_RECORD"));
+ KEYCODE_MEDIA_EJECT = (get_static_field<int>(clazz, "KEYCODE_MEDIA_EJECT"));
+ KEYCODE_MEDIA_FAST_FORWARD = (get_static_field<int>(clazz, "KEYCODE_MEDIA_FAST_FORWARD"));
+ KEYCODE_MEDIA_NEXT = (get_static_field<int>(clazz, "KEYCODE_MEDIA_NEXT"));
+ KEYCODE_MEDIA_PAUSE = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PAUSE"));
+ KEYCODE_MEDIA_PLAY = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PLAY"));
+ KEYCODE_MEDIA_PLAY_PAUSE = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PLAY_PAUSE"));
+ KEYCODE_MEDIA_PREVIOUS = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PREVIOUS"));
+ KEYCODE_MEDIA_REWIND = (get_static_field<int>(clazz, "KEYCODE_MEDIA_REWIND"));
+ KEYCODE_MEDIA_STOP = (get_static_field<int>(clazz, "KEYCODE_MEDIA_STOP"));
+}
+
+int CJNIKeyEvent::getKeyCode()
+{
+ return call_method<jint>(m_object,
+ "getKeyCode", "()I");
+}
+
+int CJNIKeyEvent::getAction()
+{
+ return call_method<jint>(m_object,
+ "getAction", "()I");
+}
diff --git a/xbmc/android/jni/KeyEvent.h b/xbmc/android/jni/KeyEvent.h
new file mode 100644
index 0000000000..5ad8474610
--- /dev/null
+++ b/xbmc/android/jni/KeyEvent.h
@@ -0,0 +1,68 @@
+#pragma once
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JNIBase.h"
+
+class CJNIKeyEventAudioFocusChangeListener : public CJNIBase
+{
+public:
+ CJNIKeyEventAudioFocusChangeListener(const jni::jhobject &object) : CJNIBase(object) {};
+ virtual ~CJNIKeyEventAudioFocusChangeListener() {};
+
+ static void _onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange);
+
+protected:
+ CJNIKeyEventAudioFocusChangeListener();
+
+ virtual void onAudioFocusChange(int focusChange)=0;
+
+private:
+ static CJNIKeyEventAudioFocusChangeListener *m_listenerInstance;
+};
+
+class CJNIKeyEvent : public CJNIBase
+{
+public:
+ CJNIKeyEvent(const jni::jhobject &object) : CJNIBase(object) {};
+ ~CJNIKeyEvent() {};
+
+ int getKeyCode();
+ int getAction();
+
+ static void PopulateStaticFields();
+ static int ACTION_DOWN;
+ static int ACTION_UP;
+
+ static int KEYCODE_MEDIA_RECORD;
+ static int KEYCODE_MEDIA_EJECT;
+ static int KEYCODE_MEDIA_FAST_FORWARD;
+ static int KEYCODE_MEDIA_NEXT ;
+ static int KEYCODE_MEDIA_PAUSE;
+ static int KEYCODE_MEDIA_PLAY;
+ static int KEYCODE_MEDIA_PLAY_PAUSE;
+ static int KEYCODE_MEDIA_PREVIOUS;
+ static int KEYCODE_MEDIA_REWIND;
+ static int KEYCODE_MEDIA_STOP;
+
+private:
+ CJNIKeyEvent();
+};
+
diff --git a/xbmc/android/jni/Makefile.in b/xbmc/android/jni/Makefile.in
index 7e7a58431a..7e1eeff619 100644
--- a/xbmc/android/jni/Makefile.in
+++ b/xbmc/android/jni/Makefile.in
@@ -54,6 +54,10 @@ SRCS += Activity.cpp
SRCS += SystemProperties.cpp
SRCS += Display.cpp
SRCS += WindowManager.cpp
+SRCS += Resources.cpp
+SRCS += PackageItemInfo.cpp
+SRCS += DisplayMetrics.cpp
+SRCS += KeyEvent.cpp
LIB = jni.a
diff --git a/xbmc/android/jni/PackageItemInfo.cpp b/xbmc/android/jni/PackageItemInfo.cpp
new file mode 100644
index 0000000000..c0d4f2ccff
--- /dev/null
+++ b/xbmc/android/jni/PackageItemInfo.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PackageItemInfo.h"
+#include "jutils/jutils-details.hpp"
+
+using namespace jni;
+const char *CJNIPackageItemInfo::m_classname = "android/content/pm/PackageItemInfo";
+
+CJNIPackageItemInfo::CJNIPackageItemInfo(const jhobject &object) : CJNIBase(object)
+ ,icon( get_field<int>(m_object, "icon"))
+{
+}
diff --git a/xbmc/android/jni/PackageItemInfo.h b/xbmc/android/jni/PackageItemInfo.h
new file mode 100644
index 0000000000..2924ef006a
--- /dev/null
+++ b/xbmc/android/jni/PackageItemInfo.h
@@ -0,0 +1,36 @@
+#pragma once
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JNIBase.h"
+
+class CJNIPackageItemInfo : public CJNIBase
+{
+public:
+ CJNIPackageItemInfo(const jni::jhobject &object);
+
+ int icon;
+
+protected:
+ CJNIPackageItemInfo();
+ ~CJNIPackageItemInfo() {};
+ static const char *m_classname;
+
+};
diff --git a/xbmc/android/jni/PackageManager.cpp b/xbmc/android/jni/PackageManager.cpp
index ddec8a72e6..b9d510ef5c 100644
--- a/xbmc/android/jni/PackageManager.cpp
+++ b/xbmc/android/jni/PackageManager.cpp
@@ -24,6 +24,7 @@
#include "List.h"
#include "CharSequence.h"
#include "ApplicationInfo.h"
+#include "Resources.h"
#include "jutils/jutils-details.hpp"
using namespace jni;
@@ -71,3 +72,16 @@ CJNIList<CJNIApplicationInfo> CJNIPackageManager::getInstalledApplications(int f
flags);
}
+CJNIResources CJNIPackageManager::getResourcesForApplication(const std::string &package)
+{
+ return call_method<jhobject>(m_object,
+ "getResourcesForApplication", "(Ljava/lang/String;)Landroid/content/res/Resources;",
+ jcast<jhstring>(package));
+}
+
+CJNIResources CJNIPackageManager::getResourcesForApplication(const CJNIApplicationInfo &info)
+{
+ return call_method<jhobject>(m_object,
+ "getResourcesForApplication", "(Landroid/content/pm/ApplicationInfo;)Landroid/content/res/Resources;",
+ info.get_raw());
+}
diff --git a/xbmc/android/jni/PackageManager.h b/xbmc/android/jni/PackageManager.h
index 5477b84008..bf22c33120 100644
--- a/xbmc/android/jni/PackageManager.h
+++ b/xbmc/android/jni/PackageManager.h
@@ -26,6 +26,7 @@ class CJNIIntent;
class CJNIDrawable;
class CJNIApplicationInfo;
class CJNICharSequence;
+class CJNIResources;
class CJNIPackageManager : public CJNIBase
{
@@ -38,6 +39,8 @@ public:
CJNIDrawable getApplicationIcon(const std::string &package);
CJNIList<CJNIApplicationInfo> getInstalledApplications(int flags);
CJNICharSequence getApplicationLabel(const CJNIApplicationInfo &info);
+ CJNIResources getResourcesForApplication(const std::string &package);
+ CJNIResources getResourcesForApplication(const CJNIApplicationInfo &info);
static void PopulateStaticFields();
static int GET_ACTIVITIES;
diff --git a/xbmc/android/jni/Resources.cpp b/xbmc/android/jni/Resources.cpp
new file mode 100644
index 0000000000..55e2a49389
--- /dev/null
+++ b/xbmc/android/jni/Resources.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Resources.h"
+#include "Drawable.h"
+#include "jutils/jutils-details.hpp"
+
+using namespace jni;
+
+CJNIDrawable CJNIResources::getDrawableForDensity(int id, int density)
+{
+ return call_method<jhobject>(m_object,
+ "getDrawableForDensity", "(II)Landroid/graphics/drawable/Drawable;",
+ id, density);
+}
+
diff --git a/xbmc/android/jni/Resources.h b/xbmc/android/jni/Resources.h
new file mode 100644
index 0000000000..bb6751ab9c
--- /dev/null
+++ b/xbmc/android/jni/Resources.h
@@ -0,0 +1,37 @@
+#pragma once
+/*
+ * Copyright (C) 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JNIBase.h"
+#include "List.h"
+
+class CJNIDrawable;
+
+class CJNIResources : public CJNIBase
+{
+public:
+ CJNIResources(const jni::jhobject &object) : CJNIBase(object) {};
+ ~CJNIResources() {};
+
+ CJNIDrawable getDrawableForDensity(int id, int density);
+
+private:
+ CJNIResources();
+};
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
index 6ed6b5dbf7..ed5c904f6f 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
@@ -364,23 +364,6 @@ void CAESinkAUDIOTRACK::Drain()
m_frames_written = 0;
}
-bool CAESinkAUDIOTRACK::HasVolume()
-{
- return true;
-}
-
-void CAESinkAUDIOTRACK::SetVolume(float scale)
-{
- // Ignore in passthrough
- if (m_passthrough)
- return;
-
- if (!m_at_jni)
- return;
-
- CXBMCApp::SetSystemVolume(scale);
-}
-
void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
m_info.m_channels.Reset();
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
index 8927e707ff..d1e188657e 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
@@ -45,8 +45,6 @@ public:
virtual double GetCacheTotal ();
virtual unsigned int AddPackets (uint8_t **data, unsigned int frames, unsigned int offset);
virtual void Drain ();
- virtual bool HasVolume ();
- virtual void SetVolume (float scale);
static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
protected:
diff --git a/xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp b/xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp
index 133a5937cd..2aafd0ef4b 100644
--- a/xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp
+++ b/xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp
@@ -20,16 +20,17 @@
#ifdef HAS_DX
-#include <DirectXPackedVector.h>
#include "WinVideoFilter.h"
-#include "windowing/WindowingFactory.h"
-#include "../../../utils/log.h"
-#include "../../../FileSystem/File.h"
-#include <map>
#include "ConvolutionKernels.h"
-#include "YUV2RGBShader.h"
-#include "win32/WIN32Util.h"
+#include <DirectXPackedVector.h>
+#include "FileSystem/File.h"
+#include "guilib/GraphicContext.h"
#include "Util.h"
+#include "utils/log.h"
+#include "win32/WIN32Util.h"
+#include "windowing/WindowingFactory.h"
+#include "YUV2RGBShader.h"
+#include <map>
using namespace DirectX::PackedVector;
@@ -314,10 +315,6 @@ void CYUV2RGBShader::Render(CRect sourceRect, CRect destRect,
contrast, brightness, flags);
SetShaderParameters(YUVbuf);
Execute(nullptr, 4);
-
- // we changed view port, so we need to restore our real viewport.
- g_Windowing.RestoreViewPort();
-
}
CYUV2RGBShader::~CYUV2RGBShader()
@@ -390,32 +387,9 @@ void CYUV2RGBShader::SetShaderParameters(YUVBuffer* YUVbuf)
m_effect.SetTexture("g_VTexture", YUVbuf->planes[2].texture);
m_effect.SetFloatArray("g_StepXY", m_texSteps, ARRAY_SIZE(m_texSteps));
- // we need to set view port to the full size of current render target
- ID3D11RenderTargetView* rtView = nullptr;
- g_Windowing.Get3D11Context()->OMGetRenderTargets(1, &rtView, nullptr);
-
- // get dimention of render target
- ID3D11Resource* rtResource = nullptr;
- rtView->GetResource(&rtResource);
- ID3D11Texture2D* rtTexture = nullptr;
- HRESULT hr = rtResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&rtTexture));
-
- float viewPortWidth = 0.0f, viewPortHeight = 0.0f;
-
- if (S_OK == hr && rtTexture)
- {
- D3D11_TEXTURE2D_DESC rtDescr = {};
- rtTexture->GetDesc(&rtDescr);
- viewPortWidth = static_cast<float>(rtDescr.Width);
- viewPortHeight = static_cast<float>(rtDescr.Height);
- }
-
- SAFE_RELEASE(rtTexture);
- SAFE_RELEASE(rtResource);
- SAFE_RELEASE(rtView);
-
- D3D11_VIEWPORT viewPort = { 0.0f, 0.0f, viewPortWidth, viewPortHeight, 0.0, 1.0f };
- g_Windowing.Get3D11Context()->RSSetViewports(1, &viewPort);
+ UINT numPorts = 1;
+ D3D11_VIEWPORT viewPort;
+ g_Windowing.Get3D11Context()->RSGetViewports(&numPorts, &viewPort);
m_effect.SetFloatArray("g_viewPort", &viewPort.Width, 2);
}
@@ -844,48 +818,57 @@ void CConvolutionShaderSeparable::SetShaderParameters(CD3DTexture &sourceTexture
void CConvolutionShaderSeparable::SetStepParams(UINT iPass)
{
- float viewPortWidth = 0.0f, viewPortHeight = 0.0f;
+ CD3D11_VIEWPORT viewPort(.0f, .0f, .0f, .0f);
ID3D11DeviceContext* pContext = g_Windowing.Get3D11Context();
if (iPass == 0)
{
// store old RT
pContext->OMGetRenderTargets(1, &m_oldRenderTarget, nullptr);
- viewPortWidth = (float)m_IntermediateTarget.GetWidth();
- viewPortHeight = (float)m_IntermediateTarget.GetHeight();
-
// setting new RT
ID3D11RenderTargetView* newRT = m_IntermediateTarget.GetRenderTarget();
pContext->OMSetRenderTargets(1, &newRT, nullptr);
+ // new viewport
+ viewPort = CD3D11_VIEWPORT(0.0f, 0.0f,
+ static_cast<float>(m_IntermediateTarget.GetWidth()),
+ static_cast<float>(m_IntermediateTarget.GetHeight()));
+ // reset scissor
+ g_Windowing.ResetScissors();
}
else if (iPass == 1)
{
- // get dimention of old render target
- ID3D11Resource* rtResource = nullptr;
- m_oldRenderTarget->GetResource(&rtResource);
- ID3D11Texture2D* rtTexture = nullptr;
- HRESULT hr = rtResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&rtTexture));
-
- if (S_OK == hr && rtTexture)
+ if (m_oldRenderTarget)
{
- D3D11_TEXTURE2D_DESC rtDescr = {};
- rtTexture->GetDesc(&rtDescr);
- viewPortWidth = static_cast<float>(rtDescr.Width);
- viewPortHeight = static_cast<float>(rtDescr.Height);
+ // get dimention of old render target
+ ID3D11Resource* rtResource = nullptr;
+ m_oldRenderTarget->GetResource(&rtResource);
+ ID3D11Texture2D* rtTexture = nullptr;
+ if (SUCCEEDED(rtResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&rtTexture))))
+ {
+ D3D11_TEXTURE2D_DESC rtDescr = {};
+ rtTexture->GetDesc(&rtDescr);
+ viewPort = CD3D11_VIEWPORT(0.0f, 0.0f,
+ static_cast<float>(rtDescr.Width),
+ static_cast<float>(rtDescr.Height));
+ }
+ SAFE_RELEASE(rtTexture);
+ SAFE_RELEASE(rtResource);
+ }
+ else
+ {
+ // current RT is null so try to restore viewport
+ CRect winViewPort;
+ g_Windowing.GetViewPort(winViewPort);
+ viewPort = CD3D11_VIEWPORT(winViewPort.x1, winViewPort.y1, winViewPort.Width(), winViewPort.Height());
}
-
- SAFE_RELEASE(rtTexture);
- SAFE_RELEASE(rtResource);
-
pContext->OMSetRenderTargets(1, &m_oldRenderTarget, nullptr);
SAFE_RELEASE(m_oldRenderTarget);
-
// at the second pass m_IntermediateTarget is a source of data
m_effect.SetTexture("g_Texture", m_IntermediateTarget);
+ // restore scissor
+ g_Windowing.SetScissors(g_graphicsContext.StereoCorrection(g_graphicsContext.GetScissors()));
}
-
- // setting view port to the full size of the current render target
- CD3D11_VIEWPORT viewPort(0.0f, 0.0f, viewPortWidth, viewPortHeight);
+ // seting view port
pContext->RSSetViewports(1, &viewPort);
// pass viewport dimention to the shaders
m_effect.SetFloatArray("g_viewPort", &viewPort.Width, 2);
diff --git a/xbmc/cores/VideoRenderers/WinRenderer.cpp b/xbmc/cores/VideoRenderers/WinRenderer.cpp
index 1dea24febb..f16ce04c11 100644
--- a/xbmc/cores/VideoRenderers/WinRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/WinRenderer.cpp
@@ -814,92 +814,62 @@ void CWinRenderer::RenderPS()
void CWinRenderer::Stage1()
{
+ CD3D11_VIEWPORT viewPort(0.0f, 0.0f, 0.0f, 0.0f);
ID3D11DeviceContext* pContext = g_Windowing.Get3D11Context();
- g_Windowing.ResetScissors();
- // Store current render target and depth view.
- ID3D11RenderTargetView *oldRT = nullptr; ID3D11DepthStencilView* oldDS = nullptr;
- pContext->OMGetRenderTargets(1, &oldRT, &oldDS);
-
- if (!m_bUseHQScaler)
- {
- // disable depth
- pContext->OMSetRenderTargets(1, &oldRT, nullptr);
- // render video frame
- m_colorShader->Render(m_sourceRect, g_graphicsContext.StereoCorrection(m_destRect),
- CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast,
- CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness,
- m_iFlags, (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer]);
- }
- else
+ // store current render target and depth view.
+ ID3D11RenderTargetView *oldRTView = nullptr; ID3D11DepthStencilView* oldDSView = nullptr;
+ pContext->OMGetRenderTargets(1, &oldRTView, &oldDSView);
+ // select destination rectangle
+ CRect destRect = m_bUseHQScaler ? m_sourceRect : g_graphicsContext.StereoCorrection(m_destRect);
+ // select target view
+ ID3D11RenderTargetView* pRTView = m_bUseHQScaler ? m_IntermediateTarget.GetRenderTarget() : oldRTView;
+ // set destination render target
+ pContext->OMSetRenderTargets(1, &pRTView, nullptr);
+ // get rendertarget's dimension
+ if (pRTView)
{
- // At DX9 setting a new render target will cause the viewport
- // to be set to the full size of the new render target.
- // In DX11 we should do this manualy
- CD3D11_VIEWPORT viewPort(0.0f, 0.0f, static_cast<float>(m_IntermediateTarget.GetWidth()), static_cast<float>(m_IntermediateTarget.GetHeight()));
- pContext->RSSetViewports(1, &viewPort);
-
- ID3D11RenderTargetView *newRT = m_IntermediateTarget.GetRenderTarget();
-
- // this needs to avoid binding m_IntermediateTarget as shader resource and as render target at the same time
- CD3DHelper::PSClearShaderResources(pContext);
- // Switch the render target to the temporary destination
- pContext->OMSetRenderTargets(1, &newRT, nullptr);
-
- CRect srcRect(0.0f, 0.0f, static_cast<float>(m_sourceWidth), static_cast<float>(m_sourceHeight));
+ ID3D11Resource* pResource = nullptr;
+ ID3D11Texture2D* pTexture = nullptr;
- m_colorShader->Render(srcRect, srcRect,
- CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast,
- CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness,
- m_iFlags, (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer]);
-
- // Restore our view port.
- g_Windowing.RestoreViewPort();
+ pRTView->GetResource(&pResource);
+ if (SUCCEEDED(pResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pTexture))))
+ {
+ D3D11_TEXTURE2D_DESC desc;
+ pTexture->GetDesc(&desc);
+ viewPort = CD3D11_VIEWPORT(0.0f, 0.0f, desc.Width, desc.Height);
+ }
+ SAFE_RELEASE(pResource);
+ SAFE_RELEASE(pTexture);
}
-
+ // reset scissors for HQ scaler
+ if (m_bUseHQScaler)
+ g_Windowing.ResetScissors();
+ // reset view port
+ pContext->RSSetViewports(1, &viewPort);
+ // render video frame
+ m_colorShader->Render(m_sourceRect, destRect,
+ CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Contrast,
+ CMediaSettings::GetInstance().GetCurrentVideoSettings().m_Brightness,
+ m_iFlags, (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer]);
+ // Restore our view port.
+ g_Windowing.RestoreViewPort();
// Restore the render target and depth view.
- pContext->OMSetRenderTargets(1, &oldRT, oldDS);
- SAFE_RELEASE(oldRT);
- SAFE_RELEASE(oldDS);
+ pContext->OMSetRenderTargets(1, &oldRTView, oldDSView);
+ SAFE_RELEASE(oldRTView);
+ SAFE_RELEASE(oldDSView);
}
void CWinRenderer::Stage2()
{
- g_Windowing.ResetScissors();
-
- CRect sourceRect;
-
- // fixup stereo+dxva+hq scaling issue
- if (m_renderMethod == RENDER_DXVA)
- {
- sourceRect.y1 = 0.0f;
- sourceRect.y2 = static_cast<float>(m_sourceHeight);
- sourceRect.x1 = 0.0f;
- sourceRect.x2 = static_cast<float>(m_sourceWidth);
- }
- else
- sourceRect = m_sourceRect;
-
- m_scalerShader->Render(m_IntermediateTarget, m_sourceWidth, m_sourceHeight, m_destWidth, m_destHeight, sourceRect, g_graphicsContext.StereoCorrection(m_destRect));
+ m_scalerShader->Render(m_IntermediateTarget, m_sourceWidth, m_sourceHeight, m_destWidth, m_destHeight, m_sourceRect, g_graphicsContext.StereoCorrection(m_destRect));
}
void CWinRenderer::RenderProcessor(DWORD flags)
{
CSingleLock lock(g_graphicsContext);
- CRect destRect;
-
- if (m_bUseHQScaler)
- {
- destRect.y1 = 0.0f;
- destRect.y2 = static_cast<float>(m_sourceHeight);
- destRect.x1 = 0.0f;
- destRect.x2 = static_cast<float>(m_sourceWidth);
- }
- else
- destRect = g_graphicsContext.StereoCorrection(m_destRect);
-
+ CRect destRect = m_bUseHQScaler ? m_sourceRect : g_graphicsContext.StereoCorrection(m_destRect);
DXVABuffer *image = (DXVABuffer*)m_VideoBuffers[m_iYV12RenderBuffer];
-
if (!image->pic)
return;
@@ -911,14 +881,17 @@ void CWinRenderer::RenderProcessor(DWORD flags)
|| true /* workaround for some GPUs */)
{
target = m_IntermediateTarget.Get();
- //target->AddRef(); // uncomment when workaround removed
+ target->AddRef();
}
else // dead code.
{
ID3D11RenderTargetView* rtv = nullptr;
g_Windowing.Get3D11Context()->OMGetRenderTargets(1, &rtv, nullptr);
- rtv->GetResource(&target);
- rtv->Release();
+ if (rtv)
+ {
+ rtv->GetResource(&target);
+ rtv->Release();
+ }
}
int past = 0;
@@ -964,7 +937,6 @@ void CWinRenderer::RenderProcessor(DWORD flags)
}
m_processor->Render(m_sourceRect, destRect, target, views, flags, image->frameIdx);
- //target->Release(); // uncomment when workaround removed
if (m_bUseHQScaler)
{
@@ -979,6 +951,7 @@ void CWinRenderer::RenderProcessor(DWORD flags)
CRect tu = { destRect.x1 / m_destWidth, destRect.y1 / m_destHeight, destRect.x2 / m_destWidth, destRect.y2 / m_destHeight };
CD3DTexture::DrawQuad(m_destRect, 0, &m_IntermediateTarget, &tu, SHADER_METHOD_RENDER_TEXTURE_NOBLEND);
}
+ SAFE_RELEASE(target);
}
bool CWinRenderer::RenderCapture(CRenderCapture* capture)
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
index 3ef826550a..b017908825 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
@@ -472,6 +472,13 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
}
if (m_render_surface)
+ {
+ m_videosurface = CXBMCApp::get()->getVideoViewSurface();
+ if (!m_videosurface)
+ return false;
+ }
+
+ if (m_render_surface)
m_formatname += "(S)";
// CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
@@ -917,8 +924,6 @@ bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
}
InitSurfaceTexture();
- if (m_render_surface)
- m_videosurface = CXBMCApp::get()->getVideoViewSurface();
// configure and start the codec.
// use the MediaFormat that we have setup.
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
index 87e90ba436..87d97afd8f 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp
@@ -751,10 +751,7 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputBuffers(void)
callbackData.callback = &CallbackAllocOMXEGLTextures;
callbackData.userptr = (void *)this;
- tMsg.dwMessage = TMSG_CALLBACK;
- tMsg.lpVoid = (void*)&callbackData;
-
- CApplicationMessenger::GetInstance().SendMsg(TMSG_CALLBACK, -1, -1 static_cast<void*>(&callbackData));
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_CALLBACK, -1, -1, static_cast<void*>(&callbackData));
omx_err = OMX_ErrorNone;
}
@@ -1227,7 +1224,7 @@ void OpenMaxVideoBuffer::ReleaseTexture()
deleteInfo->callback.userptr = (void *)deleteInfo;
// HACK, this should be synchronous, but it's not possible since Stop blocks the GUI thread.
- CApplicationMessenger::GetInstance().PostMsg(TMSG_CALLBACK, -1, -1 static_cast<void*>(deleteInfo));
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_CALLBACK, -1, -1, static_cast<void*>(deleteInfo));
}
}
diff --git a/xbmc/filesystem/AndroidAppDirectory.cpp b/xbmc/filesystem/AndroidAppDirectory.cpp
index 3317eb7c0e..69732bda57 100644
--- a/xbmc/filesystem/AndroidAppDirectory.cpp
+++ b/xbmc/filesystem/AndroidAppDirectory.cpp
@@ -69,6 +69,7 @@ bool CAndroidAppDirectory::GetDirectory(const CURL& url, CFileItemList &items)
pItem->SetPath(path);
pItem->SetLabel((*i).packageLabel);
pItem->SetArt("thumb", path+".png");
+ pItem->m_dwSize = -1; // No size
items.Add(pItem);
}
return true;
diff --git a/xbmc/filesystem/AndroidAppFile.cpp b/xbmc/filesystem/AndroidAppFile.cpp
index 439780db52..7df2026b84 100644
--- a/xbmc/filesystem/AndroidAppFile.cpp
+++ b/xbmc/filesystem/AndroidAppFile.cpp
@@ -23,13 +23,21 @@
#if defined(TARGET_ANDROID)
#include "AndroidAppFile.h"
+#include "android/activity/XBMCApp.h"
#include <sys/stat.h>
#include "Util.h"
#include "URL.h"
#include "utils/log.h"
#include "utils/URIUtils.h"
#include <jni.h>
-#include "android/activity/XBMCApp.h"
+#include <android/bitmap.h>
+#include "android/jni/Context.h"
+#include "android/jni/Build.h"
+#include "android/jni/DisplayMetrics.h"
+#include "android/jni/Resources.h"
+#include "android/jni/Bitmap.h"
+#include "android/jni/BitmapDrawable.h"
+#include "android/jni/PackageManager.h"
using namespace XFILE;
CFileAndroidApp::CFileAndroidApp(void)
@@ -46,14 +54,18 @@ CFileAndroidApp::~CFileAndroidApp(void)
bool CFileAndroidApp::Open(const CURL& url)
{
m_url = url;
- m_appname = URIUtils::GetFileName(url.Get());
- m_appname = m_appname.substr(0, m_appname.size() - 4);
+ m_packageName = URIUtils::GetFileName(url.Get());
+ m_packageName = m_packageName.substr(0, m_packageName.size() - 4);
std::vector<androidPackage> applications = CXBMCApp::GetApplications();
for(std::vector<androidPackage>::iterator i = applications.begin(); i != applications.end(); ++i)
{
- if ((*i).packageName == m_appname)
+ if ((*i).packageName == m_packageName)
+ {
+ m_packageLabel = i->packageLabel;
+ m_icon = i->icon;
return true;
+ }
}
return false;
@@ -74,31 +86,50 @@ bool CFileAndroidApp::Exists(const CURL& url)
return false;
}
-ssize_t CFileAndroidApp::Read(void* lpBuf, size_t uiBufSize)
+unsigned int CFileAndroidApp::ReadIcon(unsigned char** lpBuf, unsigned int* width, unsigned int* height)
{
- if(CXBMCApp::GetIcon(m_appname, lpBuf, uiBufSize))
- return uiBufSize; // FIXME: not full buffer may be used
- return -1;
-}
+ JNIEnv* env = xbmc_jnienv();
+ void *bitmapBuf = NULL;
-void CFileAndroidApp::Close()
-{
-}
+ CJNIBitmapDrawable bmp;
+ if (CJNIBuild::SDK_INT >= 15 && m_icon)
+ {
+ int density = CJNIDisplayMetrics::DENSITY_XHIGH;
+ if (CJNIBuild::SDK_INT >= 18)
+ density = CJNIDisplayMetrics::DENSITY_XXXHIGH;
+ else if (CJNIBuild::SDK_INT >= 16)
+ density = CJNIDisplayMetrics::DENSITY_XXHIGH;
+ CJNIResources res = CJNIContext::GetPackageManager().getResourcesForApplication(m_packageName);
+ if (res)
+ bmp = res.getDrawableForDensity(m_icon, density);
+ }
+ else
+ bmp = (CJNIBitmapDrawable)CJNIContext::GetPackageManager().getApplicationIcon(m_packageName);
-int64_t CFileAndroidApp::GetLength()
-{
- CXBMCApp::GetIconSize(m_appname, &m_iconWidth, &m_iconHeight);
- return m_iconWidth * m_iconHeight * 4;
-}
+ CJNIBitmap bitmap(bmp.getBitmap());
+ AndroidBitmapInfo info;
+ AndroidBitmap_getInfo(env, bitmap.get_raw(), &info);
+ if (!info.width || !info.height)
+ return 0;
-unsigned int CFileAndroidApp::GetIconWidth()
-{
- return m_iconWidth;
+ *width = info.width;
+ *height = info.height;
+
+ int imgsize = *width * *height * 4;
+ *lpBuf = new unsigned char[imgsize];
+
+ AndroidBitmap_lockPixels(env, bitmap.get_raw(), &bitmapBuf);
+ if (bitmapBuf)
+ {
+ memcpy(*lpBuf, bitmapBuf, imgsize);
+ AndroidBitmap_unlockPixels(env, bitmap.get_raw());
+ return imgsize;
+ }
+ return 0;
}
-unsigned int CFileAndroidApp::GetIconHeight()
+void CFileAndroidApp::Close()
{
- return m_iconHeight;
}
int CFileAndroidApp::GetChunkSize()
diff --git a/xbmc/filesystem/AndroidAppFile.h b/xbmc/filesystem/AndroidAppFile.h
index a7099fd7ad..25b6a5a600 100644
--- a/xbmc/filesystem/AndroidAppFile.h
+++ b/xbmc/filesystem/AndroidAppFile.h
@@ -24,6 +24,8 @@
#if defined(TARGET_ANDROID)
#include "IFile.h"
#include "URL.h"
+#include "string.h"
+
namespace XFILE
{
class CFileAndroidApp : public IFile
@@ -37,25 +39,24 @@ public:
virtual int Stat(const CURL& url, struct __stat64* buffer);
/*! \brief Return 32bit rgba raw bitmap. */
- virtual ssize_t Read(void* lpBuf, size_t uiBufSize);
+ virtual ssize_t Read(void* lpBuf, size_t uiBufSize) {return 0;}
virtual void Close();
- virtual int64_t GetLength();
- virtual int64_t Seek(int64_t, int) {return -1;};
- virtual int64_t GetPosition() {return 0;};
+ virtual int64_t GetLength() {return 0;}
+ virtual int64_t Seek(int64_t, int) {return -1;}
+ virtual int64_t GetPosition() {return 0;}
virtual int GetChunkSize();
virtual int IoControl(EIoControl request, void* param);
- /*! \brief Only valid after GetLength() has been called, usually by Open(). */
- unsigned int GetIconWidth();
- /*! \brief Only valid after GetLength() has been called, usually by Open(). */
- unsigned int GetIconHeight();
+ virtual unsigned int ReadIcon(unsigned char **lpBuf, unsigned int* width, unsigned int* height);
protected:
bool IsValidFile(const CURL& url);
private:
CURL m_url;
- std::string m_appname;
+ std::string m_packageName;
+ std::string m_packageLabel;
+ int m_icon;
int m_iconWidth;
int m_iconHeight;
};
diff --git a/xbmc/filesystem/MusicDatabaseDirectory.cpp b/xbmc/filesystem/MusicDatabaseDirectory.cpp
index 4413e7a316..4eafeaabcf 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory.cpp
+++ b/xbmc/filesystem/MusicDatabaseDirectory.cpp
@@ -45,6 +45,8 @@ bool CMusicDatabaseDirectory::GetDirectory(const CURL& url, CFileItemList &items
{
std::string path = CLegacyPathTranslation::TranslateMusicDbPath(url);
items.SetPath(path);
+ items.m_dwSize = -1; // No size
+
std::unique_ptr<CDirectoryNode> pNode(CDirectoryNode::ParseURL(path));
if (!pNode.get())
diff --git a/xbmc/filesystem/VideoDatabaseDirectory.cpp b/xbmc/filesystem/VideoDatabaseDirectory.cpp
index ede4b24f74..96716fa7cb 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory.cpp
@@ -46,6 +46,7 @@ bool CVideoDatabaseDirectory::GetDirectory(const CURL& url, CFileItemList &items
{
std::string path = CLegacyPathTranslation::TranslateVideoDbPath(url);
items.SetPath(path);
+ items.m_dwSize = -1; // No size
std::unique_ptr<CDirectoryNode> pNode(CDirectoryNode::ParseURL(path));
if (!pNode.get())
diff --git a/xbmc/guilib/GUIToggleButtonControl.cpp b/xbmc/guilib/GUIToggleButtonControl.cpp
index 976f4842b1..1620a16761 100644
--- a/xbmc/guilib/GUIToggleButtonControl.cpp
+++ b/xbmc/guilib/GUIToggleButtonControl.cpp
@@ -38,14 +38,8 @@ CGUIToggleButtonControl::~CGUIToggleButtonControl(void)
void CGUIToggleButtonControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
{
// ask our infoManager whether we are selected or not...
- bool selected = m_bSelected;
if (m_toggleSelect)
- selected = m_toggleSelect->Get();
- if (selected != m_bSelected)
- {
- MarkDirtyRegion();
- m_bSelected = selected;
- }
+ m_bSelected = m_toggleSelect->Get();
if (m_bSelected)
{
@@ -54,9 +48,25 @@ void CGUIToggleButtonControl::Process(unsigned int currentTime, CDirtyRegionList
m_selectButton.SetVisible(IsVisible());
m_selectButton.SetEnabled(!IsDisabled());
m_selectButton.SetPulseOnSelect(m_pulseOnSelect);
+ ProcessToggle(currentTime);
m_selectButton.DoProcess(currentTime, dirtyregions);
}
- CGUIButtonControl::Process(currentTime, dirtyregions);
+ else
+ CGUIButtonControl::Process(currentTime, dirtyregions);
+}
+
+void CGUIToggleButtonControl::ProcessToggle(unsigned int currentTime)
+{
+ bool changed = false;
+
+ changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
+ changed |= m_label.SetText(GetDescription());
+ changed |= m_label.SetColor(GetTextColor());
+ changed |= m_label.SetScrolling(HasFocus());
+ changed |= m_label.Process(currentTime);
+
+ if (changed)
+ MarkDirtyRegion();
}
void CGUIToggleButtonControl::Render()
@@ -139,12 +149,6 @@ bool CGUIToggleButtonControl::UpdateColors()
return changed;
}
-void CGUIToggleButtonControl::SetLabel(const std::string &strLabel)
-{
- CGUIButtonControl::SetLabel(strLabel);
- m_selectButton.SetLabel(strLabel);
-}
-
void CGUIToggleButtonControl::SetAltLabel(const std::string &label)
{
if (label.size())
diff --git a/xbmc/guilib/GUIToggleButtonControl.h b/xbmc/guilib/GUIToggleButtonControl.h
index a8248f0a13..a36250a6d0 100644
--- a/xbmc/guilib/GUIToggleButtonControl.h
+++ b/xbmc/guilib/GUIToggleButtonControl.h
@@ -52,7 +52,6 @@ public:
virtual void SetWidth(float width);
virtual void SetHeight(float height);
virtual void SetMinWidth(float minWidth);
- void SetLabel(const std::string& strLabel);
void SetAltLabel(const std::string& label);
virtual std::string GetDescription() const;
void SetToggleSelect(const std::string &toggleSelect);
@@ -63,5 +62,9 @@ protected:
virtual void OnClick();
CGUIButtonControl m_selectButton;
INFO::InfoPtr m_toggleSelect;
+
+private:
+ void ProcessToggle(unsigned int currentTime);
+ std::string m_altLabel;
};
#endif
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index bafd60a0a5..c04386e1f0 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -296,6 +296,6 @@ private:
\brief
*/
-XBMC_GLOBAL(CGraphicContext,g_graphicsContext);
-
+XBMC_GLOBAL_REF(CGraphicContext,g_graphicsContext);
+#define g_graphicsContext XBMC_GLOBAL_USE(CGraphicContext)
#endif
diff --git a/xbmc/guilib/Texture.cpp b/xbmc/guilib/Texture.cpp
index 5cca9a0a9f..043fc3bed8 100644
--- a/xbmc/guilib/Texture.cpp
+++ b/xbmc/guilib/Texture.cpp
@@ -184,18 +184,15 @@ CBaseTexture *CBaseTexture::LoadFromFile(const std::string& texturePath, unsigne
XFILE::CFileAndroidApp file;
if (file.Open(url))
{
- unsigned int imgsize = (unsigned int)file.GetLength();
- unsigned char* inputBuff = new unsigned char[imgsize];
- unsigned int inputBuffSize = file.Read(inputBuff, imgsize);
+ unsigned char* inputBuff;
+ unsigned int width;
+ unsigned int height;
+ unsigned int inputBuffSize = file.ReadIcon(&inputBuff, &width, &height);
file.Close();
- if (inputBuffSize != imgsize)
- {
- delete [] inputBuff;
+ if (!inputBuffSize)
return NULL;
- }
+
CTexture *texture = new CTexture();
- unsigned int width = file.GetIconWidth();
- unsigned int height = file.GetIconHeight();
texture->LoadFromMemory(width, height, width*4, XB_FMT_RGBA8, true, inputBuff);
delete [] inputBuff;
return texture;
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
index b52b106253..d5d3d6b7f3 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
@@ -137,18 +137,16 @@ JSONRPC_STATUS CAudioLibrary::GetAlbums(const std::string &method, ITransportLay
if (!musicUrl.FromString("musicdb://albums/"))
return InternalError;
-
if (parameterObject["includesingles"].asBoolean())
musicUrl.AddOption("show_singles", true);
- int artistID = -1, genreID = -1;
const CVariant &filter = parameterObject["filter"];
if (filter.isMember("artistid"))
- artistID = (int)filter["artistid"].asInteger();
+ musicUrl.AddOption("artistid", (int)filter["artistid"].asInteger());
else if (filter.isMember("artist"))
musicUrl.AddOption("artist", filter["artist"].asString());
else if (filter.isMember("genreid"))
- genreID = (int)filter["genreid"].asInteger();
+ musicUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
else if (filter.isMember("genre"))
musicUrl.AddOption("genre", filter["genre"].asString());
else if (filter.isObject())
@@ -165,17 +163,32 @@ JSONRPC_STATUS CAudioLibrary::GetAlbums(const std::string &method, ITransportLay
if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
return InvalidParams;
- CFileItemList items;
- if (!musicdatabase.GetAlbumsNav(musicUrl.ToString(), items, genreID, artistID, CDatabase::Filter(), sorting))
+ int total;
+ VECALBUMS albums;
+ if (!musicdatabase.GetAlbumsByWhere(musicUrl.ToString(), CDatabase::Filter(), albums, total, sorting))
return InternalError;
+ CFileItemList items;
+ items.Reserve(albums.size());
+ for (unsigned int index = 0; index < albums.size(); index++)
+ {
+ CMusicDbUrl itemUrl = musicUrl;
+ std::string path = StringUtils::Format("%i/", albums[index].idAlbum);
+ itemUrl.AppendPath(path);
+
+ CFileItemPtr pItem;
+ FillAlbumItem(albums[index], itemUrl.ToString(), pItem);
+ items.Add(pItem);
+ }
+
+ //Get Genre IDs
JSONRPC_STATUS ret = GetAdditionalAlbumDetails(parameterObject, items, musicdatabase);
if (ret != OK)
return ret;
int size = items.Size();
- if (items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
- size = (int)items.GetProperty("total").asInteger();
+ if (total > size)
+ size = total;
HandleFileItemList("albumid", false, "albums", items, parameterObject, result, size, false);
return OK;
@@ -221,22 +234,20 @@ JSONRPC_STATUS CAudioLibrary::GetSongs(const std::string &method, ITransportLaye
if (!musicUrl.FromString("musicdb://songs/"))
return InternalError;
-
if (!parameterObject["includesingles"].asBoolean())
musicUrl.AddOption("singles", false);
- int genreID = -1, albumID = -1, artistID = -1;
const CVariant &filter = parameterObject["filter"];
if (filter.isMember("artistid"))
- artistID = (int)filter["artistid"].asInteger();
+ musicUrl.AddOption("artistid", (int)filter["artistid"].asInteger());
else if (filter.isMember("artist"))
musicUrl.AddOption("artist", filter["artist"].asString());
else if (filter.isMember("genreid"))
- genreID = (int)filter["genreid"].asInteger();
+ musicUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
else if (filter.isMember("genre"))
musicUrl.AddOption("genre", filter["genre"].asString());
else if (filter.isMember("albumid"))
- albumID = (int)filter["albumid"].asInteger();
+ musicUrl.AddOption("albumid", (int)filter["albumid"].asInteger());
else if (filter.isMember("album"))
musicUrl.AddOption("album", filter["album"].asString());
else if (filter.isObject())
@@ -254,8 +265,8 @@ JSONRPC_STATUS CAudioLibrary::GetSongs(const std::string &method, ITransportLaye
return InvalidParams;
CFileItemList items;
- if (!musicdatabase.GetSongsNav(musicUrl.ToString(), items, genreID, artistID, albumID, sorting))
- return InternalError;
+ if (!musicdatabase.GetSongsFullByWhere(musicUrl.ToString(), CDatabase::Filter(), items, sorting, true))
+ return InternalError;
JSONRPC_STATUS ret = GetAdditionalSongDetails(parameterObject, items, musicdatabase);
if (ret != OK)
@@ -282,7 +293,10 @@ JSONRPC_STATUS CAudioLibrary::GetSongDetails(const std::string &method, ITranspo
return InvalidParams;
CFileItemList items;
- items.Add(CFileItemPtr(new CFileItem(song)));
+ CFileItemPtr item = CFileItemPtr(new CFileItem(song));
+ FillItemArtistIDs(song.GetArtistIDArray(), item);
+ items.Add(item);
+
JSONRPC_STATUS ret = GetAdditionalSongDetails(parameterObject, items, musicdatabase);
if (ret != OK)
return ret;
@@ -601,9 +615,11 @@ bool CAudioLibrary::FillFileItem(const std::string &strFilename, CFileItemPtr &i
if (musicdatabase.GetAlbum(albumid, album, false))
{
item->SetFromAlbum(album);
+ FillItemArtistIDs(album.GetArtistIDArray(), item);
CFileItemList items;
items.Add(item);
+
if (GetAdditionalAlbumDetails(parameterObject, items, musicdatabase) == OK)
filled = true;
}
@@ -614,6 +630,7 @@ bool CAudioLibrary::FillFileItem(const std::string &strFilename, CFileItemPtr &i
if (musicdatabase.GetSongByFileName(strFilename, song))
{
item->SetFromSong(song);
+ FillItemArtistIDs(song.GetArtistIDArray(), item);
CFileItemList items;
items.Add(item);
@@ -685,9 +702,22 @@ bool CAudioLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemL
return success;
}
+void CAudioLibrary::FillItemArtistIDs(const std::vector<int> artistids, CFileItemPtr &item)
+{
+ // Add artistIds as separate property as not part of CMusicInfoTag
+ CVariant artistidObj(CVariant::VariantTypeArray);
+ for (std::vector<int>::const_iterator artistid = artistids.begin(); artistid != artistids.end(); ++artistid)
+ artistidObj.push_back(*artistid);
+
+ item->SetProperty("artistid", artistidObj);
+}
+
void CAudioLibrary::FillAlbumItem(const CAlbum &album, const std::string &path, CFileItemPtr &item)
{
item = CFileItemPtr(new CFileItem(path, album));
+ // Add album artistIds as separate property as not part of CMusicInfoTag
+ std::vector<int> artistids = album.GetArtistIDArray();
+ FillItemArtistIDs(artistids, item);
}
JSONRPC_STATUS CAudioLibrary::GetAdditionalDetails(const CVariant &parameterObject, CFileItemList &items)
@@ -711,7 +741,6 @@ JSONRPC_STATUS CAudioLibrary::GetAdditionalAlbumDetails(const CVariant &paramete
std::set<std::string> checkProperties;
checkProperties.insert("genreid");
- checkProperties.insert("artistid");
std::set<std::string> additionalProperties;
if (!CheckForAdditionalProperties(parameterObject["properties"], checkProperties, additionalProperties))
return OK;
@@ -731,18 +760,7 @@ JSONRPC_STATUS CAudioLibrary::GetAdditionalAlbumDetails(const CVariant &paramete
item->SetProperty("genreid", genreidObj);
}
}
- if (additionalProperties.find("artistid") != additionalProperties.end())
- {
- std::vector<int> artistids;
- if (musicdatabase.GetArtistsByAlbum(item->GetMusicInfoTag()->GetDatabaseId(), true, artistids))
- {
- CVariant artistidObj(CVariant::VariantTypeArray);
- for (std::vector<int>::const_iterator artistid = artistids.begin(); artistid != artistids.end(); ++artistid)
- artistidObj.push_back(*artistid);
-
- item->SetProperty("artistid", artistidObj);
- }
- }
+
}
return OK;
@@ -755,8 +773,12 @@ JSONRPC_STATUS CAudioLibrary::GetAdditionalSongDetails(const CVariant &parameter
std::set<std::string> checkProperties;
checkProperties.insert("genreid");
- checkProperties.insert("artistid");
+ // Query (songview join songartistview) returns song.strAlbumArtists = CMusicInfoTag.m_strAlbumArtistDesc only
+ // Actual album artist data, if required, comes from album_artist and artist tables.
+ // It may differ from just splitting album artist description string
+ checkProperties.insert("albumartist");
checkProperties.insert("albumartistid");
+ checkProperties.insert("musicbrainzalbumartistid");
std::set<std::string> additionalProperties;
if (!CheckForAdditionalProperties(parameterObject["properties"], checkProperties, additionalProperties))
return OK;
@@ -776,28 +798,13 @@ JSONRPC_STATUS CAudioLibrary::GetAdditionalSongDetails(const CVariant &parameter
item->SetProperty("genreid", genreidObj);
}
}
- if (additionalProperties.find("artistid") != additionalProperties.end())
- {
- std::vector<int> artistids;
- if (musicdatabase.GetArtistsBySong(item->GetMusicInfoTag()->GetDatabaseId(), true, artistids))
- {
- CVariant artistidObj(CVariant::VariantTypeArray);
- for (std::vector<int>::const_iterator artistid = artistids.begin(); artistid != artistids.end(); ++artistid)
- artistidObj.push_back(*artistid);
-
- item->SetProperty("artistid", artistidObj);
- }
- }
- if (additionalProperties.find("albumartistid") != additionalProperties.end() && item->GetMusicInfoTag()->GetAlbumId() > 0)
+ if (item->GetMusicInfoTag()->GetAlbumId() > 0)
{
- std::vector<int> albumartistids;
- if (musicdatabase.GetArtistsByAlbum(item->GetMusicInfoTag()->GetAlbumId(), true, albumartistids))
+ if (additionalProperties.find("albumartist") != additionalProperties.end() ||
+ additionalProperties.find("albumartistid") != additionalProperties.end() ||
+ additionalProperties.find("musicbrainzalbumartistid") != additionalProperties.end())
{
- CVariant albumartistidObj(CVariant::VariantTypeArray);
- for (std::vector<int>::const_iterator albumartistid = albumartistids.begin(); albumartistid != albumartistids.end(); ++albumartistid)
- albumartistidObj.push_back(*albumartistid);
-
- item->SetProperty("albumartistid", albumartistidObj);
+ musicdatabase.GetArtistsByAlbum(item->GetMusicInfoTag()->GetAlbumId(), item.get());
}
}
}
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.h b/xbmc/interfaces/json-rpc/AudioLibrary.h
index c44dddb9bf..73cbd25db9 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.h
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.h
@@ -62,6 +62,7 @@ namespace JSONRPC
private:
static void FillAlbumItem(const CAlbum &album, const std::string &path, CFileItemPtr &item);
+ static void FillItemArtistIDs(const std::vector<int> artistids, CFileItemPtr &item);
static bool CheckForAdditionalProperties(const CVariant &properties, const std::set<std::string> &checkProperties, std::set<std::string> &foundProperties);
};
diff --git a/xbmc/interfaces/json-rpc/schema/version.txt b/xbmc/interfaces/json-rpc/schema/version.txt
index d2715cd5ce..d6b71af529 100644
--- a/xbmc/interfaces/json-rpc/schema/version.txt
+++ b/xbmc/interfaces/json-rpc/schema/version.txt
@@ -1 +1 @@
-6.31.0
+6.32.0
diff --git a/xbmc/music/Album.cpp b/xbmc/music/Album.cpp
index 866959e321..540c05ec7d 100644
--- a/xbmc/music/Album.cpp
+++ b/xbmc/music/Album.cpp
@@ -178,11 +178,6 @@ const std::vector<std::string> CAlbum::GetAlbumArtist() const
{
albumartists.push_back(artistCredit->GetArtist());
}
- //When artist credits have not been populated attempt to build an artist vector from the descrpition string
- //This is a tempory fix, in the longer term other areas should query the album_artist table and populate
- //artist credits. Note that splitting the string may not give the same artists as held in the album_artist table
- if (albumartists.empty() && !strArtistDesc.empty())
- albumartists = StringUtils::Split(strArtistDesc, g_advancedSettings.m_musicItemSeparator);
return albumartists;
}
@@ -209,6 +204,15 @@ const std::string CAlbum::GetAlbumArtistString() const
return artistString;
}
+const std::vector<int> CAlbum::GetArtistIDArray() const
+{
+ // Get album artist IDs for json rpc
+ std::vector<int> artistids;
+ for (VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin(); artistCredit != artistCredits.end(); ++artistCredit)
+ artistids.push_back(artistCredit->GetArtistId());
+ return artistids;
+}
+
std::string CAlbum::GetReleaseType() const
{
return ReleaseTypeToString(releaseType);
diff --git a/xbmc/music/Album.h b/xbmc/music/Album.h
index fee9116958..2a2239492e 100644
--- a/xbmc/music/Album.h
+++ b/xbmc/music/Album.h
@@ -87,6 +87,11 @@ public:
*/
const std::string GetAlbumArtistString() const;
+ /*! \brief Get album artist IDs (for json rpc) from the vector of artistcredits objects
+ \return album artist IDs as a vector of integers
+ */
+ const std::vector<int> GetArtistIDArray() const;
+
typedef enum ReleaseType {
Album = 0,
Single
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index d2281ebe35..ef3583513c 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -1421,17 +1421,13 @@ bool CMusicDatabase::GetAlbumsByArtist(int idArtist, bool includeFeatured, std::
return false;
}
-bool CMusicDatabase::GetArtistsByAlbum(int idAlbum, bool includeFeatured, std::vector<int> &artists)
+bool CMusicDatabase::GetArtistsByAlbum(int idAlbum, CFileItem* item)
{
try
{
std::string strSQL, strPrepSQL;
-
- strPrepSQL = "select idArtist from album_artist where idAlbum=%i";
- if (includeFeatured == false)
- strPrepSQL += " AND boolFeatured = 0";
-
- strSQL=PrepareSQL(strPrepSQL, idAlbum);
+
+ strSQL = PrepareSQL("SELECT * FROM albumartistview WHERE idAlbum = %i", idAlbum);
if (!m_pDS->query(strSQL))
return false;
if (m_pDS->num_rows() == 0)
@@ -1440,12 +1436,31 @@ bool CMusicDatabase::GetArtistsByAlbum(int idAlbum, bool includeFeatured, std::v
return false;
}
+ // Get album artist credits
+ VECARTISTCREDITS artistCredits;
while (!m_pDS->eof())
{
- artists.push_back(m_pDS->fv("idArtist").get_asInt());
+ artistCredits.push_back(GetArtistCreditFromDataset(m_pDS->get_sql_record(), 0));
m_pDS->next();
}
m_pDS->close();
+
+ // Populate item with song albumartist credits
+ std::vector<std::string> musicBrainzID;
+ std::vector<std::string> albumartists;
+ CVariant artistidObj(CVariant::VariantTypeArray);
+ for (VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin(); artistCredit != artistCredits.end(); ++artistCredit)
+ {
+ artistidObj.push_back(artistCredit->GetArtistId());
+ albumartists.push_back(artistCredit->GetArtist());
+ if (!artistCredit->GetMusicBrainzArtistID().empty())
+ musicBrainzID.push_back(artistCredit->GetMusicBrainzArtistID());
+ }
+ item->GetMusicInfoTag()->SetAlbumArtist(albumartists);
+ item->GetMusicInfoTag()->SetMusicBrainzAlbumArtistID(musicBrainzID);
+ // Add song albumartistIds as separate property as not part of CMusicInfoTag
+ item->SetProperty("albumartistid", artistidObj);
+
return true;
}
catch (...)
@@ -1672,8 +1687,8 @@ void CMusicDatabase::GetFileItemFromDataset(CFileItem* item, const CMusicDbUrl &
void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const record, CFileItem* item, const CMusicDbUrl &baseUrl)
{
- // get the artist string from song (not the song_artist and artist tables)
- item->GetMusicInfoTag()->SetArtist(record->at(song_strArtists).get_asString());
+ // get the artist string from songview (not the song_artist and artist tables)
+ item->GetMusicInfoTag()->SetArtistDesc(record->at(song_strArtists).get_asString());
// and the full genre string
item->GetMusicInfoTag()->SetGenre(record->at(song_strGenres).get_asString());
// and the rest...
@@ -1700,6 +1715,7 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
std::string strRealPath = URIUtils::AddFileToFolder(record->at(song_strPath).get_asString(), record->at(song_strFileName).get_asString());
item->GetMusicInfoTag()->SetURL(strRealPath);
item->GetMusicInfoTag()->SetCompilation(record->at(song_bCompilation).get_asInt() == 1);
+ // get the album artist string from songview (not the album_artist and artist tables)
item->GetMusicInfoTag()->SetAlbumArtist(record->at(song_strAlbumArtists).get_asString());
item->GetMusicInfoTag()->SetAlbumReleaseType(CAlbum::ReleaseTypeFromString(record->at(song_strAlbumReleaseType).get_asString()));
item->GetMusicInfoTag()->SetLoaded(true);
@@ -1717,6 +1733,25 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
}
}
+void CMusicDatabase::GetFileItemFromArtistCredits(VECARTISTCREDITS& artistCredits, CFileItem* item)
+{
+ // Populate fileitem with artists from vector of artist credits
+ std::vector<std::string> musicBrainzID;
+ std::vector<std::string> songartists;
+ CVariant artistidObj(CVariant::VariantTypeArray);
+ for (VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin(); artistCredit != artistCredits.end(); ++artistCredit)
+ {
+ artistidObj.push_back(artistCredit->GetArtistId());
+ songartists.push_back(artistCredit->GetArtist());
+ if (!artistCredit->GetMusicBrainzArtistID().empty())
+ musicBrainzID.push_back(artistCredit->GetMusicBrainzArtistID());
+ }
+ item->GetMusicInfoTag()->SetArtist(songartists);
+ item->GetMusicInfoTag()->SetMusicBrainzArtistID(musicBrainzID);
+ // Add album artistIds as separate property as not part of CMusicInfoTag
+ item->SetProperty("artistid", artistidObj);
+}
+
CAlbum CMusicDatabase::GetAlbumFromDataset(dbiplus::Dataset* pDS, int offset /* = 0 */, bool imageURL /* = false*/)
{
return GetAlbumFromDataset(pDS->get_sql_record(), offset, imageURL);
@@ -1987,12 +2022,14 @@ bool CMusicDatabase::GetTop100Albums(VECALBUMS& albums)
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
- // NOTE: The song.idAlbum is needed for the group by, as for some reason group by albumview.idAlbum doesn't work
- // consistently - possibly an SQLite bug, as it works fine in SQLiteSpy (v3.3.17)
- std::string strSQL = "select albumview.* from albumview "
- "where albumview.iTimesPlayed>0 and albumview.strAlbum != '' "
- "order by albumview.iTimesPlayed desc "
- "limit 100 ";
+ // Get data from album and album_artist tables to fully populate albums
+ std::string strSQL = "SELECT albumview.*, albumartistview.* FROM albumview "
+ "LEFT JOIN albumartistview ON albumview.idAlbum = albumartistview.idAlbum "
+ "WHERE albumartistview.idAlbum in "
+ "(SELECT albumview.idAlbum FROM albumview "
+ "WHERE albumview.strAlbum != '' AND albumview.iTimesPlayed>0 "
+ "ORDER BY albumview.iTimesPlayed DESC LIMIT 100) "
+ "ORDER BY albumview.iTimesPlayed DESC, albumartistview.iOrder";
CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
if (!m_pDS->query(strSQL)) return false;
@@ -2002,9 +2039,21 @@ bool CMusicDatabase::GetTop100Albums(VECALBUMS& albums)
m_pDS->close();
return true;
}
+
+ int albumArtistOffset = album_enumCount;
+ int albumId = -1;
while (!m_pDS->eof())
{
- albums.push_back(GetAlbumFromDataset(m_pDS.get()));
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+
+ if (albumId != record->at(album_idAlbum).get_asInt())
+ { // New album
+ albumId = record->at(album_idAlbum).get_asInt();
+ albums.push_back(GetAlbumFromDataset(record));
+ }
+ // Get artist details
+ albums.back().artistCredits.push_back(GetArtistCreditFromDataset(record, albumArtistOffset));
+
m_pDS->next();
}
@@ -2070,7 +2119,14 @@ bool CMusicDatabase::GetRecentlyPlayedAlbums(VECALBUMS& albums)
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
- std::string strSQL = StringUtils::Format("select distinct albumview.* from song join albumview on albumview.idAlbum=song.idAlbum where song.lastplayed IS NOT NULL order by song.lastplayed desc limit %i", RECENTLY_PLAYED_LIMIT);
+ // Get data from album and album_artist tables to fully populate albums
+ std::string strSQL = PrepareSQL("SELECT albumview.*, albumartistview.* FROM "
+ "(SELECT idAlbum FROM albumview WHERE albumview.lastplayed IS NOT NULL "
+ "ORDER BY albumview.lastplayed DESC LIMIT %u) as playedalbums "
+ "JOIN albumview ON albumview.idAlbum = playedalbums.idAlbum "
+ "LEFT JOIN albumartistview ON albumview.idAlbum = albumartistview.idAlbum "
+ "ORDER BY albumview.lastplayed DESC, albumartistview.iorder ", RECENTLY_PLAYED_LIMIT);
+
CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
if (!m_pDS->query(strSQL)) return false;
int iRowsFound = m_pDS->num_rows();
@@ -2079,12 +2135,23 @@ bool CMusicDatabase::GetRecentlyPlayedAlbums(VECALBUMS& albums)
m_pDS->close();
return true;
}
+
+ int albumArtistOffset = album_enumCount;
+ int albumId = -1;
while (!m_pDS->eof())
{
- albums.push_back(GetAlbumFromDataset(m_pDS.get()));
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+
+ if (albumId != record->at(album_idAlbum).get_asInt())
+ { // New album
+ albumId = record->at(album_idAlbum).get_asInt();
+ albums.push_back(GetAlbumFromDataset(record));
+ }
+ // Get artist details
+ albums.back().artistCredits.push_back(GetArtistCreditFromDataset(record, albumArtistOffset));
+
m_pDS->next();
}
-
m_pDS->close(); // cleanup recordset data
return true;
}
@@ -2107,7 +2174,12 @@ bool CMusicDatabase::GetRecentlyPlayedAlbumSongs(const std::string& strBaseDir,
if (!strBaseDir.empty() && !baseUrl.FromString(strBaseDir))
return false;
- std::string strSQL = StringUtils::Format("SELECT songview.*, albumview.* FROM songview JOIN albumview ON (songview.idAlbum = albumview.idAlbum) JOIN (SELECT DISTINCT album.idAlbum FROM album JOIN song ON album.idAlbum = song.idAlbum WHERE song.lastplayed IS NOT NULL ORDER BY song.lastplayed DESC LIMIT %i) AS _albumlimit ON (albumview.idAlbum = _albumlimit.idAlbum)", g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
+ std::string strSQL = PrepareSQL("SELECT songview.*, songartistview.* FROM "
+ "(SELECT idAlbum FROM albumview WHERE albumview.lastplayed IS NOT NULL "
+ "ORDER BY albumview.lastplayed DESC LIMIT %u) as playedalbums "
+ "JOIN songview ON songview.idAlbum = playedalbums.idAlbum "
+ "LEFT JOIN songartistview ON songview.idSong = songartistview.idSong ",
+ g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
CLog::Log(LOGDEBUG,"GetRecentlyPlayedAlbumSongs() query: %s", strSQL.c_str());
if (!m_pDS->query(strSQL)) return false;
@@ -2118,15 +2190,38 @@ bool CMusicDatabase::GetRecentlyPlayedAlbumSongs(const std::string& strBaseDir,
return true;
}
- // get data from returned rows
- items.Reserve(iRowsFound);
+ // Needs a separate query to determine number of songs to set items size.
+ // Get songs from returned rows. Join means there is a row for every song artist
+ int songArtistOffset = song_enumCount;
+ int songId = -1;
+ VECARTISTCREDITS artistCredits;
while (!m_pDS->eof())
{
- CFileItemPtr item(new CFileItem);
- GetFileItemFromDataset(item.get(), baseUrl);
- items.Add(item);
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+
+ if (songId != record->at(song_idSong).get_asInt())
+ { //New song
+ if (songId > 0 && !artistCredits.empty())
+ {
+ //Store artist credits for previous song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size() - 1].get());
+ artistCredits.clear();
+ }
+ songId = record->at(song_idSong).get_asInt();
+ CFileItemPtr item(new CFileItem);
+ GetFileItemFromDataset(record, item.get(), baseUrl);
+ items.Add(item);
+ }
+ // Get song artist credits
+ artistCredits.push_back(GetArtistCreditFromDataset(record, songArtistOffset));
m_pDS->next();
}
+ if (!artistCredits.empty())
+ {
+ //Store artist credits for final song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size() - 1].get());
+ artistCredits.clear();
+ }
// cleanup
m_pDS->close();
@@ -2147,7 +2242,15 @@ bool CMusicDatabase::GetRecentlyAddedAlbums(VECALBUMS& albums, unsigned int limi
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
- std::string strSQL = StringUtils::Format("select * from albumview where strAlbum != '' order by idAlbum desc limit %u", limit ? limit : g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
+ // Get data from album and album_artist tables to fully populate albums
+ // Use idAlbum to determine the recently added albums
+ // (not "dateAdded" as this is file time stamp and nothing to do with when albums added to library)
+ std::string strSQL = PrepareSQL("SELECT albumview.*, albumartistview.* FROM "
+ "(SELECT idAlbum FROM album WHERE strAlbum != '' ORDER BY idAlbum DESC LIMIT %u) AS recentalbums "
+ "JOIN albumview ON albumview.idAlbum = recentalbums.idAlbum "
+ "LEFT JOIN albumartistview ON albumview.idAlbum = albumartistview.idAlbum "
+ "ORDER BY albumview.idAlbum desc, albumartistview.iOrder ",
+ limit ? limit : g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
if (!m_pDS->query(strSQL)) return false;
@@ -2158,12 +2261,22 @@ bool CMusicDatabase::GetRecentlyAddedAlbums(VECALBUMS& albums, unsigned int limi
return true;
}
+ int albumArtistOffset = album_enumCount;
+ int albumId = -1;
while (!m_pDS->eof())
{
- albums.push_back(GetAlbumFromDataset(m_pDS.get()));
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+
+ if (albumId != record->at(album_idAlbum).get_asInt())
+ { // New album
+ albumId = record->at(album_idAlbum).get_asInt();
+ albums.push_back(GetAlbumFromDataset(record));
+ }
+ // Get artist details
+ albums.back().artistCredits.push_back(GetArtistCreditFromDataset(record, albumArtistOffset));
+
m_pDS->next();
}
-
m_pDS->close(); // cleanup recordset data
return true;
}
@@ -2186,8 +2299,16 @@ bool CMusicDatabase::GetRecentlyAddedAlbumSongs(const std::string& strBaseDir, C
if (!strBaseDir.empty() && !baseUrl.FromString(strBaseDir))
return false;
+ // Get data from song and song_artist tables to fully populate songs
+ // Use idAlbum to determine the recently added albums
+ // (not "dateAdded" as this is file time stamp and nothing to do with when albums added to library)
std::string strSQL;
- strSQL = PrepareSQL("SELECT songview.* FROM (SELECT idAlbum FROM albumview ORDER BY idAlbum DESC LIMIT %u) AS recentalbums JOIN songview ON songview.idAlbum=recentalbums.idAlbum", limit ? limit : g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
+ strSQL = PrepareSQL("SELECT songview.*, songartistview.* FROM "
+ "(SELECT idAlbum FROM album ORDER BY idAlbum DESC LIMIT %u) AS recentalbums "
+ "JOIN songview ON songview.idAlbum = recentalbums.idAlbum "
+ "JOIN songartistview ON songview.idSong = songartistview.idSong "
+ "ORDER BY songview.idAlbum desc, songview.itrack, songartistview.iOrder ",
+ limit ? limit : g_advancedSettings.m_iMusicLibraryRecentlyAddedItems);
CLog::Log(LOGDEBUG,"GetRecentlyAddedAlbumSongs() query: %s", strSQL.c_str());
if (!m_pDS->query(strSQL)) return false;
@@ -2198,15 +2319,39 @@ bool CMusicDatabase::GetRecentlyAddedAlbumSongs(const std::string& strBaseDir, C
return true;
}
- // get data from returned rows
- items.Reserve(iRowsFound);
+ // Needs a separate query to determine number of songs to set items size.
+ // Get songs from returned rows. Join means there is a row for every song artist
+ int songArtistOffset = song_enumCount;
+ int songId = -1;
+ VECARTISTCREDITS artistCredits;
while (!m_pDS->eof())
{
- CFileItemPtr item(new CFileItem);
- GetFileItemFromDataset(item.get(), baseUrl);
- items.Add(item);
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+
+ if (songId != record->at(song_idSong).get_asInt())
+ { //New song
+ if (songId > 0 && !artistCredits.empty())
+ {
+ //Store artist credits for previous song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size() - 1].get());
+ artistCredits.clear();
+ }
+ songId = record->at(song_idSong).get_asInt();
+ CFileItemPtr item(new CFileItem);
+ GetFileItemFromDataset(record, item.get(), baseUrl);
+ items.Add(item);
+ }
+ // Get song artist credits
+ artistCredits.push_back(GetArtistCreditFromDataset(record, songArtistOffset));
+
m_pDS->next();
}
+ if (!artistCredits.empty())
+ {
+ //Store artist credits for final song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size() - 1].get());
+ artistCredits.clear();
+ }
// cleanup
m_pDS->close();
@@ -3533,6 +3678,239 @@ bool CMusicDatabase::GetAlbumsByWhere(const std::string &baseDir, const Filter &
return false;
}
+bool CMusicDatabase::GetAlbumsByWhere(const std::string &baseDir, const Filter &filter, VECALBUMS& albums, int& total, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
+{
+ albums.erase(albums.begin(), albums.end());
+ if (NULL == m_pDB.get()) return false;
+ if (NULL == m_pDS.get()) return false;
+
+ try
+ {
+ total = -1;
+ // Get data from album and album_artist tables to fully populate albums
+ std::string strSQL = "SELECT %s FROM albumview LEFT JOIN albumartistview on albumartistview.idalbum = albumview.idalbum ";
+
+ Filter extFilter = filter;
+ CMusicDbUrl musicUrl;
+ SortDescription sorting = sortDescription;
+ if (!musicUrl.FromString(baseDir) || !GetFilter(musicUrl, extFilter, sorting))
+ return false;
+
+ // if there are extra WHERE conditions we might need access
+ // to songview for these conditions
+ if (extFilter.where.find("songview") != std::string::npos)
+ {
+ extFilter.AppendJoin("JOIN songview ON songview.idAlbum = albumview.idAlbum");
+ extFilter.AppendGroup("albumview.idAlbum");
+ }
+
+ std::string strSQLExtra;
+ if (!BuildSQL(strSQLExtra, extFilter, strSQLExtra))
+ return false;
+
+ // Count and return number of albums that satisfy selection criteria
+ total = (int)strtol(GetSingleValue("SELECT COUNT(1) FROM albumview " + strSQLExtra, m_pDS).c_str(), NULL, 10);
+ if (countOnly)
+ return true;
+
+ // Apply the limiting directly here if there's no special sorting but limiting
+ if (extFilter.limit.empty() &&
+ sortDescription.sortBy == SortByNone &&
+ (sortDescription.limitStart > 0 || sortDescription.limitEnd > 0))
+ {
+ strSQLExtra += DatabaseUtils::BuildLimitClause(sortDescription.limitEnd, sortDescription.limitStart);
+ albums.reserve(sortDescription.limitEnd - sortDescription.limitStart);
+ }
+ else
+ albums.reserve(total);
+
+ strSQL = PrepareSQL(strSQL, !filter.fields.empty() && filter.fields.compare("*") != 0 ? filter.fields.c_str() : "albumview.*, albumartistview.* ") + strSQLExtra;
+
+ CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
+ // run query
+ unsigned int time = XbmcThreads::SystemClockMillis();
+ if (!m_pDS->query(strSQL))
+ return false;
+ CLog::Log(LOGDEBUG, "%s - query took %i ms",
+ __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
+
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound <= 0)
+ {
+ m_pDS->close();
+ return true;
+ }
+
+ //Sort the results set - need to add sort by iOrder to maintain artist name order??
+ DatabaseResults results;
+ results.reserve(iRowsFound);
+ if (!SortUtils::SortFromDataset(sortDescription, MediaTypeAlbum, m_pDS, results))
+ return false;
+
+ // Get albums from returned rows. Join means there is a row for every album artist
+ int albumArtistOffset = album_enumCount;
+ int albumId = -1;
+
+ const dbiplus::query_data &data = m_pDS->get_result_set().records;
+ for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); ++it)
+ {
+ unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
+ const dbiplus::sql_record* const record = data.at(targetRow);
+
+ if (albumId != record->at(album_idAlbum).get_asInt())
+ { // New album
+ albumId = record->at(album_idAlbum).get_asInt();
+ albums.push_back(GetAlbumFromDataset(record));
+ }
+ // Get album artist credits
+ albums.back().artistCredits.push_back(GetArtistCreditFromDataset(record, albumArtistOffset));
+ }
+
+ m_pDS->close(); // cleanup recordset data
+ return true;
+ }
+ catch (...)
+ {
+ m_pDS->close();
+ CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filter.where.c_str());
+ }
+ return false;
+}
+
+bool CMusicDatabase::GetSongsFullByWhere(const std::string &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription /* = SortDescription() */, bool artistData /* = false*/)
+{
+ if (m_pDB.get() == NULL || m_pDS.get() == NULL)
+ return false;
+
+ try
+ {
+ unsigned int time = XbmcThreads::SystemClockMillis();
+ int total = -1;
+
+ std::string strSQL = "SELECT %s FROM songview ";
+ if (artistData) // Get data from song and song_artist tables to fully populate songs with artists
+ strSQL = "SELECT %s FROM songview JOIN songartistview on songartistview.idsong = songview.idsong ";
+
+ Filter extFilter = filter;
+ CMusicDbUrl musicUrl;
+ SortDescription sorting = sortDescription;
+ if (!musicUrl.FromString(baseDir) || !GetFilter(musicUrl, extFilter, sorting))
+ return false;
+
+ // if there are extra WHERE conditions we might need access
+ // to songview for these conditions
+ if (extFilter.where.find("albumview") != std::string::npos)
+ {
+ extFilter.AppendJoin("JOIN albumview ON albumview.idAlbum = songview.idAlbum");
+ extFilter.AppendGroup("songview.idSong");
+ }
+
+ std::string strSQLExtra;
+ if (!BuildSQL(strSQLExtra, extFilter, strSQLExtra))
+ return false;
+
+ // Count number of songs that satisfy selection criteria
+ total = (int)strtol(GetSingleValue("SELECT COUNT(1) FROM songview " + strSQLExtra, m_pDS).c_str(), NULL, 10);
+
+ // Apply the limiting directly here if there's no special sorting but limiting
+ if (extFilter.limit.empty() &&
+ sortDescription.sortBy == SortByNone &&
+ (sortDescription.limitStart > 0 || sortDescription.limitEnd > 0))
+ strSQLExtra += DatabaseUtils::BuildLimitClause(sortDescription.limitEnd, sortDescription.limitStart);
+
+ if (artistData)
+ strSQL = PrepareSQL(strSQL, !filter.fields.empty() && filter.fields.compare("*") != 0 ? filter.fields.c_str() : "songview.*, songartistview.* ") + strSQLExtra;
+ else
+ strSQL = PrepareSQL(strSQL, !filter.fields.empty() && filter.fields.compare("*") != 0 ? filter.fields.c_str() : "songview.* ") + strSQLExtra;
+
+ CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
+ // run query
+ if (!m_pDS->query(strSQL))
+ return false;
+
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound == 0)
+ {
+ m_pDS->close();
+ return true;
+ }
+
+ // Store the total number of songs as a property
+ items.SetProperty("total", total);
+
+ DatabaseResults results;
+ results.reserve(iRowsFound);
+ if (!SortUtils::SortFromDataset(sortDescription, MediaTypeSong, m_pDS, results))
+ return false;
+
+ // Get songs from returned rows. If join songartistview then there is a row for every album artist
+ items.Reserve(total);
+ int songArtistOffset = song_enumCount;
+ int songId = -1;
+ VECARTISTCREDITS artistCredits;
+ const dbiplus::query_data &data = m_pDS->get_result_set().records;
+ int count = 0;
+ for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); ++it)
+ {
+ unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
+ const dbiplus::sql_record* const record = data.at(targetRow);
+
+ try
+ {
+ if (songId != record->at(song_idSong).get_asInt())
+ { //New song
+ if (songId > 0 && !artistCredits.empty())
+ {
+ //Store artist credits for previous song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size()-1].get());
+ artistCredits.clear();
+ }
+ songId = record->at(song_idSong).get_asInt();
+ CFileItemPtr item(new CFileItem);
+ GetFileItemFromDataset(record, item.get(), musicUrl);
+ // HACK for sorting by database returned order
+ item->m_iprogramCount = ++count;
+ items.Add(item);
+ }
+ // Get song artist credits
+ if (artistData)
+ artistCredits.push_back(GetArtistCreditFromDataset(record, songArtistOffset));
+ }
+ catch (...)
+ {
+ m_pDS->close();
+ CLog::Log(LOGERROR, "%s: out of memory loading query: %s", __FUNCTION__, filter.where.c_str());
+ return (items.Size() > 0);
+ }
+
+
+ }
+ if (!artistCredits.empty())
+ {
+ //Store artist credits for final song
+ GetFileItemFromArtistCredits(artistCredits, items[items.Size() - 1].get());
+ artistCredits.clear();
+ }
+ // cleanup
+ m_pDS->close();
+
+ // Load some info from embedded cuesheet if present (now only ReplayGain)
+ CueInfoLoader cueLoader;
+ for (int i = 0; i < items.Size(); ++i)
+ cueLoader.Load(LoadCuesheet(items[i]->GetMusicInfoTag()->GetURL()), items[i]);
+
+ CLog::Log(LOGDEBUG, "%s(%s) - took %d ms", __FUNCTION__, filter.where.c_str(), XbmcThreads::SystemClockMillis() - time);
+ return true;
+ }
+ catch (...)
+ {
+ // cleanup
+ m_pDS->close();
+ CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, filter.where.c_str());
+ }
+ return false;
+}
+
bool CMusicDatabase::GetSongsByWhere(const std::string &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription /* = SortDescription() */)
{
if (m_pDB.get() == NULL || m_pDS.get() == NULL)
diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h
index 1f07d42e97..b16cb6c4e5 100644
--- a/xbmc/music/MusicDatabase.h
+++ b/xbmc/music/MusicDatabase.h
@@ -293,7 +293,7 @@ public:
/////////////////////////////////////////////////
bool AddAlbumArtist(int idArtist, int idAlbum, std::string strArtist, std::string joinPhrase, bool featured, int iOrder);
bool GetAlbumsByArtist(int idArtist, bool includeFeatured, std::vector<int>& albums);
- bool GetArtistsByAlbum(int idAlbum, bool includeFeatured, std::vector<int>& artists);
+ bool GetArtistsByAlbum(int idAlbum, CFileItem* item);
bool DeleteAlbumArtistsByAlbum(int idAlbum);
bool AddSongArtist(int idArtist, int idSong, std::string strArtist, std::string joinPhrase, bool featured, int iOrder);
@@ -354,7 +354,9 @@ public:
bool GetSongsNav(const std::string& strBaseDir, CFileItemList& items, int idGenre, int idArtist,int idAlbum, const SortDescription &sortDescription = SortDescription());
bool GetSongsByYear(const std::string& baseDir, CFileItemList& items, int year);
bool GetSongsByWhere(const std::string &baseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription());
+ bool GetSongsFullByWhere(const std::string &baseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription(), bool artistData = false);
bool GetAlbumsByWhere(const std::string &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
+ bool GetAlbumsByWhere(const std::string &baseDir, const Filter &filter, VECALBUMS& albums, int& total, const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
bool GetArtistsByWhere(const std::string& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
bool GetRandomSong(CFileItem* item, int& idSong, const Filter &filter);
int GetSongsCount(const Filter &filter = Filter());
@@ -493,6 +495,7 @@ private:
void UpdateFileDateAdded(int songId, const std::string& strFileNameAndPath);
void GetFileItemFromDataset(CFileItem* item, const CMusicDbUrl &baseUrl);
void GetFileItemFromDataset(const dbiplus::sql_record* const record, CFileItem* item, const CMusicDbUrl &baseUrl);
+ void GetFileItemFromArtistCredits(VECARTISTCREDITS& artistCredits, CFileItem* item);
CSong GetAlbumInfoSongFromDataset(const dbiplus::sql_record* const record, int offset = 0);
bool CleanupSongs();
bool CleanupSongsByIds(const std::string &strSongIds);
diff --git a/xbmc/music/Song.cpp b/xbmc/music/Song.cpp
index 50cf0f6c00..01ea30accb 100644
--- a/xbmc/music/Song.cpp
+++ b/xbmc/music/Song.cpp
@@ -162,11 +162,6 @@ const std::vector<std::string> CSong::GetArtist() const
{
songartists.push_back(artistCredit->GetArtist());
}
- //When artist credits have not been populated attempt to build an artist vector from the descrpition string
- //This is a tempory fix, in the longer term other areas should query the song_artist table and populate
- //artist credits. Note that splitting the string may not give the same artists as held in the song_artist table
- if (songartists.empty() && !strArtistDesc.empty())
- songartists = StringUtils::Split(strArtistDesc, g_advancedSettings.m_musicItemSeparator);
return songartists;
}
@@ -193,6 +188,15 @@ const std::string CSong::GetArtistString() const
return artistString;
}
+const std::vector<int> CSong::GetArtistIDArray() const
+{
+ // Get song artist IDs for json rpc
+ std::vector<int> artistids;
+ for (VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin(); artistCredit != artistCredits.end(); ++artistCredit)
+ artistids.push_back(artistCredit->GetArtistId());
+ return artistids;
+}
+
bool CSong::HasArt() const
{
if (!strThumb.empty()) return true;
diff --git a/xbmc/music/Song.h b/xbmc/music/Song.h
index 14b32bc47b..5caf404ee0 100644
--- a/xbmc/music/Song.h
+++ b/xbmc/music/Song.h
@@ -88,6 +88,11 @@ public:
*/
const std::string GetArtistString() const;
+ /*! \brief Get song artist IDs (for json rpc) from the vector of artistcredits objects
+ \return album artist IDs as a vector of integers
+ */
+ const std::vector<int> GetArtistIDArray() const;
+
/*! \brief Get album artist names associated with song from tag data
Note for initial album processing only, normalised album artist data belongs to album
and is stored in album artist credits
diff --git a/xbmc/music/tags/MusicInfoTag.cpp b/xbmc/music/tags/MusicInfoTag.cpp
index 08ac5f0f26..fc8dfbe366 100644
--- a/xbmc/music/tags/MusicInfoTag.cpp
+++ b/xbmc/music/tags/MusicInfoTag.cpp
@@ -706,9 +706,9 @@ void CMusicInfoTag::Serialize(CVariant& value) const
value["loaded"] = m_bLoaded;
value["year"] = m_dwReleaseDate.wYear;
value["musicbrainztrackid"] = m_strMusicBrainzTrackID;
- value["musicbrainzartistid"] = StringUtils::Join(m_musicBrainzArtistID, " / ");
+ value["musicbrainzartistid"] = m_musicBrainzArtistID;
value["musicbrainzalbumid"] = m_strMusicBrainzAlbumID;
- value["musicbrainzalbumartistid"] = StringUtils::Join(m_musicBrainzAlbumArtistID, " / ");
+ value["musicbrainzalbumartistid"] = m_musicBrainzAlbumArtistID;
value["musicbrainztrmid"] = m_strMusicBrainzTRMID;
value["comment"] = m_strComment;
value["mood"] = m_strMood;
@@ -738,9 +738,9 @@ void CMusicInfoTag::ToSortable(SortItem& sortable, Field field) const
sortable[FieldTitle] = title;
break;
}
- case FieldArtist: sortable[FieldArtist] = m_artist; break;
+ case FieldArtist: sortable[FieldArtist] = m_strArtistDesc; break;
case FieldAlbum: sortable[FieldAlbum] = m_strAlbum; break;
- case FieldAlbumArtist: sortable[FieldAlbumArtist] = m_albumArtist; break;
+ case FieldAlbumArtist: sortable[FieldAlbumArtist] = m_strAlbumArtistDesc; break;
case FieldGenre: sortable[FieldGenre] = m_genre; break;
case FieldTime: sortable[FieldTime] = m_iDuration; break;
case FieldTrackNumber: sortable[FieldTrackNumber] = m_iTrack; break;
diff --git a/xbmc/music/tags/TagLoaderTagLib.cpp b/xbmc/music/tags/TagLoaderTagLib.cpp
index 1ccd36ab17..7c5c51e115 100644
--- a/xbmc/music/tags/TagLoaderTagLib.cpp
+++ b/xbmc/music/tags/TagLoaderTagLib.cpp
@@ -25,6 +25,7 @@
#include <taglib/id3v1tag.h>
#include <taglib/id3v2tag.h>
#include <taglib/apetag.h>
+#include <taglib/asftag.h>
#include <taglib/xiphcomment.h>
#include <taglib/id3v1genres.h>
@@ -76,7 +77,6 @@ CTagLoaderTagLib::CTagLoaderTagLib()
CTagLoaderTagLib::~CTagLoaderTagLib()
{
-
}
static const std::vector<std::string> StringListToVectorString(const StringList& stringList)
@@ -92,177 +92,9 @@ bool CTagLoaderTagLib::Load(const std::string& strFileName, MUSIC_INFO::CMusicIn
return Load(strFileName, tag, "", art);
}
-bool CTagLoaderTagLib::Load(const std::string& strFileName, CMusicInfoTag& tag, const std::string& fallbackFileExtension, MUSIC_INFO::EmbeddedArt *art /* = NULL */)
-{
- std::string strExtension = URIUtils::GetExtension(strFileName);
- StringUtils::ToLower(strExtension);
- StringUtils::TrimLeft(strExtension, ".");
-
- if (strExtension.empty())
- {
- strExtension = fallbackFileExtension;
- if (strExtension.empty())
- return false;
- StringUtils::ToLower(strExtension);
- }
-
- TagLibVFSStream* stream = new TagLibVFSStream(strFileName, true);
- if (!stream)
- {
- CLog::Log(LOGERROR, "could not create TagLib VFS stream for: %s", strFileName.c_str());
- return false;
- }
-
- ID3v1::Tag::setStringHandler(&ID3v1StringHandler);
- ID3v2::Tag::setLatin1StringHandler(&ID3v2StringHandler);
- TagLib::File* file = NULL;
- TagLib::APE::File* apeFile = NULL;
- TagLib::ASF::File* asfFile = NULL;
- TagLib::FLAC::File* flacFile = NULL;
- TagLib::IT::File* itFile = NULL;
- TagLib::Mod::File* modFile = NULL;
- TagLib::MP4::File* mp4File = NULL;
- TagLib::MPC::File* mpcFile = NULL;
- TagLib::MPEG::File* mpegFile = NULL;
- TagLib::Ogg::Vorbis::File* oggVorbisFile = NULL;
- TagLib::Ogg::FLAC::File* oggFlacFile = NULL;
- TagLib::S3M::File* s3mFile = NULL;
- TagLib::TrueAudio::File* ttaFile = NULL;
- TagLib::WavPack::File* wvFile = NULL;
- TagLib::XM::File* xmFile = NULL;
- TagLib::RIFF::WAV::File * wavFile = NULL;
- TagLib::RIFF::AIFF::File * aiffFile = NULL;
-
- if (strExtension == "ape")
- file = apeFile = new APE::File(stream);
- else if (strExtension == "asf" || strExtension == "wmv" || strExtension == "wma")
- file = asfFile = new ASF::File(stream);
- else if (strExtension == "flac")
- file = flacFile = new FLAC::File(stream, ID3v2::FrameFactory::instance());
- else if (strExtension == "it")
- file = itFile = new IT::File(stream);
- else if (strExtension == "mod" || strExtension == "module" || strExtension == "nst" || strExtension == "wow")
- file = modFile = new Mod::File(stream);
- else if (strExtension == "mp4" || strExtension == "m4a" ||
- strExtension == "m4r" || strExtension == "m4b" ||
- strExtension == "m4p" || strExtension == "3g2")
- file = mp4File = new MP4::File(stream);
- else if (strExtension == "mpc")
- file = mpcFile = new MPC::File(stream);
- else if (strExtension == "mp3" || strExtension == "aac")
- file = mpegFile = new MPEG::File(stream, ID3v2::FrameFactory::instance());
- else if (strExtension == "s3m")
- file = s3mFile = new S3M::File(stream);
- else if (strExtension == "tta")
- file = ttaFile = new TrueAudio::File(stream, ID3v2::FrameFactory::instance());
- else if (strExtension == "wv")
- file = wvFile = new WavPack::File(stream);
- else if (strExtension == "aif" || strExtension == "aiff")
- file = aiffFile = new RIFF::AIFF::File(stream);
- else if (strExtension == "wav")
- file = wavFile = new RIFF::WAV::File(stream);
- else if (strExtension == "xm")
- file = xmFile = new XM::File(stream);
- else if (strExtension == "ogg")
- file = oggVorbisFile = new Ogg::Vorbis::File(stream);
- else if (strExtension == "oga") // Leave this madness until last - oga container can have Vorbis or FLAC
- {
- file = oggFlacFile = new Ogg::FLAC::File(stream);
- if (!file || !file->isValid())
- {
- delete file;
- oggFlacFile = NULL;
- file = oggVorbisFile = new Ogg::Vorbis::File(stream);
- }
- }
-
- if (!file || !file->isOpen())
- {
- delete file;
- delete stream;
- CLog::Log(LOGDEBUG, "file could not be opened for tag reading");
- return false;
- }
-
- APE::Tag *ape = NULL;
- ASF::Tag *asf = NULL;
- MP4::Tag *mp4 = NULL;
- ID3v1::Tag *id3v1 = NULL;
- ID3v2::Tag *id3v2 = NULL;
- Ogg::XiphComment *xiph = NULL;
- Tag *generic = NULL;
-
- if (apeFile)
- ape = apeFile->APETag(false);
- else if (asfFile)
- asf = asfFile->tag();
- else if (flacFile)
- {
- xiph = flacFile->xiphComment(false);
- id3v2 = flacFile->ID3v2Tag(false);
- }
- else if (mp4File)
- mp4 = mp4File->tag();
- else if (mpegFile)
- {
- id3v1 = mpegFile->ID3v1Tag(false);
- id3v2 = mpegFile->ID3v2Tag(false);
- ape = mpegFile->APETag(false);
- }
- else if (oggFlacFile)
- xiph = dynamic_cast<Ogg::XiphComment *>(oggFlacFile->tag());
- else if (oggVorbisFile)
- xiph = dynamic_cast<Ogg::XiphComment *>(oggVorbisFile->tag());
- else if (ttaFile)
- id3v2 = ttaFile->ID3v2Tag(false);
- else if (aiffFile)
- id3v2 = aiffFile->tag();
- else if (wavFile)
-#if TAGLIB_MAJOR_VERSION > 1 || TAGLIB_MINOR_VERSION > 8
- id3v2 = wavFile->ID3v2Tag();
-#else
- id3v2 = wavFile->tag();
-#endif
- else if (wvFile)
- ape = wvFile->APETag(false);
- else if (mpcFile)
- ape = mpcFile->APETag(false);
- else // This is a catch all to get generic information for other files types (s3m, xm, it, mod, etc)
- generic = file->tag();
- if (file->audioProperties())
- tag.SetDuration(file->audioProperties()->length());
-
- if (asf)
- ParseASF(asf, art, tag);
- if (id3v1)
- ParseID3v1Tag(id3v1, art, tag);
- if (id3v2)
- ParseID3v2Tag(id3v2, art, tag);
- if (generic)
- ParseGenericTag(generic, art, tag);
- if (mp4)
- ParseMP4Tag(mp4, art, tag);
- if (xiph) // xiph tags override id3v2 tags in badly tagged FLACs
- ParseXiphComment(xiph, art, tag);
- if (ape && (!id3v2 || g_advancedSettings.m_prioritiseAPEv2tags)) // ape tags override id3v2 if we're prioritising them
- ParseAPETag(ape, art, tag);
-
- // art for flac files is outside the tag
- if (flacFile)
- SetFlacArt(flacFile, art, tag);
-
- if (!tag.GetTitle().empty() || !tag.GetArtist().empty() || !tag.GetAlbum().empty())
- tag.SetLoaded();
- tag.SetURL(strFileName);
-
- delete file;
- delete stream;
-
- return true;
-}
-
-bool CTagLoaderTagLib::ParseASF(ASF::Tag *asf, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(ASF::Tag *asf, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!asf)
return false;
@@ -338,12 +170,14 @@ bool CTagLoaderTagLib::ParseASF(ASF::Tag *asf, EmbeddedArt *art, CMusicInfoTag&
if (tag.GetArtist().empty())
tag.SetArtist(asf->artist().toCString(true));
+ if (asf->comment() != String::null)
+ tag.SetComment(asf->comment().toCString(true));
tag.SetReplayGain(replayGainInfo);
tag.SetLoaded(true);
return true;
}
-char POPMtoXBMC(int popm)
+char CTagLoaderTagLib::POPMtoXBMC(int popm)
{
// Ratings:
// FROM: http://thiagoarrais.com/repos/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamRatingTagger.cs
@@ -364,7 +198,8 @@ char POPMtoXBMC(int popm)
return '5';
}
-bool CTagLoaderTagLib::ParseID3v1Tag(ID3v1::Tag *id3v1, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(ID3v1::Tag *id3v1, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!id3v1) return false;
tag.SetTitle(id3v1->title().to8Bit(true));
@@ -377,17 +212,20 @@ bool CTagLoaderTagLib::ParseID3v1Tag(ID3v1::Tag *id3v1, EmbeddedArt *art, CMusic
return true;
}
-bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(ID3v2::Tag *id3v2, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag)
{
- // tag.SetURL(strFile);
if (!id3v2) return false;
-
ReplayGain replayGainInfo;
ID3v2::AttachedPictureFrame *pictures[3] = {};
const ID3v2::FrameListMap& frameListMap = id3v2->frameListMap();
for (ID3v2::FrameListMap::ConstIterator it = frameListMap.begin(); it != frameListMap.end(); ++it)
{
+ // It is possible that the taglist is empty. In that case no useable values can be extracted.
+ // and we should skip the tag.
+ if (it->second.isEmpty()) continue;
+
if (it->first == "TPE1") SetArtist(tag, GetID3v2StringList(it->second));
else if (it->first == "TALB") tag.SetAlbum(it->second.front()->toString().to8Bit(true));
else if (it->first == "TPE2") SetAlbumArtist(tag, GetID3v2StringList(it->second));
@@ -409,7 +247,7 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
for (ID3v2::FrameList::ConstIterator lt = it->second.begin(); lt != it->second.end(); ++lt)
{
ID3v2::UnsynchronizedLyricsFrame *lyricsFrame = dynamic_cast<ID3v2::UnsynchronizedLyricsFrame *> (*lt);
- if (lyricsFrame)
+ if (lyricsFrame)
tag.SetLyrics(lyricsFrame->text().to8Bit(true));
}
else if (it->first == "COMM")
@@ -426,9 +264,10 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
{
ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<ID3v2::UserTextIdentificationFrame *> (*ut);
if (!frame) continue;
-
+
// First field is the same as the description
- StringList stringList = frame->fieldList();
+ StringList stringList = frame->fieldList();
+ if (stringList.size() == 1) continue;
stringList.erase(stringList.begin());
String desc = frame->description().upper();
if (desc == "MUSICBRAINZ ARTIST ID")
@@ -489,7 +328,7 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
{
ID3v2::AttachedPictureFrame *pictureFrame = dynamic_cast<ID3v2::AttachedPictureFrame *> (*pi);
if (!pictureFrame) continue;
-
+
if (pictureFrame->type() == ID3v2::AttachedPictureFrame::FrontCover) pictures[0] = pictureFrame;
else if (pictureFrame->type() == ID3v2::AttachedPictureFrame::Other) pictures[1] = pictureFrame;
else if (pi == it->second.begin()) pictures[2] = pictureFrame;
@@ -500,7 +339,7 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
{
ID3v2::PopularimeterFrame *popFrame = dynamic_cast<ID3v2::PopularimeterFrame *> (*ct);
if (!popFrame) continue;
-
+
// @xbmc.org ratings trump others (of course)
if (popFrame->email() == "ratings@xbmc.org")
tag.SetUserrating(popFrame->rating() / 51 + '0');
@@ -528,16 +367,21 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
tag.SetCoverArtInfo(size, mime);
if (art)
art->set((const uint8_t*)pictures[i]->picture().data(), size, mime);
-
+
// Stop after we find the first picture for now.
break;
}
+
+ if (id3v2->comment() != String::null)
+ tag.SetComment(id3v2->comment().toCString(true));
+
tag.SetReplayGain(replayGainInfo);
return true;
}
-bool CTagLoaderTagLib::ParseAPETag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!ape)
return false;
@@ -602,7 +446,8 @@ bool CTagLoaderTagLib::ParseAPETag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTa
return true;
}
-bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(Ogg::XiphComment *xiph, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!xiph)
return false;
@@ -685,7 +530,7 @@ bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art
if (pictureFrame->type() == FLAC::Picture::FrontCover) pictures[0].parse(bv);
else if (pictureFrame->type() == FLAC::Picture::Other) pictures[1].parse(bv);
-
+
delete pictureFrame;
}
else if (it->first == "COVERART")
@@ -721,11 +566,15 @@ bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art
break;
}
+ if (xiph->comment() != String::null)
+ tag.SetComment(xiph->comment().toCString(true));
+
tag.SetReplayGain(replayGainInfo);
return true;
}
-bool CTagLoaderTagLib::ParseMP4Tag(MP4::Tag *mp4, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(MP4::Tag *mp4, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!mp4)
return false;
@@ -803,11 +652,15 @@ bool CTagLoaderTagLib::ParseMP4Tag(MP4::Tag *mp4, EmbeddedArt *art, CMusicInfoTa
}
}
+ if (mp4->comment() != String::null)
+ tag.SetComment(mp4->comment().toCString(true));
+
tag.SetReplayGain(replayGainInfo);
return true;
}
-bool CTagLoaderTagLib::ParseGenericTag(Tag *generic, EmbeddedArt *art, CMusicInfoTag& tag)
+template<>
+bool CTagLoaderTagLib::ParseTag(Tag *generic, EmbeddedArt *art, CMusicInfoTag& tag)
{
if (!generic)
return false;
@@ -866,7 +719,7 @@ const std::vector<std::string> CTagLoaderTagLib::GetASFStringList(const List<ASF
return values;
}
-const std::vector<std::string> CTagLoaderTagLib::GetID3v2StringList(const ID3v2::FrameList& frameList) const
+const std::vector<std::string> CTagLoaderTagLib::GetID3v2StringList(const ID3v2::FrameList& frameList)
{
const ID3v2::TextIdentificationFrame *frame = dynamic_cast<ID3v2::TextIdentificationFrame *>(frameList.front());
if (frame)
@@ -874,6 +727,7 @@ const std::vector<std::string> CTagLoaderTagLib::GetID3v2StringList(const ID3v2:
return std::vector<std::string>();
}
+
void CTagLoaderTagLib::SetArtist(CMusicInfoTag &tag, const std::vector<std::string> &values)
{
if (values.size() == 1)
@@ -933,3 +787,172 @@ void CTagLoaderTagLib::SetGenre(CMusicInfoTag &tag, const std::vector<std::strin
else
tag.SetGenre(genres);
}
+
+bool CTagLoaderTagLib::Load(const std::string& strFileName, CMusicInfoTag& tag, const std::string& fallbackFileExtension, MUSIC_INFO::EmbeddedArt *art /* = NULL */)
+{
+ std::string strExtension = URIUtils::GetExtension(strFileName);
+ StringUtils::TrimLeft(strExtension, ".");
+
+ if (strExtension.empty())
+ {
+ strExtension = fallbackFileExtension;
+ if (strExtension.empty())
+ return false;
+ }
+
+ StringUtils::ToLower(strExtension);
+ TagLibVFSStream* stream = new TagLibVFSStream(strFileName, true);
+ if (!stream)
+ {
+ CLog::Log(LOGERROR, "could not create TagLib VFS stream for: %s", strFileName.c_str());
+ return false;
+ }
+
+ ID3v1::Tag::setStringHandler(&ID3v1StringHandler);
+ ID3v2::Tag::setLatin1StringHandler(&ID3v2StringHandler);
+ TagLib::File* file = NULL;
+ TagLib::APE::File* apeFile = NULL;
+ TagLib::ASF::File* asfFile = NULL;
+ TagLib::FLAC::File* flacFile = NULL;
+ TagLib::IT::File* itFile = NULL;
+ TagLib::Mod::File* modFile = NULL;
+ TagLib::MP4::File* mp4File = NULL;
+ TagLib::MPC::File* mpcFile = NULL;
+ TagLib::MPEG::File* mpegFile = NULL;
+ TagLib::Ogg::Vorbis::File* oggVorbisFile = NULL;
+ TagLib::Ogg::FLAC::File* oggFlacFile = NULL;
+ TagLib::S3M::File* s3mFile = NULL;
+ TagLib::TrueAudio::File* ttaFile = NULL;
+ TagLib::WavPack::File* wvFile = NULL;
+ TagLib::XM::File* xmFile = NULL;
+ TagLib::RIFF::WAV::File * wavFile = NULL;
+ TagLib::RIFF::AIFF::File * aiffFile = NULL;
+
+ if (strExtension == "ape")
+ file = apeFile = new APE::File(stream);
+ else if (strExtension == "asf" || strExtension == "wmv" || strExtension == "wma")
+ file = asfFile = new ASF::File(stream);
+ else if (strExtension == "flac")
+ file = flacFile = new FLAC::File(stream, ID3v2::FrameFactory::instance());
+ else if (strExtension == "it")
+ file = itFile = new IT::File(stream);
+ else if (strExtension == "mod" || strExtension == "module" || strExtension == "nst" || strExtension == "wow")
+ file = modFile = new Mod::File(stream);
+ else if (strExtension == "mp4" || strExtension == "m4a" ||
+ strExtension == "m4r" || strExtension == "m4b" ||
+ strExtension == "m4p" || strExtension == "3g2")
+ file = mp4File = new MP4::File(stream);
+ else if (strExtension == "mpc")
+ file = mpcFile = new MPC::File(stream);
+ else if (strExtension == "mp3" || strExtension == "aac")
+ file = mpegFile = new MPEG::File(stream, ID3v2::FrameFactory::instance());
+ else if (strExtension == "s3m")
+ file = s3mFile = new S3M::File(stream);
+ else if (strExtension == "tta")
+ file = ttaFile = new TrueAudio::File(stream, ID3v2::FrameFactory::instance());
+ else if (strExtension == "wv")
+ file = wvFile = new WavPack::File(stream);
+ else if (strExtension == "aif" || strExtension == "aiff")
+ file = aiffFile = new RIFF::AIFF::File(stream);
+ else if (strExtension == "wav")
+ file = wavFile = new RIFF::WAV::File(stream);
+ else if (strExtension == "xm")
+ file = xmFile = new XM::File(stream);
+ else if (strExtension == "ogg")
+ file = oggVorbisFile = new Ogg::Vorbis::File(stream);
+ else if (strExtension == "oga") // Leave this madness until last - oga container can have Vorbis or FLAC
+ {
+ file = oggFlacFile = new Ogg::FLAC::File(stream);
+ if (!file || !file->isValid())
+ {
+ delete file;
+ oggFlacFile = NULL;
+ file = oggVorbisFile = new Ogg::Vorbis::File(stream);
+ }
+ }
+
+ if (!file || !file->isOpen())
+ {
+ delete file;
+ delete stream;
+ CLog::Log(LOGDEBUG, "file could not be opened for tag reading");
+ return false;
+ }
+
+ APE::Tag *ape = NULL;
+ ASF::Tag *asf = NULL;
+ MP4::Tag *mp4 = NULL;
+ ID3v1::Tag *id3v1 = NULL;
+ ID3v2::Tag *id3v2 = NULL;
+ Ogg::XiphComment *xiph = NULL;
+ Tag *generic = NULL;
+
+ if (apeFile)
+ ape = apeFile->APETag(false);
+ else if (asfFile)
+ asf = asfFile->tag();
+ else if (flacFile)
+ {
+ xiph = flacFile->xiphComment(false);
+ id3v2 = flacFile->ID3v2Tag(false);
+ }
+ else if (mp4File)
+ mp4 = mp4File->tag();
+ else if (mpegFile)
+ {
+ id3v1 = mpegFile->ID3v1Tag(false);
+ id3v2 = mpegFile->ID3v2Tag(false);
+ ape = mpegFile->APETag(false);
+ }
+ else if (oggFlacFile)
+ xiph = dynamic_cast<Ogg::XiphComment *>(oggFlacFile->tag());
+ else if (oggVorbisFile)
+ xiph = dynamic_cast<Ogg::XiphComment *>(oggVorbisFile->tag());
+ else if (ttaFile)
+ id3v2 = ttaFile->ID3v2Tag(false);
+ else if (aiffFile)
+ id3v2 = aiffFile->tag();
+ else if (wavFile)
+#if TAGLIB_MAJOR_VERSION > 1 || TAGLIB_MINOR_VERSION > 8
+ id3v2 = wavFile->ID3v2Tag();
+#else
+ id3v2 = wavFile->tag();
+#endif
+ else if (wvFile)
+ ape = wvFile->APETag(false);
+ else if (mpcFile)
+ ape = mpcFile->APETag(false);
+ else // This is a catch all to get generic information for other files types (s3m, xm, it, mod, etc)
+ generic = file->tag();
+
+ if (file->audioProperties())
+ tag.SetDuration(file->audioProperties()->length());
+
+ if (asf)
+ ParseTag(asf, art, tag);
+ if (id3v1)
+ ParseTag(id3v1, art, tag);
+ if (id3v2)
+ ParseTag(id3v2, art, tag);
+ if (generic)
+ ParseTag(generic, art, tag);
+ if (mp4)
+ ParseTag(mp4, art, tag);
+ if (xiph) // xiph tags override id3v2 tags in badly tagged FLACs
+ ParseTag(xiph, art, tag);
+ if (ape && (!id3v2 || g_advancedSettings.m_prioritiseAPEv2tags)) // ape tags override id3v2 if we're prioritising them
+ ParseTag(ape, art, tag);
+
+ // art for flac files is outside the tag
+ if (flacFile)
+ SetFlacArt(flacFile, art, tag);
+
+ if (!tag.GetTitle().empty() || !tag.GetArtist().empty() || !tag.GetAlbum().empty())
+ tag.SetLoaded();
+ tag.SetURL(strFileName);
+
+ delete file;
+ delete stream;
+
+ return true;
+}
diff --git a/xbmc/music/tags/TagLoaderTagLib.h b/xbmc/music/tags/TagLoaderTagLib.h
index b648e7b82e..de41c0b4e1 100644
--- a/xbmc/music/tags/TagLoaderTagLib.h
+++ b/xbmc/music/tags/TagLoaderTagLib.h
@@ -56,24 +56,21 @@ public:
CTagLoaderTagLib();
virtual ~CTagLoaderTagLib();
virtual bool Load(const std::string& strFileName, MUSIC_INFO::CMusicInfoTag& tag, MUSIC_INFO::EmbeddedArt *art = NULL);
-
bool Load(const std::string& strFileName, MUSIC_INFO::CMusicInfoTag& tag, const std::string& fallbackFileExtension, MUSIC_INFO::EmbeddedArt *art = NULL);
- const std::vector<std::string> SplitMBID(const std::vector<std::string> &values);
+ static const std::vector<std::string> SplitMBID(const std::vector<std::string> &values);
+protected:
+ static void SetArtist(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
+ static void SetAlbumArtist(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
+ static void SetGenre(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
+ static char POPMtoXBMC(int popm);
+
+template<typename T>
+ static bool ParseTag(T *tag, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& infoTag);
private:
bool Open(const std::string& strFileName, bool readOnly);
- const std::vector<std::string> GetASFStringList(const TagLib::List<TagLib::ASF::Attribute>& list);
- const std::vector<std::string> GetID3v2StringList(const TagLib::ID3v2::FrameList& frameList) const;
-
- bool ParseAPETag(TagLib::APE::Tag *ape, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseASF(TagLib::ASF::Tag *asf, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseID3v1Tag(TagLib::ID3v1::Tag *id3v1, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseID3v2Tag(TagLib::ID3v2::Tag *id3v2, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseXiphComment(TagLib::Ogg::XiphComment *id3v2, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseMP4Tag(TagLib::MP4::Tag *mp4, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
- bool ParseGenericTag(TagLib::Tag *generic, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& tag);
+ static const std::vector<std::string> GetASFStringList(const TagLib::List<TagLib::ASF::Attribute>& list);
+ static const std::vector<std::string> GetID3v2StringList(const TagLib::ID3v2::FrameList& frameList);
void SetFlacArt(TagLib::FLAC::File *flacFile, MUSIC_INFO::EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag &tag);
- void SetArtist(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
- void SetAlbumArtist(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
- void SetGenre(MUSIC_INFO::CMusicInfoTag &tag, const std::vector<std::string> &values);
};
+
diff --git a/xbmc/music/tags/test/TestTagLoaderTagLib.cpp b/xbmc/music/tags/test/TestTagLoaderTagLib.cpp
index be0f012d0d..5ef462611d 100644
--- a/xbmc/music/tags/test/TestTagLoaderTagLib.cpp
+++ b/xbmc/music/tags/test/TestTagLoaderTagLib.cpp
@@ -20,6 +20,154 @@
#include "gtest/gtest.h"
#include "music/tags/TagLoaderTagLib.h"
+#include "music/tags/MusicInfoTag.h"
+#include <taglib/tpropertymap.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/apetag.h>
+#include <taglib/xiphcomment.h>
+#include <taglib/id3v1genres.h>
+
+using namespace TagLib;
+using namespace MUSIC_INFO;
+
+template <typename T>
+class TestTagParser : public ::testing::Test, public CTagLoaderTagLib {
+ public:
+ T value_;
+};
+
+
+typedef ::testing::Types<ID3v2::Tag, ID3v1::Tag, ASF::Tag, APE::Tag, Ogg::XiphComment, MP4::Tag> TagTypes;
+TYPED_TEST_CASE(TestTagParser, TagTypes);
+
+TYPED_TEST(TestTagParser, ParsesBasicTag) {
+ // Create a basic tag
+ TypeParam *tg = &this->value_;
+ // Configure a basic tag..
+ tg->setTitle ("title");
+ tg->setArtist ("artist");
+ tg->setAlbum ("album");
+ tg->setComment("comment");
+ tg->setGenre("Jazz");
+ tg->setYear (1985);
+ tg->setTrack (2);
+
+ CMusicInfoTag tag;
+ EXPECT_TRUE(CTagLoaderTagLib::ParseTag<TypeParam>(tg, NULL, tag));
+
+ EXPECT_EQ(1985, tag.GetYear());
+ EXPECT_EQ(2, tag.GetTrackNumber());
+ EXPECT_EQ(1u, tag.GetArtist().size());
+ if (tag.GetArtist().size() > 0) EXPECT_EQ("artist", tag.GetArtist().front());
+ EXPECT_EQ("album", tag.GetAlbum());
+ EXPECT_EQ("comment", tag.GetComment());
+ EXPECT_EQ(1u, tag.GetGenre().size());
+ if (tag.GetGenre().size() > 0) EXPECT_EQ("Jazz", tag.GetGenre().front());
+ EXPECT_EQ("title", tag.GetTitle());
+}
+
+
+TYPED_TEST(TestTagParser, HandleNullTag) {
+ // A Null tag should not parse, and not break us either
+ CMusicInfoTag tag;
+ EXPECT_FALSE(CTagLoaderTagLib::ParseTag<TypeParam>(NULL, NULL, tag));
+}
+
+template<typename T, size_t N>
+T * end(T (&ra)[N]) {
+ return ra + N;
+}
+
+const char *tags[] = { "APIC", "ASPI", "COMM", "COMR", "ENCR", "EQU2",
+ "ETCO", "GEOB", "GRID", "LINK", "MCDI", "MLLT", "OWNE", "PRIV", "PCNT",
+ "POPM", "POSS", "RBUF", "RVA2", "RVRB", "SEEK", "SIGN", "SYLT",
+ "SYTC", "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR",
+ "TDRC", "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2",
+ "TIT3", "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN",
+ "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO",
+ "TPUB", "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE",
+ "TSST", "TXXX", "UFID", "USER", "USLT", "WCOM", "WCOP", "WOAF", "WOAR",
+ "WOAS", "WORS", "WPAY", "WPUB", "WXXX", "ARTIST", "ARTISTS",
+ "ALBUMARTIST" , "ALBUM ARTIST", "ALBUMARTISTS" , "ALBUM ARTISTS", "ALBUM",
+ "TITLE", "TRACKNUMBER" "TRACK", "DISCNUMBER" "DISC", "YEAR", "GENRE",
+ "COMMENT", "CUESHEET", "ENCODEDBY", "COMPILATION", "LYRICS",
+ "REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "REPLAYGAIN_TRACK_PEAK",
+ "REPLAYGAIN_ALBUM_PEAK", "MUSICBRAINZ_ARTISTID",
+ "MUSICBRAINZ_ALBUMARTISTID", "RATING", "MUSICBRAINZ_ALBUMARTIST",
+ "MUSICBRAINZ_ALBUMID", "MUSICBRAINZ_TRACKID", "METADATA_BLOCK_PICTURE",
+ "COVERART"
+};
+
+
+// This test exposes a bug in taglib library (#670) so for now we will not run it for all tag types
+// See https://github.com/taglib/taglib/issues/670 for details.
+typedef ::testing::Types<ID3v2::Tag, ID3v1::Tag, ASF::Tag, APE::Tag, Ogg::XiphComment> EmptyPropertiesTagTypes;
+template <typename T>
+class EmptyTagParser : public ::testing::Test, public CTagLoaderTagLib {
+ public:
+ T value_;
+};
+TYPED_TEST_CASE(EmptyTagParser, EmptyPropertiesTagTypes);
+
+TYPED_TEST(EmptyTagParser, EmptyProperties) {
+ TypeParam *tg = &this->value_;
+ CMusicInfoTag tag;
+ PropertyMap props;
+ int tagcount = end(tags) - tags;
+ for(int i = 0; i < tagcount; i++) {
+ props.insert(tags[i], StringList());
+ }
+
+ // Even though all the properties are empty, we shouldn't
+ // crash
+ EXPECT_TRUE(CTagLoaderTagLib::ParseTag<TypeParam>(tg, NULL, tag));
+}
+
+
+
+TYPED_TEST(TestTagParser, FooProperties) {
+ TypeParam *tg = &this->value_;
+ CMusicInfoTag tag;
+ PropertyMap props;
+ int tagcount = end(tags) - tags;
+ for(int i = 0; i < tagcount; i++) {
+ props.insert(tags[i], String("foo"));
+ }
+ tg->setProperties(props);
+
+ EXPECT_TRUE(CTagLoaderTagLib::ParseTag<TypeParam>(tg, NULL, tag));
+ EXPECT_EQ(0, tag.GetYear());
+ EXPECT_EQ(0, tag.GetTrackNumber());
+ EXPECT_EQ(1u, tag.GetArtist().size());
+ if (tag.GetArtist().size() > 0) EXPECT_EQ("foo", tag.GetArtist().front());
+ EXPECT_EQ("foo", tag.GetAlbum());
+ EXPECT_EQ("foo", tag.GetComment());
+ if (tag.GetGenre().size() > 0) EXPECT_EQ("foo", tag.GetGenre().front());
+ EXPECT_EQ("foo", tag.GetTitle());
+}
+
+class TestCTagLoaderTagLib : public ::testing::Test, public CTagLoaderTagLib {};
+TEST_F(TestCTagLoaderTagLib, SetGenre)
+{
+ CMusicInfoTag tag, tag2;
+ const char *genre_nr[] = {"0", "2", "4"};
+ const char *names[] = { "Jazz", "Funk", "Ska" };
+ std::vector<std::string> genres(genre_nr, end(genre_nr));
+ std::vector<std::string> named_genre(names, end(names));
+
+ CTagLoaderTagLib::SetGenre(tag, genres);
+ EXPECT_EQ(3u, tag.GetGenre().size());
+ EXPECT_EQ("Blues", tag.GetGenre()[0]);
+ EXPECT_EQ("Country", tag.GetGenre()[1]);
+ EXPECT_EQ("Disco", tag.GetGenre()[2]);
+
+ CTagLoaderTagLib::SetGenre(tag2, named_genre);
+ EXPECT_EQ(3u, tag2.GetGenre().size());
+ for(int i = 0; i < 3; i++)
+ EXPECT_EQ(names[i], tag2.GetGenre()[i]);
+
+}
TEST(TestTagLoaderTagLib, SplitMBID)
{
diff --git a/xbmc/pvr/PVRGUIInfo.cpp b/xbmc/pvr/PVRGUIInfo.cpp
index b174ceb038..b551664994 100644
--- a/xbmc/pvr/PVRGUIInfo.cpp
+++ b/xbmc/pvr/PVRGUIInfo.cpp
@@ -89,6 +89,12 @@ void CPVRGUIInfo::ResetProperties(void)
m_bHasTVChannels = false;
m_bHasRadioChannels = false;
m_bIsTimeshifting = false;
+ m_iTimeshiftStartTime = time_t(0);
+ m_iTimeshiftEndTime = time_t(0);
+ m_iTimeshiftPlayTime = time_t(0);
+ m_strTimeshiftStartTime.clear();
+ m_strTimeshiftEndTime.clear();
+ m_strTimeshiftPlayTime.clear();
ResetPlayingTag();
ClearQualityInfo(m_qualityInfo);
diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp
index 2f5397005e..a5f90b4d86 100644
--- a/xbmc/pvr/PVRManager.cpp
+++ b/xbmc/pvr/PVRManager.cpp
@@ -400,8 +400,6 @@ public:
g_PVRManager.Start(false);
return true;
}
-private:
- int m_openWindowId;
};
void CPVRManager::Start(bool bAsync /* = false */)
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
index 3451b90731..c83d8c19e1 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
@@ -51,6 +51,16 @@ using namespace PVR;
CGUIDialogPVRRadioRDSInfo::CGUIDialogPVRRadioRDSInfo(void)
: CGUIDialog(WINDOW_DIALOG_PVR_RADIO_RDS_INFO, "DialogPVRRadioRDSInfo.xml")
, m_rdsItem(new CFileItem)
+ , m_InfoPresent(false)
+ , m_LabelInfoNewsPresent(false)
+ , m_LabelInfoNewsLocalPresent(false)
+ , m_LabelInfoWeatherPresent(false)
+ , m_LabelInfoLotteryPresent(false)
+ , m_LabelInfoSportPresent(false)
+ , m_LabelInfoStockPresent(false)
+ , m_LabelInfoOtherPresent(false)
+ , m_LabelInfoCinemaPresent(false)
+ , m_LabelInfoHoroscopePresent(false)
{
}
diff --git a/xbmc/rendering/dx/RenderSystemDX.cpp b/xbmc/rendering/dx/RenderSystemDX.cpp
index e3795e2af6..52daea7664 100644
--- a/xbmc/rendering/dx/RenderSystemDX.cpp
+++ b/xbmc/rendering/dx/RenderSystemDX.cpp
@@ -1248,9 +1248,7 @@ bool CRenderSystemDX::ClearBuffers(color_t color)
if (m_stereoView == RENDER_STEREO_VIEW_RIGHT)
{
// execute command's queue
- if ( m_stereoMode != RENDER_STEREO_MODE_SPLIT_HORIZONTAL
- && m_stereoMode != RENDER_STEREO_MODE_SPLIT_VERTICAL)
- FinishCommandList();
+ FinishCommandList();
// do not clear RT for anaglyph modes
if ( m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index bcba6f6b47..bc3aa8cec8 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -274,7 +274,6 @@ void CAdvancedSettings::Initialize()
m_bVideoLibraryAllItemsOnBottom = false;
m_iVideoLibraryRecentlyAddedItems = 25;
- m_bVideoLibraryHideEmptySeries = false;
m_bVideoLibraryCleanOnUpdate = false;
m_bVideoLibraryUseFastHash = true;
m_bVideoLibraryExportAutoThumbs = false;
@@ -720,7 +719,6 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
{
XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bVideoLibraryAllItemsOnBottom);
XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iVideoLibraryRecentlyAddedItems, 1, INT_MAX);
- XMLUtils::GetBoolean(pElement, "hideemptyseries", m_bVideoLibraryHideEmptySeries);
XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bVideoLibraryCleanOnUpdate);
XMLUtils::GetBoolean(pElement, "usefasthash", m_bVideoLibraryUseFastHash);
XMLUtils::GetString(pElement, "itemseparator", m_videoItemSeparator);
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
index 230f4910e7..6475350c42 100644
--- a/xbmc/settings/AdvancedSettings.h
+++ b/xbmc/settings/AdvancedSettings.h
@@ -279,7 +279,6 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
bool m_bVideoLibraryAllItemsOnBottom;
int m_iVideoLibraryRecentlyAddedItems;
- bool m_bVideoLibraryHideEmptySeries;
bool m_bVideoLibraryCleanOnUpdate;
bool m_bVideoLibraryUseFastHash;
bool m_bVideoLibraryExportAutoThumbs;
@@ -401,4 +400,5 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
void setExtraLogLevel(const std::vector<CVariant> &components);
};
-XBMC_GLOBAL(CAdvancedSettings,g_advancedSettings);
+XBMC_GLOBAL_REF(CAdvancedSettings,g_advancedSettings);
+#define g_advancedSettings XBMC_GLOBAL_USE(CAdvancedSettings)
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index 38bb87570f..ecd155d965 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -143,6 +143,7 @@ const std::string CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE = "videolibra
const std::string CSettings::SETTING_VIDEOLIBRARY_CLEANUP = "videolibrary.cleanup";
const std::string CSettings::SETTING_VIDEOLIBRARY_EXPORT = "videolibrary.export";
const std::string CSettings::SETTING_VIDEOLIBRARY_IMPORT = "videolibrary.import";
+const std::string CSettings::SETTING_VIDEOLIBRARY_SHOWEMPTYTVSHOWS = "videolibrary.showemptytvshows";
const std::string CSettings::SETTING_LOCALE_AUDIOLANGUAGE = "locale.audiolanguage";
const std::string CSettings::SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG = "videoplayer.preferdefaultflag";
const std::string CSettings::SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM = "videoplayer.autoplaynextitem";
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index 5f132f7040..55e150dc62 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -99,6 +99,7 @@ public:
static const std::string SETTING_VIDEOLIBRARY_CLEANUP;
static const std::string SETTING_VIDEOLIBRARY_EXPORT;
static const std::string SETTING_VIDEOLIBRARY_IMPORT;
+ static const std::string SETTING_VIDEOLIBRARY_SHOWEMPTYTVSHOWS;
static const std::string SETTING_LOCALE_AUDIOLANGUAGE;
static const std::string SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG;
static const std::string SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM;
diff --git a/xbmc/settings/dialogs/GUIDialogAudioDSPSettings.cpp b/xbmc/settings/dialogs/GUIDialogAudioDSPSettings.cpp
index b18db5c6e5..ed60a339cd 100644
--- a/xbmc/settings/dialogs/GUIDialogAudioDSPSettings.cpp
+++ b/xbmc/settings/dialogs/GUIDialogAudioDSPSettings.cpp
@@ -379,7 +379,7 @@ void CGUIDialogAudioDSPSettings::Save()
return;
// prompt user if they are sure
- if (!CGUIDialogYesNo::ShowAndGetInput(12376, 750, 0, 12377))
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12376}, CVariant{12377}))
return;
// reset the settings
diff --git a/xbmc/threads/Thread.cpp b/xbmc/threads/Thread.cpp
index b7343751c0..ea03bb246d 100644
--- a/xbmc/threads/Thread.cpp
+++ b/xbmc/threads/Thread.cpp
@@ -123,7 +123,7 @@ THREADFUNC CThread::staticThread(void* data)
pThread->SetThreadInfo();
- LOG(LOGNOTICE,"Thread %s start, auto delete: %s", name.c_str(), (autodelete ? "true" : "false"));
+ LOG(LOGDEBUG,"Thread %s start, auto delete: %s", name.c_str(), (autodelete ? "true" : "false"));
currentThread.set(pThread);
pThread->m_StartEvent.Set();
diff --git a/xbmc/threads/platform/win/Win32Exception.cpp b/xbmc/threads/platform/win/Win32Exception.cpp
index 5eb1f01ddf..2b79ce0c3e 100644
--- a/xbmc/threads/platform/win/Win32Exception.cpp
+++ b/xbmc/threads/platform/win/Win32Exception.cpp
@@ -121,7 +121,7 @@ bool win32_exception::write_minidump(EXCEPTION_POINTERS* pEp)
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
- dumpFileName = StringUtils::Format("xbmc_crashlog-%s-%04d%02d%02d-%02d%02d%02d.dmp",
+ dumpFileName = StringUtils::Format("kodi_crashlog-%s-%04d%02d%02d-%02d%02d%02d.dmp",
mVersion.c_str(),
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
@@ -224,7 +224,7 @@ bool win32_exception::write_stacktrace(EXCEPTION_POINTERS* pEp)
pSFTA == NULL || pSGMB == NULL)
goto cleanup;
- dumpFileName = StringUtils::Format("xbmc_stacktrace-%s-%04d%02d%02d-%02d%02d%02d.txt",
+ dumpFileName = StringUtils::Format("kodi_stacktrace-%s-%04d%02d%02d-%02d%02d%02d.txt",
mVersion.c_str(),
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
diff --git a/xbmc/utils/CharsetConverter.h b/xbmc/utils/CharsetConverter.h
index 36777e84ad..ab504099dc 100644
--- a/xbmc/utils/CharsetConverter.h
+++ b/xbmc/utils/CharsetConverter.h
@@ -173,6 +173,6 @@ private:
class CInnerConverter;
};
-XBMC_GLOBAL(CCharsetConverter,g_charsetConverter);
-
+XBMC_GLOBAL_REF(CCharsetConverter,g_charsetConverter);
+#define g_charsetConverter XBMC_GLOBAL_USE(CCharsetConverter)
#endif
diff --git a/xbmc/utils/GlobalsHandling.h b/xbmc/utils/GlobalsHandling.h
index ab46c58538..1e18ede49e 100644
--- a/xbmc/utils/GlobalsHandling.h
+++ b/xbmc/utils/GlobalsHandling.h
@@ -212,13 +212,3 @@ namespace xbmcutil
* #define g_variable XBMC_GLOBAL_USE(classname)
*/
#define XBMC_GLOBAL_USE(classname) (*(xbmcutil::GlobalsSingleton<classname>::getQuick()))
-
-/**
- * For pattern (1) above, you can use the following macro. WARNING: This should only
- * be used when the global in question is never accessed, directly or indirectly, from
- * a static method called (again, directly or indirectly) during startup or shutdown.
- */
-#define XBMC_GLOBAL(classname,g_variable) \
- XBMC_GLOBAL_REF(classname,g_variable); \
- static classname & g_variable = (*(g_variable##Ref.get()))
-
diff --git a/xbmc/utils/LabelFormatter.cpp b/xbmc/utils/LabelFormatter.cpp
index 2ec0e91a83..7b0b4e5109 100644
--- a/xbmc/utils/LabelFormatter.cpp
+++ b/xbmc/utils/LabelFormatter.cpp
@@ -238,7 +238,7 @@ std::string CLabelFormatter::GetMaskContent(const CMaskString &mask, const CFile
}
break;
case 'I': // size
- if( !item->m_bIsFolder || item->m_dwSize != 0 )
+ if( (item->m_bIsFolder && item->m_dwSize != 0) || item->m_dwSize >= 0 )
value = StringUtils::SizeToString(item->m_dwSize);
break;
case 'J': // date
diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp
index af6cf3750f..b56e2e8f88 100644
--- a/xbmc/video/VideoDatabase.cpp
+++ b/xbmc/video/VideoDatabase.cpp
@@ -6280,7 +6280,7 @@ bool CVideoDatabase::GetTvShowsByWhere(const std::string& strBaseDir, const Filt
if ((CProfilesManager::GetInstance().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
g_passwordManager.bMasterUser ||
g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::GetInstance().GetSources("video"))) &&
- (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
+ (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOLIBRARY_SHOWEMPTYTVSHOWS) || movie.m_iEpisode > 0))
{
pItem->SetFromVideoInfoTag(movie);
diff --git a/xbmc/windowing/egl/EGLNativeTypeAndroid.cpp b/xbmc/windowing/egl/EGLNativeTypeAndroid.cpp
index 5f9de94972..8b7b695c03 100644
--- a/xbmc/windowing/egl/EGLNativeTypeAndroid.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeAndroid.cpp
@@ -97,8 +97,8 @@ bool CEGLNativeTypeAndroid::GetNativeWindow(XBNativeWindowType **nativeWindow) c
{
if (!nativeWindow)
return false;
- *nativeWindow = (XBNativeWindowType*) CXBMCApp::GetNativeWindow(30000);
- return (*nativeWindow != NULL);
+ *nativeWindow = (XBNativeWindowType*) CXBMCApp::GetNativeWindow(2000);
+ return (*nativeWindow != NULL && **nativeWindow != NULL);
}
bool CEGLNativeTypeAndroid::DestroyNativeDisplay()
@@ -117,7 +117,7 @@ static float currentRefreshRate()
if (window)
{
float preferredRate = window.getAttributes().getpreferredRefreshRate();
- if (preferredRate > 20.0 && preferredRate < 61.0)
+ if (preferredRate > 20.0 && preferredRate < 70.0)
{
CLog::Log(LOGDEBUG, "CEGLNativeTypeAndroid: Preferred refresh rate: %f", preferredRate);
return preferredRate;
@@ -128,7 +128,7 @@ static float currentRefreshRate()
if (display)
{
float reportedRate = display.getRefreshRate();
- if (reportedRate > 20.0 && reportedRate < 61.0)
+ if (reportedRate > 20.0 && reportedRate < 70.0)
{
CLog::Log(LOGDEBUG, "CEGLNativeTypeAndroid: Current display refresh rate: %f", reportedRate);
return reportedRate;
diff --git a/xbmc/windows/GUIWindowScreensaverDim.cpp b/xbmc/windows/GUIWindowScreensaverDim.cpp
index 249ee22514..ed54dc2511 100644
--- a/xbmc/windows/GUIWindowScreensaverDim.cpp
+++ b/xbmc/windows/GUIWindowScreensaverDim.cpp
@@ -28,6 +28,7 @@ CGUIWindowScreensaverDim::CGUIWindowScreensaverDim(void)
{
m_needsScaling = false;
m_dimLevel = 100.0f;
+ m_newDimLevel = 100.0f;
m_animations.push_back(CAnimation::CreateFader(0, 100, 0, 1000, ANIM_TYPE_WINDOW_OPEN));
m_animations.push_back(CAnimation::CreateFader(100, 0, 0, 1000, ANIM_TYPE_WINDOW_CLOSE));
m_renderOrder = RENDER_ORDER_WINDOW_SCREENSAVER;
@@ -39,8 +40,8 @@ CGUIWindowScreensaverDim::~CGUIWindowScreensaverDim(void)
void CGUIWindowScreensaverDim::UpdateVisibility()
{
- m_dimLevel = g_application.GetDimScreenSaverLevel();
- if (m_dimLevel)
+ m_newDimLevel = g_application.GetDimScreenSaverLevel();
+ if (m_newDimLevel)
Open();
else
Close();
@@ -48,6 +49,8 @@ void CGUIWindowScreensaverDim::UpdateVisibility()
void CGUIWindowScreensaverDim::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
{
+ if (m_newDimLevel != m_dimLevel && !IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
+ m_dimLevel = m_newDimLevel;
CGUIDialog::Process(currentTime, dirtyregions);
m_renderRegion.SetRect(0, 0, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight());
}
diff --git a/xbmc/windows/GUIWindowScreensaverDim.h b/xbmc/windows/GUIWindowScreensaverDim.h
index e0db07d105..8da9f0b918 100644
--- a/xbmc/windows/GUIWindowScreensaverDim.h
+++ b/xbmc/windows/GUIWindowScreensaverDim.h
@@ -34,4 +34,5 @@ protected:
virtual void UpdateVisibility();
private:
float m_dimLevel;
+ float m_newDimLevel;
};