diff options
author | Garrett Brown <garbearucla@gmail.com> | 2013-09-11 23:17:42 -0700 |
---|---|---|
committer | Garrett Brown <themagnificentmrb@gmail.com> | 2016-12-01 18:08:29 -0800 |
commit | 5a3be439dad1ee32d79b2c4fdd85782ee67254df (patch) | |
tree | 5705ad46778c8e9f7a4d8f491af74bc4259725a8 | |
parent | e2563f6529ea0f08fe153d6baa9dff0935ea8d65 (diff) |
[retroplayer] RetroPlayer core
Thanks to elpendor for RGB565 support, poisson for RAII improvements,
ChrisMyhre for catching a compile error, notspiff for CMake fixes,
acmiyaguchi for video and audio codec support, and popcornmix for
Raspberry Pi support (PR 62).
TODO: Is a call to `g_renderManager.IsStarted()` needed?
33 files changed, 1876 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore index 3a617d0fa4..83895b5878 100644 --- a/.gitignore +++ b/.gitignore @@ -442,6 +442,7 @@ lib/cpluff/stamp-h1 /xbmc/cores/playercorefactory/Makefile /xbmc/cores/Makefile /xbmc/cores/VideoPlayer/VideoRenderers/Makefile +/xbmc/cores/RetroPlayer/Makefile # /xbmc/filesystem/ /xbmc/filesystem/Makefile diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj index 6ad12ab571..7ca98c7295 100644 --- a/Kodi.xcodeproj/project.pbxproj +++ b/Kodi.xcodeproj/project.pbxproj @@ -317,6 +317,14 @@ 6890C2511DDBDDC900F8F362 /* MouseInputHandling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C24E1DDBDDC900F8F362 /* MouseInputHandling.cpp */; }; 6890C2571DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */; }; 6890C2581DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */; }; + 6890C2771DDBDFD900F8F362 /* PixelConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C26E1DDBDFD900F8F362 /* PixelConverter.cpp */; }; + 6890C2781DDBDFD900F8F362 /* PixelConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C26E1DDBDFD900F8F362 /* PixelConverter.cpp */; }; + 6890C2791DDBDFD900F8F362 /* RetroPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2701DDBDFD900F8F362 /* RetroPlayer.cpp */; }; + 6890C27A1DDBDFD900F8F362 /* RetroPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2701DDBDFD900F8F362 /* RetroPlayer.cpp */; }; + 6890C27B1DDBDFD900F8F362 /* RetroPlayerAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2721DDBDFD900F8F362 /* RetroPlayerAudio.cpp */; }; + 6890C27C1DDBDFD900F8F362 /* RetroPlayerAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2721DDBDFD900F8F362 /* RetroPlayerAudio.cpp */; }; + 6890C27D1DDBDFD900F8F362 /* RetroPlayerVideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2751DDBDFD900F8F362 /* RetroPlayerVideo.cpp */; }; + 6890C27E1DDBDFD900F8F362 /* RetroPlayerVideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6890C2751DDBDFD900F8F362 /* RetroPlayerVideo.cpp */; }; 68AE5BA51C92412900C4D527 /* AddonCallbacksPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */; }; 68AE5BA61C92412900C4D527 /* AddonCallbacksPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */; }; 68AE5BBD1C9241DF00C4D527 /* DefaultJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68AE5BAC1C9241DF00C4D527 /* DefaultJoystick.cpp */; }; @@ -2919,6 +2927,16 @@ 6890C2541DDBDDD500F8F362 /* IMouseInputHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMouseInputHandler.h; path = mouse/IMouseInputHandler.h; sourceTree = "<group>"; }; 6890C2551DDBDDD500F8F362 /* MouseWindowingButtonMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MouseWindowingButtonMap.cpp; path = mouse/MouseWindowingButtonMap.cpp; sourceTree = "<group>"; }; 6890C2561DDBDDD500F8F362 /* MouseWindowingButtonMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseWindowingButtonMap.h; path = mouse/MouseWindowingButtonMap.h; sourceTree = "<group>"; }; + 6890C26D1DDBDFD900F8F362 /* IPixelConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPixelConverter.h; path = RetroPlayer/IPixelConverter.h; sourceTree = "<group>"; }; + 6890C26E1DDBDFD900F8F362 /* PixelConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PixelConverter.cpp; path = RetroPlayer/PixelConverter.cpp; sourceTree = "<group>"; }; + 6890C26F1DDBDFD900F8F362 /* PixelConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PixelConverter.h; path = RetroPlayer/PixelConverter.h; sourceTree = "<group>"; }; + 6890C2701DDBDFD900F8F362 /* RetroPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RetroPlayer.cpp; path = RetroPlayer/RetroPlayer.cpp; sourceTree = "<group>"; }; + 6890C2711DDBDFD900F8F362 /* RetroPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RetroPlayer.h; path = RetroPlayer/RetroPlayer.h; sourceTree = "<group>"; }; + 6890C2721DDBDFD900F8F362 /* RetroPlayerAudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RetroPlayerAudio.cpp; path = RetroPlayer/RetroPlayerAudio.cpp; sourceTree = "<group>"; }; + 6890C2731DDBDFD900F8F362 /* RetroPlayerAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RetroPlayerAudio.h; path = RetroPlayer/RetroPlayerAudio.h; sourceTree = "<group>"; }; + 6890C2741DDBDFD900F8F362 /* RetroPlayerDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RetroPlayerDefines.h; path = RetroPlayer/RetroPlayerDefines.h; sourceTree = "<group>"; }; + 6890C2751DDBDFD900F8F362 /* RetroPlayerVideo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RetroPlayerVideo.cpp; path = RetroPlayer/RetroPlayerVideo.cpp; sourceTree = "<group>"; }; + 6890C2761DDBDFD900F8F362 /* RetroPlayerVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RetroPlayerVideo.h; path = RetroPlayer/RetroPlayerVideo.h; sourceTree = "<group>"; }; 68AE5BA01C923E5300C4D527 /* kodi_vfs_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kodi_vfs_utils.hpp; path = "kodi-addon-dev-kit/include/kodi/kodi_vfs_utils.hpp"; sourceTree = "<group>"; }; 68AE5BA31C92412900C4D527 /* AddonCallbacksPeripheral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonCallbacksPeripheral.cpp; path = addons/binary/interfaces/api1/Peripheral/AddonCallbacksPeripheral.cpp; sourceTree = "<group>"; }; 68AE5BA41C92412900C4D527 /* AddonCallbacksPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonCallbacksPeripheral.h; path = addons/binary/interfaces/api1/Peripheral/AddonCallbacksPeripheral.h; sourceTree = "<group>"; }; @@ -6443,6 +6461,23 @@ name = generic; sourceTree = "<group>"; }; + 6890C26C1DDBDFC100F8F362 /* RetroPlayer */ = { + isa = PBXGroup; + children = ( + 6890C26D1DDBDFD900F8F362 /* IPixelConverter.h */, + 6890C26E1DDBDFD900F8F362 /* PixelConverter.cpp */, + 6890C26F1DDBDFD900F8F362 /* PixelConverter.h */, + 6890C2701DDBDFD900F8F362 /* RetroPlayer.cpp */, + 6890C2711DDBDFD900F8F362 /* RetroPlayer.h */, + 6890C2721DDBDFD900F8F362 /* RetroPlayerAudio.cpp */, + 6890C2731DDBDFD900F8F362 /* RetroPlayerAudio.h */, + 6890C2741DDBDFD900F8F362 /* RetroPlayerDefines.h */, + 6890C2751DDBDFD900F8F362 /* RetroPlayerVideo.cpp */, + 6890C2761DDBDFD900F8F362 /* RetroPlayerVideo.h */, + ); + name = RetroPlayer; + sourceTree = "<group>"; + }; 68AE5BA21C92410300C4D527 /* Peripheral */ = { isa = PBXGroup; children = ( @@ -8292,6 +8327,7 @@ 7C5608C30F1754930056433A /* ExternalPlayer */, E38E15D20D25F9FA00618676 /* paplayer */, F5E56B11108284E6006E788A /* playercorefactory */, + 6890C26C1DDBDFC100F8F362 /* RetroPlayer */, E38E14F80D25F9F900618676 /* VideoPlayer */, DF923E5B1A11536A008CDB0C /* DataCacheCore.cpp */, DF923E5C1A11536A008CDB0C /* DataCacheCore.h */, @@ -10585,6 +10621,7 @@ 6890C1F21DDBDBEA00F8F362 /* GameClientReversiblePlayback.cpp in Sources */, F592568810FBF2E100D2C91D /* ConvolutionKernels.cpp in Sources */, F5DC87E2110A287400EE1B15 /* RingBuffer.cpp in Sources */, + 6890C2791DDBDFD900F8F362 /* RetroPlayer.cpp in Sources */, F5F244651110DC6B009126C6 /* FileOperationJob.cpp in Sources */, 6890C24A1DDBDDA400F8F362 /* KeyboardEasterEgg.cpp in Sources */, DF0E4AC51AD597ED00A75430 /* VideoPlayerRadioRDS.cpp in Sources */, @@ -10757,6 +10794,7 @@ F5AE40A413415D9E0004BD79 /* PlayerOperations.cpp in Sources */, F5AE40A513415D9E0004BD79 /* PlaylistOperations.cpp in Sources */, F5AE40A613415D9E0004BD79 /* SystemOperations.cpp in Sources */, + 6890C2771DDBDFD900F8F362 /* PixelConverter.cpp in Sources */, F5AE40A713415D9E0004BD79 /* VideoLibrary.cpp in Sources */, F5AE40A813415D9E0004BD79 /* XBMCOperations.cpp in Sources */, C84BF7341349BB74006D6FC9 /* JSONServiceDescription.cpp in Sources */, @@ -10960,6 +10998,7 @@ DF1D2DF31B6E85EE002BB9DB /* XbtManager.cpp in Sources */, DFB25D30163D4743006C4A48 /* AddonCallback.cpp in Sources */, DFB25D31163D4743006C4A48 /* AddonClass.cpp in Sources */, + 6890C27B1DDBDFD900F8F362 /* RetroPlayerAudio.cpp in Sources */, DFB25D32163D4743006C4A48 /* AddonUtils.cpp in Sources */, DFB25D33163D4743006C4A48 /* CallbackFunction.cpp in Sources */, DFB25D34163D4743006C4A48 /* CallbackHandler.cpp in Sources */, @@ -11262,6 +11301,7 @@ B179BD6B1AD8EA7B00EA8D49 /* InputCodingTableBaiduPY.cpp in Sources */, DFD7173B1C09FEC60025D964 /* OSXGNUReplacements.c in Sources */, B179BD6E1AD8EA7B00EA8D49 /* InputCodingTableBasePY.cpp in Sources */, + 6890C27D1DDBDFD900F8F362 /* RetroPlayerVideo.cpp in Sources */, B179BD711AD8EA7B00EA8D49 /* InputCodingTableFactory.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -11391,6 +11431,7 @@ E49911C9174E5D2500741B6D /* DVDOverlayCodecSSA.cpp in Sources */, E49911CA174E5D2500741B6D /* DVDOverlayCodecText.cpp in Sources */, 7C8E02471BA35D0B0072E8B2 /* SystemBuiltins.cpp in Sources */, + 6890C27A1DDBDFD900F8F362 /* RetroPlayer.cpp in Sources */, E49911CB174E5D2500741B6D /* DVDOverlayCodecTX3G.cpp in Sources */, E49911CE174E5D2500741B6D /* DVDVideoCodecFFmpeg.cpp in Sources */, 395C29BD1A94733100EBC7AD /* Key.cpp in Sources */, @@ -11564,6 +11605,7 @@ E499127F174E5D9900741B6D /* DirectoryNodeAlbumCompilationsSongs.cpp in Sources */, E4991280174E5D9900741B6D /* DirectoryNodeAlbumRecentlyAdded.cpp in Sources */, 395F6DDE1A8133360088CC74 /* GUIDialogSimpleMenu.cpp in Sources */, + 6890C2781DDBDFD900F8F362 /* PixelConverter.cpp in Sources */, E4991281174E5D9900741B6D /* DirectoryNodeAlbumRecentlyAddedSong.cpp in Sources */, E4991282174E5D9900741B6D /* DirectoryNodeAlbumRecentlyPlayed.cpp in Sources */, DFD7175D1C0A031B0025D964 /* IOSExternalTouchController.mm in Sources */, @@ -11727,6 +11769,7 @@ E4991313174E5DAD00741B6D /* GUISpinControlEx.cpp in Sources */, E4991314174E5DAD00741B6D /* GUIStaticItem.cpp in Sources */, E4991315174E5DAD00741B6D /* GUITextBox.cpp in Sources */, + 6890C27E1DDBDFD900F8F362 /* RetroPlayerVideo.cpp in Sources */, E4991316174E5DAD00741B6D /* GUITextLayout.cpp in Sources */, E4991317174E5DAD00741B6D /* GUITexture.cpp in Sources */, 395C2A121A9F072400EBC7AD /* ResourceDirectory.cpp in Sources */, @@ -12325,6 +12368,7 @@ 7CCDA1B0192753E30074CF51 /* X_MS_MediaReceiverRegistrarSCPD.cpp in Sources */, 7CCDA1BB192753E30074CF51 /* AVTransportSCPD.cpp in Sources */, 7CCDA1C8192753E30074CF51 /* PltMediaController.cpp in Sources */, + 6890C27C1DDBDFD900F8F362 /* RetroPlayerAudio.cpp in Sources */, 7CCDA1D1192753E30074CF51 /* PltMediaRenderer.cpp in Sources */, 7CCDA1DC192753E30074CF51 /* RdrConnectionManagerSCPD.cpp in Sources */, 7CCDA1E7192753E30074CF51 /* RenderingControlSCPD.cpp in Sources */, diff --git a/Makefile.in b/Makefile.in index f28925e9f2..a8c1f63a8d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -41,6 +41,7 @@ DIRECTORY_ARCHIVES=$(VideoPlayer_ARCHIVES) \ xbmc/cores/DllLoader/exports/exports.a \ xbmc/cores/DllLoader/exports/util/exports_utils.a \ xbmc/cores/ExternalPlayer/ExternalPlayer.a \ + xbmc/cores/RetroPlayer/retroplayer.a \ xbmc/cores/VideoPlayer/VideoRenderers/VideoRenderer.a \ xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoShaders.a \ xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/HwDecRender.a \ diff --git a/configure.ac b/configure.ac index 1da41576dd..8d1e85e293 100644 --- a/configure.ac +++ b/configure.ac @@ -2173,6 +2173,7 @@ OUTPUT_FILES="Makefile \ xbmc/cores/paplayer/Makefile \ xbmc/cores/omxplayer/Makefile \ xbmc/cores/playercorefactory/Makefile \ + xbmc/cores/RetroPlayer/Makefile \ xbmc/messaging/Makefile \ xbmc/messaging/helpers/Makefile \ xbmc/guilib/Makefile \ diff --git a/project/cmake/treedata/common/cores.txt b/project/cmake/treedata/common/cores.txt index d23939d314..8d79dd18dc 100644 --- a/project/cmake/treedata/common/cores.txt +++ b/project/cmake/treedata/common/cores.txt @@ -6,3 +6,4 @@ xbmc/cores/DllLoader/exports/util cores/dll-loader/exports/util xbmc/cores/ExternalPlayer cores/externalplayer xbmc/cores/paplayer cores/paplayer xbmc/cores/playercorefactory cores/playercorefactory +xbmc/cores/RetroPlayer cores/RetroPlayer diff --git a/system/keymaps/gamepad.xml b/system/keymaps/gamepad.xml index e1767515ba..63140230da 100644 --- a/system/keymaps/gamepad.xml +++ b/system/keymaps/gamepad.xml @@ -84,6 +84,21 @@ <dpaddown>ChapterOrBigStepBack</dpaddown> </gamepad> </FullscreenVideo> + <FullscreenGame> + <gamepad> + <A>Pause</A> + <B>Stop</B> + <Y>AspectRatio</Y> + <black>CodecInfo</black> + <white>Info</white> + <back>Seek(-7)</back><!-- Replaces smallstepback --> + <start>OSD</start> + <leftanalogtrigger>AnalogRewind</leftanalogtrigger> + <rightanalogtrigger>AnalogFastForward</rightanalogtrigger> + <dpadleft>StepBack</dpadleft> + <dpadright>StepForward</dpadright> + </gamepad> + </FullscreenGame> <FullscreenLiveTV> <gamepad> <dpadleft>StepBack</dpadleft> diff --git a/system/keymaps/joystick.xml b/system/keymaps/joystick.xml index 937baf2229..7717c587f1 100644 --- a/system/keymaps/joystick.xml +++ b/system/keymaps/joystick.xml @@ -91,6 +91,35 @@ <rightstickdown>VolumeDown</rightstickdown> </joystick> </FullscreenVideo> + <FullscreenGame> + <joystick> + <a>noop</a> + <b>noop</b> + <x>noop</x> + <y>noop</y> + <start>noop</start> + <back>noop</back> + <!--<guide>noop</guide>--> + <up>noop</up> + <down>noop</down> + <right>noop</right> + <left>noop</left> + <leftthumb>noop</leftthumb> + <rightthumb>noop</rightthumb> + <lefttrigger>noop</lefttrigger> + <righttrigger>noop</righttrigger> + <leftbumper>noop</leftbumper> + <rightbumper>noop</rightbumper> + <leftstickleft>noop</leftstickleft> + <leftstickright>noop</leftstickright> + <leftstickup>noop</leftstickup> + <leftstickdown>noop</leftstickdown> + <rightstickleft>noop</rightstickleft> + <rightstickright>noop</rightstickright> + <rightstickup>noop</rightstickup> + <rightstickdown>noop</rightstickdown> + </joystick> + </FullscreenGame> <FullscreenLiveTV> <joystick> <up>ChannelUp</up> diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml index d4dffed997..fd91476f56 100644 --- a/system/keymaps/keyboard.xml +++ b/system/keymaps/keyboard.xml @@ -373,6 +373,31 @@ <b mod="alt">CreateEpisodeBookmark</b> </keyboard> </FullscreenVideo> + <FullscreenGame> + <keyboard> + <f>FastForward</f> + <r>Rewind</r> + <period>StepForward</period> + <comma>StepBack</comma> + <backspace>Fullscreen</backspace> + <backspace mod="longpress">Stop</backspace> + <browser_back>Fullscreen</browser_back> + <browser_back mod="longpress">Stop</browser_back> + <return>OSD</return> + <enter>OSD</enter> + <return mod="longpress">PlayPause</return> + <enter mod="longpress">PlayPause</enter> + <m>OSD</m> + <menu>OSD</menu> + <i>Info</i> + <o>CodecInfo</o> + <z>AspectRatio</z> + <zoom>AspectRatio</zoom> + <left>StepBack</left> + <right>StepForward</right> + <escape>Fullscreen</escape> + </keyboard> + </FullscreenGame> <VideoTimeSeek> <keyboard> <return>Select</return> diff --git a/system/keymaps/mouse.xml b/system/keymaps/mouse.xml index 9df7a976d5..b4edcd482d 100644 --- a/system/keymaps/mouse.xml +++ b/system/keymaps/mouse.xml @@ -53,4 +53,9 @@ <rightclick>Info</rightclick> </mouse> </FullscreenVideo> + <FullscreenGame> + <mouse> + <rightclick>Info</rightclick> + </mouse> + </FullscreenGame> </keymap> diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml index 54193b6525..259265ace2 100644 --- a/system/keymaps/remote.xml +++ b/system/keymaps/remote.xml @@ -202,6 +202,20 @@ <pageminus>SkipPrevious</pageminus> </remote> </FullscreenVideo> + <FullscreenGame> + <remote> + <left>StepBack</left> + <right>StepForward</right> + <back>Back</back> + <menu>OSD</menu> + <contentsmenu>OSD</contentsmenu> + <rootmenu>OSD</rootmenu> + <start>OSD</start> + <select>OSD</select> + <title>CodecInfo</title> + <info>Info</info> + </remote> + </FullscreenGame> <VideoTimeSeek> <remote> <select>Select</select> diff --git a/system/keymaps/touchscreen.xml b/system/keymaps/touchscreen.xml index c3135ae791..fee4eb4ad2 100644 --- a/system/keymaps/touchscreen.xml +++ b/system/keymaps/touchscreen.xml @@ -55,6 +55,14 @@ <tap pointers="3">PlayPause</tap> </touch> </FullScreenVideo> + <FullScreenGame> + <touch> + <swipe direction="left">StepBack</swipe> + <swipe direction="right">StepForward</swipe> + <swipe direction="left" pointers="2">Seek(-7)</swipe> + <tap pointers="3">PlayPause</tap> + </touch> + </FullScreenGame> <PlayerControls> <touch> <swipe direction="down" pointers="3">Back</swipe> diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 23f03ddcbb..ff3e3c192c 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -3472,7 +3472,7 @@ PlayBackRet CApplication::PlayFile(CFileItem item, const std::string& player, bo * This should speed up player startup for files on internet filesystems (eg. webdav) and * increase performance on low powered systems (Atom/ARM). */ - if (item.IsVideo()) + if (item.IsVideo() || item.IsGame()) { CJobManager::GetInstance().PauseJobs(); } @@ -4434,7 +4434,7 @@ bool CApplication::ExecuteXBMCAction(std::string actionStr, const CGUIListItemPt } else #endif - if (item.IsAudio() || item.IsVideo()) + if (item.IsAudio() || item.IsVideo() || item.IsGame()) { // an audio or video file PlayFile(item, ""); } diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index 169241b2be..2033c32676 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -43,6 +43,7 @@ #include "pictures/PictureInfoTag.h" #include "music/tags/MusicInfoTag.h" #include "games/addons/savestates/SavestateDefines.h" +#include "games/tags/GameInfoTag.h" #include "guilib/IGUIContainer.h" #include "guilib/GUIWindowManager.h" #include "PlayListPlayer.h" @@ -9255,6 +9256,8 @@ void CGUIInfoManager::SetCurrentItemJob(const CFileItemPtr item) if (item->IsAudio()) SetCurrentSong(*item); + else if (item->IsGame()) + SetCurrentGame(*item); else SetCurrentMovie(*item); @@ -9374,6 +9377,22 @@ void CGUIInfoManager::SetCurrentMovie(CFileItem &item) m_currentMovieThumb = item.GetArt("thumb"); } +void CGUIInfoManager::SetCurrentGame(CFileItem &item) +{ + CLog::Log(LOGDEBUG,"CGUIInfoManager::SetCurrentGame(%s)", item.GetPath().c_str()); + *m_currentFile = item; + + m_currentFile->LoadGameTag(); + if (m_currentFile->GetGameInfoTag()->GetTitle().empty()) + { + // No title in tag, show filename only + m_currentFile->GetGameInfoTag()->SetTitle(CUtil::GetTitleFromPath(m_currentFile->GetPath())); + } + m_currentFile->GetGameInfoTag()->SetLoaded(true); + + m_currentFile->FillInDefaultIcon(); +} + std::string CGUIInfoManager::GetSystemHeatInfo(int info) { if (CTimeUtils::GetFrameTime() - m_lastSysHeatInfoTime >= SYSHEATUPDATEINTERVAL) diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h index 48d1c6f282..fc96e73351 100644 --- a/xbmc/GUIInfoManager.h +++ b/xbmc/GUIInfoManager.h @@ -171,6 +171,7 @@ public: void SetCurrentSlide(CFileItem &item); const CFileItem &GetCurrentSlide() const; void ResetCurrentSlide(); + void SetCurrentGame(CFileItem &item); void SetCurrentSongTag(const MUSIC_INFO::CMusicInfoTag &tag); void SetCurrentVideoTag(const CVideoInfoTag &tag); diff --git a/xbmc/cores/RetroPlayer/CMakeLists.txt b/xbmc/cores/RetroPlayer/CMakeLists.txt new file mode 100644 index 0000000000..d9a8e3ad2a --- /dev/null +++ b/xbmc/cores/RetroPlayer/CMakeLists.txt @@ -0,0 +1,18 @@ +set(SOURCES PixelConverter.cpp + RetroPlayer.cpp + RetroPlayerAudio.cpp + RetroPlayerVideo.cpp) + +set(HEADERS IPixelConverter.h + PixelConverter.h + RetroPlayer.h + RetroPlayerAudio.h + RetroPlayerDefines.h + RetroPlayerVideo.h) + +if(CORE_SYSTEM_NAME STREQUAL rbpi) + list(APPEND SOURCES PixelConverterRBP.cpp) + list(APPEND HEADERS PixelConverterRBP.h) +endif() + +core_add_library(retroplayer) diff --git a/xbmc/cores/RetroPlayer/IPixelConverter.h b/xbmc/cores/RetroPlayer/IPixelConverter.h new file mode 100644 index 0000000000..387f454b03 --- /dev/null +++ b/xbmc/cores/RetroPlayer/IPixelConverter.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "libavutil/pixfmt.h" + +#include <stdint.h> + +struct DVDVideoPicture; + +class IPixelConverter +{ +public: + virtual ~IPixelConverter() = default; + + /*! + * \brief Open a context for converting pixels + * \param pixfmt The source format + * \param target The target format + * \param with The width of the frame + * \param height The height of the frame + * \return true if this object is ready to convert pixels + */ + virtual bool Open(AVPixelFormat pixfmt, AVPixelFormat target, unsigned int width, unsigned int height) = 0; + + /*! + * \brief Release the resources used by this class + */ + virtual void Dispose() = 0; + + /*! + * \brief Send a frame to the pixel converter + * \param pData A pointer to the pixel data + * \param size The size of the data + * \return true if a picture is ready to be read, false otherwise + */ + virtual bool Decode(const uint8_t* pData, unsigned int size) = 0; + + /*! + * \brief Get the results of processing the pixels + * \param dvdVideoPicture a container for the resulting pixel data + */ + virtual void GetPicture(DVDVideoPicture& dvdVideoPicture) = 0; +}; diff --git a/xbmc/cores/RetroPlayer/Makefile.in b/xbmc/cores/RetroPlayer/Makefile.in new file mode 100644 index 0000000000..976b7c7f15 --- /dev/null +++ b/xbmc/cores/RetroPlayer/Makefile.in @@ -0,0 +1,13 @@ +SRCS=PixelConverter.cpp \ + RetroPlayer.cpp \ + RetroPlayerAudio.cpp \ + RetroPlayerVideo.cpp + +ifeq (@CORE_SYSTEM_NAME@,rbpi) + SRCS += PixelConverterRBP.cpp +endif + +LIB=retroplayer.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/cores/RetroPlayer/PixelConverter.cpp b/xbmc/cores/RetroPlayer/PixelConverter.cpp new file mode 100644 index 0000000000..8d8a2dcda2 --- /dev/null +++ b/xbmc/cores/RetroPlayer/PixelConverter.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "PixelConverter.h" +#include "cores/VideoPlayer/DVDClock.h" +#include "cores/VideoPlayer/DVDCodecs/DVDCodecUtils.h" +#include "utils/log.h" + +extern "C" +{ + #include "libswscale/swscale.h" +} + +CPixelConverter::CPixelConverter() : + m_renderFormat(RENDER_FMT_NONE), + m_width(0), + m_height(0), + m_swsContext(nullptr), + m_buf(nullptr) +{ +} + +bool CPixelConverter::Open(AVPixelFormat pixfmt, AVPixelFormat targetfmt, unsigned int width, unsigned int height) +{ + if (pixfmt == targetfmt || width == 0 || height == 0) + return false; + + m_renderFormat = CDVDCodecUtils::EFormatFromPixfmt(targetfmt); + if (m_renderFormat == RENDER_FMT_NONE) + { + CLog::Log(LOGERROR, "%s: Invalid target pixel format: %d", __FUNCTION__, targetfmt); + return false; + } + + m_width = width; + m_height = height; + + m_swsContext = sws_getContext(width, height, pixfmt, + width, height, targetfmt, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (!m_swsContext) + { + CLog::Log(LOGERROR, "%s: Failed to create swscale context", __FUNCTION__); + return false; + } + + m_buf = CDVDCodecUtils::AllocatePicture(width, height); + if (!m_buf) + { + CLog::Log(LOGERROR, "%s: Failed to allocate picture of dimensions %dx%d", __FUNCTION__, width, height); + return false; + } + + return true; +} + +void CPixelConverter::Dispose() +{ + if (m_swsContext) + { + sws_freeContext(m_swsContext); + m_swsContext = nullptr; + } + + if (m_buf) + { + CDVDCodecUtils::FreePicture(m_buf); + m_buf = nullptr; + } +} + +bool CPixelConverter::Decode(const uint8_t* pData, unsigned int size) +{ + if (pData == nullptr || size == 0 || m_swsContext == nullptr) + return false; + + uint8_t* dataMutable = const_cast<uint8_t*>(pData); + + const int stride = size / m_height; + + uint8_t* src[] = { dataMutable, 0, 0, 0 }; + int srcStride[] = { stride, 0, 0, 0 }; + uint8_t* dst[] = { m_buf->data[0], m_buf->data[1], m_buf->data[2], 0 }; + int dstStride[] = { m_buf->iLineSize[0], m_buf->iLineSize[1], m_buf->iLineSize[2], 0 }; + + sws_scale(m_swsContext, src, srcStride, 0, m_height, dst, dstStride); + + return true; +} + +void CPixelConverter::GetPicture(DVDVideoPicture& dvdVideoPicture) +{ + dvdVideoPicture.dts = DVD_NOPTS_VALUE; + dvdVideoPicture.pts = DVD_NOPTS_VALUE; + + for (int i = 0; i < 4; i++) + { + dvdVideoPicture.data[i] = m_buf->data[i]; + dvdVideoPicture.iLineSize[i] = m_buf->iLineSize[i]; + } + + dvdVideoPicture.iFlags = 0; // *not* DVP_FLAG_ALLOCATED + dvdVideoPicture.color_matrix = 4; // CONF_FLAGS_YUVCOEF_BT601 + dvdVideoPicture.color_range = 0; // *not* CONF_FLAGS_YUV_FULLRANGE + dvdVideoPicture.iWidth = m_width; + dvdVideoPicture.iHeight = m_height; + dvdVideoPicture.iDisplayWidth = m_width; //! @todo: Update if aspect ratio changes + dvdVideoPicture.iDisplayHeight = m_height; + dvdVideoPicture.format = m_renderFormat; +} diff --git a/xbmc/cores/RetroPlayer/PixelConverter.h b/xbmc/cores/RetroPlayer/PixelConverter.h new file mode 100644 index 0000000000..fd009ae12c --- /dev/null +++ b/xbmc/cores/RetroPlayer/PixelConverter.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "IPixelConverter.h" +#include "cores/VideoPlayer/VideoRenderers/RenderFormats.h" + +#include <stdint.h> + +struct DVDVideoPicture; +struct SwsContext; + +class CPixelConverter : public IPixelConverter +{ +public: + CPixelConverter(); + virtual ~CPixelConverter() { Dispose(); } + + // implementation of IPixelConverter + virtual bool Open(AVPixelFormat pixfmt, AVPixelFormat target, unsigned int width, unsigned int height) override; + virtual void Dispose() override; + virtual bool Decode(const uint8_t* pData, unsigned int size) override; + virtual void GetPicture(DVDVideoPicture& dvdVideoPicture) override; + +protected: + ERenderFormat m_renderFormat; + unsigned int m_width; + unsigned int m_height; + SwsContext* m_swsContext; + DVDVideoPicture* m_buf; +}; diff --git a/xbmc/cores/RetroPlayer/PixelConverterRBP.cpp b/xbmc/cores/RetroPlayer/PixelConverterRBP.cpp new file mode 100644 index 0000000000..4d159ca91b --- /dev/null +++ b/xbmc/cores/RetroPlayer/PixelConverterRBP.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "PixelConverterRBP.h" +#include "cores/VideoPlayer/DVDClock.h" +#include "cores/VideoPlayer/VideoRenderers/HwDecRender/MMALRenderer.h" +#include "linux/RBP.h" +#include "utils/log.h" + +extern "C" +{ + #include "libavutil/imgutils.h" + #include "libswscale/swscale.h" +} + +std::vector<CPixelConverterRBP::PixelFormatTargetTable> CPixelConverterRBP::pixfmt_target_table = +{ + { AV_PIX_FMT_BGR0, AV_PIX_FMT_BGR0 }, + { AV_PIX_FMT_RGB565LE, AV_PIX_FMT_RGB565LE }, +}; + +std::vector<CPixelConverterRBP::MMALEncodingTable> CPixelConverterRBP::mmal_encoding_table = +{ + { AV_PIX_FMT_YUV420P, MMAL_ENCODING_I420 }, + { AV_PIX_FMT_ARGB, MMAL_ENCODING_ARGB }, + { AV_PIX_FMT_RGBA, MMAL_ENCODING_RGBA }, + { AV_PIX_FMT_ABGR, MMAL_ENCODING_ABGR }, + { AV_PIX_FMT_BGRA, MMAL_ENCODING_ABGR }, + { AV_PIX_FMT_BGR0, MMAL_ENCODING_BGRA }, + { AV_PIX_FMT_RGB24, MMAL_ENCODING_RGB24 }, + { AV_PIX_FMT_BGR24, MMAL_ENCODING_BGR24 }, + { AV_PIX_FMT_RGB565, MMAL_ENCODING_RGB16 }, + { AV_PIX_FMT_RGB565LE, MMAL_ENCODING_RGB16 }, + { AV_PIX_FMT_BGR565, MMAL_ENCODING_BGR16 }, +}; + +CPixelConverterRBP::CPixelConverterRBP() : + m_mmal_format(MMAL_ENCODING_UNKNOWN) +{ +} + +bool CPixelConverterRBP::Open(AVPixelFormat pixfmt, AVPixelFormat targetfmt, unsigned int width, unsigned int height) +{ + if (width == 0 || height == 0) + return false; + + targetfmt = TranslateTargetFormat(pixfmt); + + CLog::Log(LOGDEBUG, "CPixelConverter::%s: pixfmt:%d(%s) targetfmt:%d(%s) %dx%d", __FUNCTION__, pixfmt, av_get_pix_fmt_name(pixfmt), targetfmt, av_get_pix_fmt_name(targetfmt), width, height); + + if (targetfmt == AV_PIX_FMT_NONE) + { + CLog::Log(LOGERROR, "%s: Invalid target pixel format: %d", __FUNCTION__, targetfmt); + return false; + } + + m_renderFormat = RENDER_FMT_MMAL; + m_width = width; + m_height = height; + m_swsContext = sws_getContext(width, height, pixfmt, + width, height, targetfmt, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (!m_swsContext) + { + CLog::Log(LOGERROR, "%s: Failed to create swscale context", __FUNCTION__); + return false; + } + + m_mmal_format = TranslateFormat(targetfmt); + + if (m_mmal_format == MMAL_ENCODING_UNKNOWN) + return false; + + /* Create dummy component with attached pool */ + m_pool = std::make_shared<CMMALPool>(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, false, MMAL_NUM_OUTPUT_BUFFERS, 0, MMAL_ENCODING_I420, MMALStateFFDec); + + return true; +} + +void CPixelConverterRBP::Dispose() +{ + m_pool->Close(); + m_pool = nullptr; + + CPixelConverter::Dispose(); +} + +bool CPixelConverterRBP::Decode(const uint8_t* pData, unsigned int size) +{ + if (pData == nullptr || size == 0 || m_swsContext == nullptr) + return false; + + if (m_buf) + FreePicture(m_buf); + + m_buf = AllocatePicture(m_width, m_height); + if (!m_buf) + { + CLog::Log(LOGERROR, "%s: Failed to allocate picture of dimensions %dx%d", __FUNCTION__, m_width, m_height); + return false; + } + + uint8_t* dataMutable = const_cast<uint8_t*>(pData); + + int bpp; + if (m_mmal_format == MMAL_ENCODING_ARGB || + m_mmal_format == MMAL_ENCODING_RGBA || + m_mmal_format == MMAL_ENCODING_ABGR || + m_mmal_format == MMAL_ENCODING_BGRA) + bpp = 4; + else if (m_mmal_format == MMAL_ENCODING_RGB24 || + m_mmal_format == MMAL_ENCODING_BGR24) + bpp = 4; + else if (m_mmal_format == MMAL_ENCODING_RGB16 || + m_mmal_format == MMAL_ENCODING_BGR16) + bpp = 2; + else + { + CLog::Log(LOGERROR, "CPixelConverter::AllocatePicture, unknown format:%.4s", (char *)&m_mmal_format); + return false; + } + + MMAL::CMMALYUVBuffer *omvb = (MMAL::CMMALYUVBuffer *)m_buf->MMALBuffer; + + const int stride = size / m_height; + + uint8_t* src[] = { dataMutable, 0, 0, 0 }; + int srcStride[] = { stride, 0, 0, 0 }; + uint8_t* dst[] = { (uint8_t *)omvb->gmem->m_arm, 0, 0, 0 }; + int dstStride[] = { (int)omvb->m_aligned_width * bpp, 0, 0, 0 }; + + sws_scale(m_swsContext, src, srcStride, 0, m_height, dst, dstStride); + + return true; +} + +void CPixelConverterRBP::GetPicture(DVDVideoPicture& dvdVideoPicture) +{ + CPixelConverter::GetPicture(dvdVideoPicture); + + dvdVideoPicture.MMALBuffer = m_buf->MMALBuffer; + + MMAL::CMMALYUVBuffer *omvb = (MMAL::CMMALYUVBuffer *)m_buf->MMALBuffer; + + // need to flush ARM cache so GPU can see it + omvb->gmem->Flush(); +} + +DVDVideoPicture* CPixelConverterRBP::AllocatePicture(int iWidth, int iHeight) +{ + MMAL::CMMALYUVBuffer *omvb = nullptr; + DVDVideoPicture* pPicture = new DVDVideoPicture; + + // gpu requirements + int w = (iWidth + 31) & ~31; + int h = (iHeight + 15) & ~15; + if (pPicture && m_pool) + { + m_pool->SetFormat(m_mmal_format, iWidth, iHeight, w, h, 0, nullptr); + + omvb = dynamic_cast<MMAL::CMMALYUVBuffer *>(m_pool->GetBuffer(500)); + if (!omvb || + !omvb->mmal_buffer || + !omvb->gmem || + !omvb->gmem->m_arm) + { + CLog::Log(LOGERROR, "CPixelConverterRBP::AllocatePicture, unable to allocate new video picture, out of memory."); + delete pPicture; + pPicture = nullptr; + } + + CGPUMEM *gmem = omvb->gmem; + omvb->mmal_buffer->data = (uint8_t *)gmem->m_vc_handle; + omvb->mmal_buffer->alloc_size = omvb->mmal_buffer->length = gmem->m_numbytes; + } + else + CLog::Log(LOGERROR, "CPixelConverterRBP::AllocatePicture invalid picture:%p pool:%p", pPicture, m_pool.get()); + + if (pPicture) + { + pPicture->MMALBuffer = omvb; + pPicture->iWidth = iWidth; + pPicture->iHeight = iHeight; + } + + return pPicture; +} + +void CPixelConverterRBP::FreePicture(DVDVideoPicture* pPicture) +{ + if (pPicture) + { + if (pPicture->MMALBuffer) + pPicture->MMALBuffer->Release(); + + delete pPicture; + } + else + CLog::Log(LOGERROR, "CPixelConverterRBP::FreePicture invalid picture:%p", pPicture); +} + +AVPixelFormat CPixelConverterRBP::TranslateTargetFormat(AVPixelFormat pixfmt) +{ + for (const auto& entry : pixfmt_target_table) + { + if (entry.pixfmt == pixfmt) + return entry.targetfmt; + } + return AV_PIX_FMT_NONE; +} + +uint32_t CPixelConverterRBP::TranslateFormat(AVPixelFormat pixfmt) +{ + for (const auto& entry : mmal_encoding_table) + { + if (entry.pixfmt == pixfmt) + return entry.encoding; + } + return MMAL_ENCODING_UNKNOWN; +} diff --git a/xbmc/cores/RetroPlayer/PixelConverterRBP.h b/xbmc/cores/RetroPlayer/PixelConverterRBP.h new file mode 100644 index 0000000000..9924ec6d48 --- /dev/null +++ b/xbmc/cores/RetroPlayer/PixelConverterRBP.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "PixelConverter.h" + +#include <stdint.h> +#include <vector> + +class CMMALPool; +struct DVDVideoPicture; + +class CPixelConverterRBP : public CPixelConverter +{ +public: + CPixelConverterRBP(); + virtual ~CPixelConverterRBP() { Dispose(); } + + // implementation of IPixelConverter + virtual bool Open(AVPixelFormat pixfmt, AVPixelFormat target, unsigned int width, unsigned int height) override; + virtual void Dispose() override; + virtual bool Decode(const uint8_t* pData, unsigned int size) override; + virtual void GetPicture(DVDVideoPicture& dvdVideoPicture) override; + +private: + /*! + * \brief Allocate a new picture (AV_PIX_FMT_YUV420P) + */ + DVDVideoPicture* AllocatePicture(int iWidth, int iHeight); + + /*! + * \brief Free an allocated picture + */ + void FreePicture(DVDVideoPicture* pPicture); + + struct PixelFormatTargetTable + { + AVPixelFormat pixfmt; + AVPixelFormat targetfmt; + }; + + struct MMALEncodingTable + { + AVPixelFormat pixfmt; + uint32_t encoding; + }; + + static std::vector<PixelFormatTargetTable> pixfmt_target_table; + static std::vector<MMALEncodingTable> mmal_encoding_table; + + static AVPixelFormat TranslateTargetFormat(AVPixelFormat pixfmt); + static uint32_t TranslateFormat(AVPixelFormat pixfmt); + + std::shared_ptr<CMMALPool> m_pool; + uint32_t m_mmal_format; +}; diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.cpp b/xbmc/cores/RetroPlayer/RetroPlayer.cpp new file mode 100644 index 0000000000..89960db88a --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayer.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "RetroPlayer.h" +#include "RetroPlayerAudio.h" +#include "RetroPlayerVideo.h" +#include "cores/VideoPlayer/Process/ProcessInfo.h" +#include "games/addons/playback/IGameClientPlayback.h" +#include "games/addons/GameClient.h" +#include "games/tags/GameInfoTag.h" +#include "games/GameUtils.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/MathUtils.h" +#include "utils/StringUtils.h" +#include "windowing/WindowingFactory.h" +#include "FileItem.h" +#include "URL.h" + +using namespace GAME; + +CRetroPlayer::CRetroPlayer(IPlayerCallback& callback) : + IPlayer(callback), + m_renderManager(m_clock, this), + m_processInfo(CProcessInfo::CreateInstance()) +{ + g_Windowing.Register(this); +} + +CRetroPlayer::~CRetroPlayer() +{ + CloseFile(); +} + +bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options) +{ + std::string redactedPath = CURL::GetRedacted(file.GetPath()); + CLog::Log(LOGINFO, "RetroPlayer: Opening: %s", redactedPath.c_str()); + + CSingleLock lock(m_mutex); + + if (IsPlaying()) + CloseFile(); + + PrintGameInfo(file); + + bool bSuccess = false; + + m_gameClient = CGameUtils::OpenGameClient(file); + if (m_gameClient) + { + if (m_gameClient->Initialize()) + { + m_audio.reset(new CRetroPlayerAudio(*m_processInfo)); + m_video.reset(new CRetroPlayerVideo(m_clock, m_renderManager, *m_processInfo)); + if (m_gameClient->OpenFile(file, m_audio.get(), m_video.get())) + { + CLog::Log(LOGDEBUG, "RetroPlayer: Using game client %s", m_gameClient->ID().c_str()); + bSuccess = true; + } + else + CLog::Log(LOGERROR, "RetroPlayer: Failed to open file using %s", m_gameClient->ID().c_str()); + } + else + CLog::Log(LOGERROR, "RetroPlayer: Failed to initialize %s", m_gameClient->ID().c_str()); + } + else + CLog::Log(LOGERROR, "RetroPlayer: Can't find add-on for game file"); + + if (bSuccess) + { + if (file.m_lStartOffset == STARTOFFSET_RESUME && file.HasGameInfoTag()) + { + std::string redactedSavestatePath = CURL::GetRedacted(file.GetGameInfoTag()->GetSavestate()); + CLog::Log(LOGDEBUG, "RetroPlayer: Loading savestate %s", redactedSavestatePath.c_str()); + + if (!SetPlayerState(file.GetGameInfoTag()->GetSavestate())) + CLog::Log(LOGERROR, "RetroPlayer: Failed to load savestate"); + } + + SetSpeed(1); + + m_callback.OnPlayBackStarted(); + } + else + { + m_gameClient.reset(); + m_audio.reset(); + m_video.reset(); + } + + return bSuccess; +} + +bool CRetroPlayer::CloseFile(bool reopen /* = false */) +{ + CLog::Log(LOGDEBUG, "RetroPlayer: Closing file"); + + CSingleLock lock(m_mutex); + + if (m_gameClient) + { + m_gameClient->CloseFile(); + m_gameClient->Unload(); + m_gameClient.reset(); + m_callback.OnPlayBackEnded(); + } + + m_audio.reset(); + m_video.reset(); + + return true; +} + +bool CRetroPlayer::IsPlaying() const +{ + if (m_gameClient) + return m_gameClient->IsPlaying(); + return false; +} + +bool CRetroPlayer::CanPause() +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->CanPause(); + return false; +} + +void CRetroPlayer::Pause() +{ + if (!CanPause()) + return; + + if (m_gameClient) + { + m_gameClient->GetPlayback()->PauseUnpause(); + m_audio->Enable(m_gameClient->GetPlayback()->GetSpeed() == 1.0); + } +} + +bool CRetroPlayer::CanSeek() +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->CanSeek(); + return false; +} + +void CRetroPlayer::Seek(bool bPlus /* = true */, + bool bLargeStep /* = false */, + bool bChapterOverride /* = false */) +{ + if (!CanSeek()) + return; + + if (m_gameClient) + { + //! @todo + /* + if (bPlus) + { + if (bLargeStep) + m_gameClient->GetPlayback()->BigSkipForward(); + else + m_gameClient->GetPlayback()->SmallSkipForward(); + } + else + { + if (bLargeStep) + m_gameClient->GetPlayback()->BigSkipBackward(); + else + m_gameClient->GetPlayback()->SmallSkipBackward(); + } + */ + } +} + +void CRetroPlayer::SeekPercentage(float fPercent /* = 0 */) +{ + if (!CanSeek()) + return; + + if (fPercent < 0.0f ) + fPercent = 0.0f; + else if (fPercent > 100.0f) + fPercent = 100.0f; + + int64_t totalTime = GetTotalTime(); + if (totalTime != 0) + SeekTime(static_cast<int64_t>(totalTime * fPercent / 100.0f)); +} + +float CRetroPlayer::GetPercentage() +{ + if (m_gameClient) + { + const float timeMs = static_cast<float>(m_gameClient->GetPlayback()->GetTimeMs()); + const float totalMs = static_cast<float>(m_gameClient->GetPlayback()->GetTotalTimeMs()); + + if (totalMs != 0.0f) + return timeMs / totalMs * 100.0f; + } + + return 0.0f; +} + +float CRetroPlayer::GetCachePercentage() +{ + if (m_gameClient) + { + const float cacheMs = static_cast<float>(m_gameClient->GetPlayback()->GetCacheTimeMs()); + const float totalMs = static_cast<float>(m_gameClient->GetPlayback()->GetTotalTimeMs()); + + if (totalMs != 0.0f) + return cacheMs / totalMs * 100.0f; + } + return 0.0f; +} + +void CRetroPlayer::SetMute(bool bOnOff) +{ + if (m_audio) + m_audio->Enable(!bOnOff); +} + +void CRetroPlayer::SeekTime(int64_t iTime /* = 0 */) +{ + if (!CanSeek()) + return; + + if (m_gameClient) + { + m_gameClient->GetPlayback()->SeekTimeMs(static_cast<unsigned int>(iTime)); + m_audio->Enable(m_gameClient->GetPlayback()->GetSpeed() == 1.0); + } +} + +bool CRetroPlayer::SeekTimeRelative(int64_t iTime) +{ + if (!CanSeek()) + return false; + + SeekTime(GetTime() + iTime); + + return true; +} + +int64_t CRetroPlayer::GetTime() +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->GetTimeMs(); + return 0; +} + +int64_t CRetroPlayer::GetTotalTime() +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->GetTotalTimeMs(); + return 0; +} + +bool CRetroPlayer::GetStreamDetails(CStreamDetails &details) +{ + //! @todo + return false; +} + +void CRetroPlayer::SetSpeed(float speed) +{ + if (m_gameClient) + { + if (m_gameClient->GetPlayback()->GetSpeed() != speed) + { + if (speed == 1.0f) + m_callback.OnPlayBackResumed(); + else if (speed == 0.0f) + m_callback.OnPlayBackPaused(); + } + + m_gameClient->GetPlayback()->SetSpeed(speed); + m_audio->Enable(m_gameClient->GetPlayback()->GetSpeed() == 1.0); + } +} + +float CRetroPlayer::GetSpeed() +{ + if (m_gameClient) + return static_cast<float>(m_gameClient->GetPlayback()->GetSpeed()); + return 0; +} + +std::string CRetroPlayer::GetPlayerState() +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->CreateManualSavestate(); + return ""; +} + +bool CRetroPlayer::SetPlayerState(const std::string& state) +{ + if (m_gameClient) + return m_gameClient->GetPlayback()->LoadSavestate(state); + return false; +} + +bool CRetroPlayer::Supports(EINTERLACEMETHOD method) +{ + return m_processInfo->Supports(method); +} + +EINTERLACEMETHOD CRetroPlayer::GetDeinterlacingMethodDefault() +{ + return m_processInfo->GetDeinterlacingMethodDefault(); +} + +void CRetroPlayer::UpdateClockSync(bool enabled) +{ + m_processInfo->SetRenderClockSync(enabled); +} + +void CRetroPlayer::UpdateRenderInfo(CRenderInfo &info) +{ + m_processInfo->UpdateRenderInfo(info); +} + +void CRetroPlayer::PrintGameInfo(const CFileItem &file) const +{ + const CGameInfoTag *tag = file.GetGameInfoTag(); + if (tag) + { + CLog::Log(LOGDEBUG, "RetroPlayer: ---------------------------------------"); + CLog::Log(LOGDEBUG, "RetroPlayer: Game tag loaded"); + CLog::Log(LOGDEBUG, "RetroPlayer: URL: %s", tag->GetURL().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Title: %s", tag->GetTitle().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Platform: %s", tag->GetPlatform().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Genres: %s", StringUtils::Join(tag->GetGenres(), ", ").c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Developer: %s", tag->GetDeveloper().c_str()); + if (tag->GetYear() > 0) + CLog::Log(LOGDEBUG, "RetroPlayer: Year: %u", tag->GetYear()); + CLog::Log(LOGDEBUG, "RetroPlayer: Game Code: %s", tag->GetID().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Region: %s", tag->GetRegion().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Publisher: %s", tag->GetPublisher().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Format: %s", tag->GetFormat().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Cartridge type: %s", tag->GetCartridgeType().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Save state: %s", tag->GetSavestate().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: Game client: %s", tag->GetGameClient().c_str()); + CLog::Log(LOGDEBUG, "RetroPlayer: ---------------------------------------"); + } +} diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.h b/xbmc/cores/RetroPlayer/RetroPlayer.h new file mode 100644 index 0000000000..f4081b89c3 --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayer.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "cores/IPlayer.h" +#include "cores/VideoPlayer/VideoRenderers/RenderManager.h" +#include "cores/VideoPlayer/DVDClock.h" +#include "games/GameTypes.h" +#include "guilib/DispResource.h" +#include "threads/CriticalSection.h" + +#include <memory> + +class CProcessInfo; + +namespace GAME +{ + class CRetroPlayerAudio; + class CRetroPlayerVideo; + + class CRetroPlayer : public IPlayer, + public IRenderMsg, + public IDispResource + { + public: + CRetroPlayer(IPlayerCallback& callback); + virtual ~CRetroPlayer(); + + // implementation of IPlayer + //virtual bool Initialize(TiXmlElement* pConfig) override { return true; } + virtual bool OpenFile(const CFileItem& file, const CPlayerOptions& options) override; + //virtual bool QueueNextFile(const CFileItem &file) override { return false; } + //virtual void OnNothingToQueueNotify() override { } + virtual bool CloseFile(bool reopen = false) override; + virtual bool IsPlaying() const override; + virtual bool CanPause() override; + virtual void Pause() override; + virtual bool HasVideo() const override { return true; } + virtual bool HasAudio() const override { return true; } + virtual bool HasGame() const override { return true; } + //virtual bool HasRDS() const override { return false; } + //virtual bool IsPassthrough() const override { return false;} + virtual bool CanSeek() override; + virtual void Seek(bool bPlus = true, bool bLargeStep = false, bool bChapterOverride = false) override; + //virtual bool SeekScene(bool bPlus = true) override { return false; } + virtual void SeekPercentage(float fPercent = 0) override; + virtual float GetPercentage() override; + virtual float GetCachePercentage() override; + virtual void SetMute(bool bOnOff) override; + //virtual void SetVolume(float volume) override { } + //virtual void SetDynamicRangeCompression(long drc) override { } + //virtual bool CanRecord() override { return false; } + //virtual bool IsRecording() override { return false; } + //virtual bool Record(bool bOnOff) override { return false; } + //virtual void SetAVDelay(float fValue = 0.0f) override { return; } + //virtual float GetAVDelay() override { return 0.0f; } + //virtual void SetSubTitleDelay(float fValue = 0.0f) override { } + //virtual float GetSubTitleDelay() override { return 0.0f; } + //virtual int GetSubtitleCount() override { return 0; } + //virtual int GetSubtitle() override { return -1; } + //virtual void GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info) override { } + //virtual void SetSubtitle(int iStream) override { } + //virtual bool GetSubtitleVisible() override { return false; } + //virtual void SetSubtitleVisible(bool bVisible) override { } + //virtual void AddSubtitle(const std::string& strSubPath) override { } + //virtual int GetAudioStreamCount() override { return 0; } + //virtual int GetAudioStream() override { return -1; } + //virtual void SetAudioStream(int iStream) override { } + //virtual void GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info) override { } + //virtual int GetVideoStream() const override { return -1; } + //virtual int GetVideoStreamCount() const override { return 0; } + //virtual void GetVideoStreamInfo(int streamId, SPlayerVideoStreamInfo &info) override { } + //virtual void SetVideoStream(int iStream) override { } + //virtual TextCacheStruct_t* GetTeletextCache() override { return NULL; } + //virtual void LoadPage(int p, int sp, unsigned char* buffer) override { } + //virtual std::string GetRadioText(unsigned int line) override { return ""; } + //virtual int GetChapterCount() override { return 0; } + //virtual int GetChapter() override { return -1; } + //virtual void GetChapterName(std::string& strChapterName, int chapterIdx = -1) override { return; } + //virtual int64_t GetChapterPos(int chapterIdx = -1) override { return 0; } + //virtual int SeekChapter(int iChapter) override { return -1; } + //virtual float GetActualFPS() override { return 0.0f; } + virtual void SeekTime(int64_t iTime = 0) override; + virtual bool SeekTimeRelative(int64_t iTime) override; + virtual int64_t GetTime() override; + //virtual void SetTime(int64_t time) override { } // Only used by Air Tunes Server + virtual int64_t GetTotalTime() override; + //virtual void SetTotalTime(int64_t time) override { } // Only used by Air Tunes Server + //virtual int GetSourceBitrate() override { return 0; } + virtual bool GetStreamDetails(CStreamDetails &details) override; + virtual void SetSpeed(float speed) override; + virtual float GetSpeed() override; + //virtual bool SkipNext() override { return false; } + //virtual bool IsCaching() const override { return false; } + //virtual int GetCacheLevel() const override { return -1; } + //virtual bool IsInMenu() const override { return false; } + //virtual bool HasMenu() const override { return false; } + //virtual void DoAudioWork() override { } + //virtual bool OnAction(const CAction &action) override { return false; } + virtual std::string GetPlayerState() override; + virtual bool SetPlayerState(const std::string& state) override; + //virtual std::string GetPlayingTitle() override { return ""; } + //virtual bool SwitchChannel(const PVR::CPVRChannelPtr &channel) override { return false; } + //virtual void GetAudioCapabilities(std::vector<int> &audioCaps) override { audioCaps.assign(1,IPC_AUD_ALL); } + //virtual void GetSubtitleCapabilities(std::vector<int> &subCaps) override { subCaps.assign(1,IPC_SUBS_ALL); } + virtual void FrameMove() override { m_renderManager.FrameMove(); } + virtual void Render(bool clear, uint32_t alpha = 255, bool gui = true) override { m_renderManager.Render(clear, 0, alpha, gui); } + virtual void FlushRenderer() override { m_renderManager.Flush(); } + virtual void SetRenderViewMode(int mode) override { m_renderManager.SetViewMode(mode); } + virtual float GetRenderAspectRatio() override { return m_renderManager.GetAspectRatio(); } + virtual void TriggerUpdateResolution() override { m_renderManager.TriggerUpdateResolution(0.0f, 0, 0); } + virtual bool IsRenderingVideo() override { return m_renderManager.IsConfigured(); } + virtual bool IsRenderingGuiLayer() override { return m_renderManager.IsGuiLayer(); } + virtual bool IsRenderingVideoLayer() override { return m_renderManager.IsVideoLayer(); } + virtual bool Supports(EINTERLACEMETHOD method) override; + virtual EINTERLACEMETHOD GetDeinterlacingMethodDefault() override; + virtual bool Supports(ESCALINGMETHOD method) override { return m_renderManager.Supports(method); } + virtual bool Supports(ERENDERFEATURE feature) override { return m_renderManager.Supports(feature); } + virtual unsigned int RenderCaptureAlloc() override { return m_renderManager.AllocRenderCapture(); } + virtual void RenderCaptureRelease(unsigned int captureId) override { m_renderManager.ReleaseRenderCapture(captureId); } + virtual void RenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags) override { m_renderManager.StartRenderCapture(captureId, width, height, flags); } + virtual bool RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size) override { return m_renderManager.RenderCaptureGetPixels(captureId, millis, buffer, size); } + + // implementation of IRenderMsg + virtual void VideoParamsChange() override { } + virtual void GetDebugInfo(std::string &audio, std::string &video, std::string &general) override { } + virtual void UpdateClockSync(bool enabled) override; + virtual void UpdateRenderInfo(CRenderInfo &info) override; + + // implementation of IDispResource + //virtual void OnLostDisplay() override { } + //virtual void OnResetDisplay() override { } + //virtual void OnAppFocusChange(bool focus) override { } + + private: + /** + * \brief Dump game information (if any) to the debug log. + */ + void PrintGameInfo(const CFileItem &file) const; + + CDVDClock m_clock; + CRenderManager m_renderManager; + std::unique_ptr<CProcessInfo> m_processInfo; + std::unique_ptr<CRetroPlayerAudio> m_audio; + std::unique_ptr<CRetroPlayerVideo> m_video; + GameClientPtr m_gameClient; + CCriticalSection m_mutex; + }; +} diff --git a/xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp b/xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp new file mode 100644 index 0000000000..8679b5c790 --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "RetroPlayerAudio.h" +#include "RetroPlayerDefines.h" +#include "cores/AudioEngine/AEFactory.h" +#include "cores/AudioEngine/Interfaces/AEStream.h" +#include "cores/AudioEngine/Utils/AEChannelInfo.h" +#include "cores/AudioEngine/Utils/AEUtil.h" +#include "cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodec.h" +#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" +#include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h" +#include "cores/VideoPlayer/DVDClock.h" +#include "cores/VideoPlayer/DVDStreamInfo.h" +#include "cores/VideoPlayer/Process/ProcessInfo.h" +#include "threads/Thread.h" +#include "utils/log.h" + +using namespace GAME; + +CRetroPlayerAudio::CRetroPlayerAudio(CProcessInfo& processInfo) : + m_processInfo(processInfo), + m_pAudioStream(nullptr), + m_bAudioEnabled(true) +{ +} + +CRetroPlayerAudio::~CRetroPlayerAudio() +{ + CloseStream(); +} + +unsigned int CRetroPlayerAudio::NormalizeSamplerate(unsigned int samplerate) const +{ + //! @todo List comes from AESinkALSA.cpp many moons ago + static unsigned int sampleRateList[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 0 }; + + for (unsigned int *rate = sampleRateList; ; rate++) + { + const unsigned int thisValue = *rate; + const unsigned int nextValue = *(rate + 1); + + if (nextValue == 0) + { + // Reached the end of our list + return thisValue; + } + + if (samplerate < (thisValue + nextValue) / 2) + { + // samplerate is between this rate and the next, so use this rate + return thisValue; + } + } + + return samplerate; // Shouldn't happen +} + +bool CRetroPlayerAudio::OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout) +{ + if (m_pAudioStream != nullptr) + CloseStream(); + + CLog::Log(LOGINFO, "RetroPlayerAudio: Creating audio stream, sample rate = %d", samplerate); + + // Resampling is not supported + if (NormalizeSamplerate(samplerate) != samplerate) + { + CLog::Log(LOGERROR, "RetroPlayerAudio: Resampling to %d not supported", NormalizeSamplerate(samplerate)); + return false; + } + + AEAudioFormat audioFormat; + audioFormat.m_dataFormat = format; + audioFormat.m_sampleRate = samplerate; + audioFormat.m_channelLayout = channelLayout; + m_pAudioStream = CAEFactory::MakeStream(audioFormat); + + if (!m_pAudioStream) + { + CLog::Log(LOGERROR, "RetroPlayerAudio: Failed to create audio stream"); + return false; + } + + return true; +} + +bool CRetroPlayerAudio::OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout) +{ + CDemuxStreamAudio audioStream; + + // Stream + audioStream.uniqueId = GAME_STREAM_AUDIO_ID; + audioStream.codec = codec; + audioStream.type = STREAM_AUDIO; + audioStream.source = STREAM_SOURCE_DEMUX; + audioStream.realtime = true; + + // Audio + audioStream.iChannels = channelLayout.Count(); + audioStream.iSampleRate = samplerate; + audioStream.iChannelLayout = CAEUtil::GetAVChannelLayout(channelLayout); + + CDVDStreamInfo hint(audioStream); + m_pAudioCodec.reset(CDVDFactoryCodec::CreateAudioCodec(hint, m_processInfo, false)); + + if (!m_pAudioCodec) + { + CLog::Log(LOGERROR, "RetroPlayerAudio: Failed to create audio codec (codec=%d, samplerate=%u)", codec, samplerate); + return false; + } + + return true; +} + +void CRetroPlayerAudio::AddData(const uint8_t* data, unsigned int size) +{ + if (m_bAudioEnabled) + { + if (m_pAudioCodec) + { + int consumed = m_pAudioCodec->Decode(const_cast<uint8_t*>(data), size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); + if (consumed < 0) + { + CLog::Log(LOGERROR, "CRretroPlayerAudio::AddData - Decode Error (%d)", consumed); + m_pAudioCodec.reset(); + return; + } + + DVDAudioFrame audioframe; + m_pAudioCodec->GetData(audioframe); + + if (audioframe.nb_frames != 0) + { + // Open audio stream if not already open + if (!m_pAudioStream) + { + const AEAudioFormat& format = audioframe.format; + if (!OpenPCMStream(format.m_dataFormat, format.m_sampleRate, format.m_channelLayout)) + m_pAudioCodec.reset(); + } + + if (m_pAudioStream) + m_pAudioStream->AddData(audioframe.data, 0, audioframe.nb_frames); + } + } + else if (m_pAudioStream) + { + const unsigned int frameSize = m_pAudioStream->GetChannelCount() * (CAEUtil::DataFormatToBits(m_pAudioStream->GetDataFormat()) >> 3); + m_pAudioStream->AddData(&data, 0, size / frameSize); + } + } +} + +void CRetroPlayerAudio::CloseStream() +{ + if (m_pAudioCodec) + { + m_pAudioCodec->Dispose(); + m_pAudioCodec.reset(); + } + if (m_pAudioStream) + { + CAEFactory::FreeStream(m_pAudioStream); + m_pAudioStream = nullptr; + } +} diff --git a/xbmc/cores/RetroPlayer/RetroPlayerAudio.h b/xbmc/cores/RetroPlayer/RetroPlayerAudio.h new file mode 100644 index 0000000000..c7913e292b --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayerAudio.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "games/addons/GameClientCallbacks.h" + +#include <memory> + +class CDVDAudioCodec; +class CProcessInfo; +class IAEStream; + +namespace GAME +{ + class CRetroPlayerAudio : public IGameAudioCallback + { + public: + CRetroPlayerAudio(CProcessInfo& processInfo); + virtual ~CRetroPlayerAudio(); + + // implementation of IGameAudioCallback + virtual unsigned int NormalizeSamplerate(unsigned int samplerate) const override; + virtual bool OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout) override; + virtual bool OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout) override; + virtual void AddData(const uint8_t* data, unsigned int size) override; + virtual void CloseStream() override; + + void Enable(bool bEnabled) { m_bAudioEnabled = bEnabled; } + + private: + CProcessInfo& m_processInfo; + IAEStream* m_pAudioStream; + std::unique_ptr<CDVDAudioCodec> m_pAudioCodec; + bool m_bAudioEnabled; + }; +} diff --git a/xbmc/cores/RetroPlayer/RetroPlayerDefines.h b/xbmc/cores/RetroPlayer/RetroPlayerDefines.h new file mode 100644 index 0000000000..092aa24816 --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayerDefines.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#define GAME_STREAM_VIDEO_ID 1 +#define GAME_STREAM_AUDIO_ID 2 diff --git a/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp b/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp new file mode 100644 index 0000000000..0eff2b078e --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "RetroPlayerVideo.h" +#include "RetroPlayerDefines.h" +#include "PixelConverter.h" +#include "PixelConverterRBP.h" +#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h" +#include "cores/VideoPlayer/DVDCodecs/DVDCodecUtils.h" +#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" +#include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h" +#include "cores/VideoPlayer/VideoRenderers/RenderFlags.h" +#include "cores/VideoPlayer/VideoRenderers/RenderManager.h" +#include "cores/VideoPlayer/DVDStreamInfo.h" +#include "utils/log.h" + +#include <atomic> //! @todo + +using namespace GAME; + +CRetroPlayerVideo::CRetroPlayerVideo(CDVDClock& clock, CRenderManager& renderManager, CProcessInfo& processInfo) : + //CThread("RetroPlayerVideo"), + m_clock(clock), + m_renderManager(renderManager), + m_processInfo(processInfo), + m_framerate(0.0), + m_orientation(0), + m_bConfigured(false), + m_droppedFrames(0) +{ + m_renderManager.PreInit(); +} + +CRetroPlayerVideo::~CRetroPlayerVideo() +{ + CloseStream(); + m_renderManager.UnInit(); +} + +bool CRetroPlayerVideo::OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, double framerate, unsigned int orientationDeg) +{ + CLog::Log(LOGINFO, "RetroPlayerVideo: Creating video stream with pixel format: %i, %dx%d", pixfmt, width, height); + + m_framerate = framerate; + m_orientation = orientationDeg; + m_bConfigured = false; + m_droppedFrames = 0; + +#ifdef TARGET_RASPBERRY_PI + m_pixelConverter.reset(new CPixelConverterRBP); +#else + m_pixelConverter.reset(new CPixelConverter); +#endif + + if (m_pixelConverter->Open(pixfmt, AV_PIX_FMT_YUV420P, width, height)) + { + //! @todo + //m_processInfo.SetVideoPixelFormat(CDVDVideoCodecFFmpeg::GetPixelFormatName(pixfmt)); + m_processInfo.SetVideoDimensions(width, height); + m_processInfo.SetVideoFps(static_cast<float>(framerate)); + return true; + } + + m_pixelConverter.reset(); + + return false; +} + +bool CRetroPlayerVideo::OpenEncodedStream(AVCodecID codec) +{ + CDemuxStreamVideo videoStream; + + // Stream + videoStream.uniqueId = GAME_STREAM_VIDEO_ID; + videoStream.codec = codec; + videoStream.type = STREAM_VIDEO; + videoStream.source = STREAM_SOURCE_DEMUX; + videoStream.realtime = true; + + // Video + //! @todo Needed? + /* + videoStream.iFpsScale = 1000; + videoStream.iFpsRate = static_cast<int>(framerate * 1000); + videoStream.iHeight = height; + videoStream.iWidth = width; + videoStream.fAspect = static_cast<float>(width) / static_cast<float>(height); + videoStream.iOrientation = orientationDeg; + */ + + CDVDStreamInfo hint(videoStream); + m_pVideoCodec.reset(CDVDFactoryCodec::CreateVideoCodec(hint, m_processInfo, m_renderManager.GetRenderInfo())); + + return m_pVideoCodec.get() != nullptr; +} + +void CRetroPlayerVideo::AddData(const uint8_t* data, unsigned int size) +{ + DVDVideoPicture picture = { }; + + if (GetPicture(data, size, picture)) + { + if (!Configure(picture)) + { + CLog::Log(LOGERROR, "RetroPlayerVideo: Failed to configure renderer"); + CloseStream(); + } + else + { + SendPicture(picture); + } + } +} + +void CRetroPlayerVideo::CloseStream() +{ + m_renderManager.Flush(); + m_pixelConverter.reset(); + m_pVideoCodec.reset(); +} + +bool CRetroPlayerVideo::Configure(DVDVideoPicture& picture) +{ + if (!m_bConfigured) + { + // Determine RenderManager flags + unsigned int flags = CONF_FLAGS_YUVCOEF_BT601 | // color_matrix = 4 + CONF_FLAGS_FULLSCREEN; // Allow fullscreen + + const int buffers = 1; //! @todo + + m_bConfigured = m_renderManager.Configure(picture, static_cast<float>(m_framerate), flags, m_orientation, buffers); + + if (m_bConfigured) + { + // Update process info + AVPixelFormat pixfmt = static_cast<AVPixelFormat>(CDVDCodecUtils::PixfmtFromEFormat(picture.format)); + if (pixfmt != AV_PIX_FMT_NONE) + { + //! @todo + //m_processInfo.SetVideoPixelFormat(CDVDVideoCodecFFmpeg::GetPixelFormatName(pixfmt)); + } + m_processInfo.SetVideoDimensions(picture.iWidth, picture.iHeight); + m_processInfo.SetVideoFps(static_cast<float>(m_framerate)); + } + } + + return m_bConfigured; +} + +bool CRetroPlayerVideo::GetPicture(const uint8_t* data, unsigned int size, DVDVideoPicture& picture) +{ + bool bHasPicture = false; + + if (m_pixelConverter) + { + int lateframes; + double renderPts; + int queued, discard; + m_renderManager.GetStats(lateframes, renderPts, queued, discard); + + // Drop frame if another is queued + const bool bDropped = (queued > 0); + + if (!bDropped) + { + if (m_pixelConverter->Decode(data, size)) + { + m_pixelConverter->GetPicture(picture); + bHasPicture = true; + } + } + } + else if (m_pVideoCodec) + { + int iDecoderState = m_pVideoCodec->Decode(const_cast<uint8_t*>(data), size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); + if (iDecoderState & VC_PICTURE) + { + m_pVideoCodec->ClearPicture(&picture); + + if (m_pVideoCodec->GetPicture(&picture)) + { + // Drop frame if requested by the decoder + const bool bDropped = (picture.iFlags & DVP_FLAG_DROPPED) != 0; + + if (!bDropped) + bHasPicture = true; + } + } + } + + return bHasPicture; +} + +void CRetroPlayerVideo::SendPicture(DVDVideoPicture& picture) +{ + std::atomic_bool bAbortOutput(false); //! @todo + + int index = m_renderManager.AddVideoPicture(picture); + if (index < 0) + { + // Video device might not be done yet, drop the frame + m_droppedFrames++; + } + else + { + m_renderManager.FlipPage(bAbortOutput, 0.0, VS_INTERLACEMETHOD_NONE, FS_NONE, false); + } +} diff --git a/xbmc/cores/RetroPlayer/RetroPlayerVideo.h b/xbmc/cores/RetroPlayer/RetroPlayerVideo.h new file mode 100644 index 0000000000..f9dc3ea94a --- /dev/null +++ b/xbmc/cores/RetroPlayer/RetroPlayerVideo.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012-2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "games/addons/GameClientCallbacks.h" +//#include "threads/Thread.h" + +#include <memory> + +class CDVDClock; +class CDVDVideoCodec; +class CPixelConverter; +class CProcessInfo; +class CRenderManager; +struct DVDVideoPicture; + +namespace GAME +{ + class CRetroPlayerVideo : public IGameVideoCallback + //protected CThread + { + public: + CRetroPlayerVideo(CDVDClock& m_clock, CRenderManager& m_renderManager, CProcessInfo& m_processInfo); + + virtual ~CRetroPlayerVideo(); + + // implementation of IGameVideoCallback + virtual bool OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, double framerate, unsigned int orientationDeg) override; + virtual bool OpenEncodedStream(AVCodecID codec) override; + virtual void AddData(const uint8_t* data, unsigned int size) override; + virtual void CloseStream() override; + + /* + protected: + // implementation of CThread + virtual void Process(void); + */ + + private: + bool Configure(DVDVideoPicture& picture); + bool GetPicture(const uint8_t* data, unsigned int size, DVDVideoPicture& picture); + void SendPicture(DVDVideoPicture& picture); + + // Construction parameters + CDVDClock& m_clock; + CRenderManager& m_renderManager; + CProcessInfo& m_processInfo; + + // Stream properties + double m_framerate; + unsigned int m_orientation; // Degrees counter-clockwise + bool m_bConfigured; // Need first picture to configure the render manager + unsigned int m_droppedFrames; + std::unique_ptr<CPixelConverter> m_pixelConverter; + std::unique_ptr<CDVDVideoCodec> m_pVideoCodec; + }; +} diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h index 15a6570a27..69b14eda13 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h +++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h @@ -24,6 +24,7 @@ #include "PlayerCoreFactory.h" #include "cores/VideoPlayer/VideoPlayer.h" #include "cores/paplayer/PAPlayer.h" +#include "cores/RetroPlayer/RetroPlayer.h" #include "cores/ExternalPlayer/ExternalPlayer.h" #ifdef HAS_UPNP #include "network/upnp/UPnPPlayer.h" @@ -93,6 +94,10 @@ public: { pPlayer = new PAPlayer(callback); } + else if (m_type.compare("game") == 0) + { + pPlayer = new GAME::CRetroPlayer(callback); + } else if (m_type.compare("external") == 0) { pPlayer = new CExternalPlayer(callback); diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp index 896b865895..9abf1cbd76 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp +++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp @@ -116,9 +116,9 @@ void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::stri // Process defaults - // Set video default player. Check whether it's video first (overrule audio check) - // Also push these players in case it is NOT audio either - if (item.IsVideo() || !item.IsAudio()) + // Set video default player. Check whether it's video first (overrule audio and + // game check). Also push these players in case it is NOT audio or game either. + if (item.IsVideo() || (!item.IsAudio() && !item.IsGame())) { int idx = GetPlayerIndex("videodefaultplayer"); if (idx > -1) @@ -146,6 +146,12 @@ void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::stri GetPlayers(players, true, true); // Audio & video players } + if (item.IsGame()) + { + CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding retroplayer"); + players.push_back("RetroPlayer"); + } + CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: added %" PRIuS" players", players.size()); } @@ -323,6 +329,9 @@ bool CPlayerCoreFactory::LoadConfiguration(const std::string &file, bool clear) CPlayerCoreConfig* paplayer = new CPlayerCoreConfig("PAPlayer", "music", nullptr); paplayer->m_bPlaysAudio = true; m_vecPlayerConfigs.push_back(paplayer); + + CPlayerCoreConfig* retroPlayer = new CPlayerCoreConfig("RetroPlayer", "game", nullptr); + m_vecPlayerConfigs.push_back(retroPlayer); } if (!pConfig || strcmpi(pConfig->Value(), "playercorefactory") != 0) diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp index d1ffc1f736..53aec7e604 100644 --- a/xbmc/guilib/GUIWindowManager.cpp +++ b/xbmc/guilib/GUIWindowManager.cpp @@ -678,6 +678,15 @@ void CGUIWindowManager::PreviousWindow() // ok to go to the previous window now + // pause game when leaving fullscreen or resume game when entering fullscreen + if (g_application.m_pPlayer->IsPlayingGame()) + { + if (previousWindow == WINDOW_FULLSCREEN_VIDEO && g_application.m_pPlayer->IsPaused()) + g_application.OnAction(ACTION_PAUSE); + else if (currentWindow == WINDOW_FULLSCREEN_VIDEO && !g_application.m_pPlayer->IsPaused()) + g_application.OnAction(ACTION_PAUSE); + } + // tell our info manager which window we are going to g_infoManager.SetNextWindow(previousWindow); @@ -790,6 +799,15 @@ void CGUIWindowManager::ActivateWindow_Internal(int iWindowID, const std::vector return; } + // pause game when leaving fullscreen or resume game when entering fullscreen + if (g_application.m_pPlayer->IsPlayingGame()) + { + if (GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO && !g_application.m_pPlayer->IsPaused()) + g_application.OnAction(ACTION_PAUSE); + else if (iWindowID == WINDOW_FULLSCREEN_VIDEO && g_application.m_pPlayer->IsPaused()) + g_application.OnAction(ACTION_PAUSE); + } + g_infoManager.SetNextWindow(iWindowID); // deactivate any window @@ -1406,6 +1424,9 @@ int CGUIWindowManager::GetActiveWindowID() // special casing for numeric seek else if (CSeekHandler::GetInstance().HasTimeCode()) iWin = WINDOW_VIDEO_TIME_SEEK; + // check if a game is playing + else if (g_application.m_pPlayer->IsPlayingGame()) + iWin = WINDOW_FULLSCREEN_GAME; } if (iWin == WINDOW_VISUALISATION) { diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp index e882c2a73c..06a0ec3ab1 100644 --- a/xbmc/input/ButtonTranslator.cpp +++ b/xbmc/input/ButtonTranslator.cpp @@ -391,6 +391,7 @@ static const ActionMapping windows[] = { "fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO }, { "fullscreenlivetv" , WINDOW_FULLSCREEN_LIVETV }, // virtual window/keymap section for PVR specific bindings in fullscreen playback (which internally uses WINDOW_FULLSCREEN_VIDEO) { "fullscreenradio" , WINDOW_FULLSCREEN_RADIO }, // virtual window for fullscreen radio, uses WINDOW_VISUALISATION as fallback + { "fullscreengame" , WINDOW_FULLSCREEN_GAME }, // virtual window for fullscreen games, uses WINDOW_FULLSCREEN_VIDEO as fallback { "visualisation" , WINDOW_VISUALISATION }, { "slideshow" , WINDOW_SLIDESHOW }, { "weather" , WINDOW_WEATHER }, @@ -444,7 +445,8 @@ static const ActionMapping touchcommands[] = static const WindowMapping fallbackWindows[] = { { WINDOW_FULLSCREEN_LIVETV , WINDOW_FULLSCREEN_VIDEO }, - { WINDOW_FULLSCREEN_RADIO , WINDOW_VISUALISATION } + { WINDOW_FULLSCREEN_RADIO , WINDOW_VISUALISATION }, + { WINDOW_FULLSCREEN_GAME , WINDOW_FULLSCREEN_VIDEO } }; #ifdef TARGET_WINDOWS diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp index 547507fbfb..20ac6f9bf7 100644 --- a/xbmc/windows/GUIWindowFileManager.cpp +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -617,7 +617,7 @@ void CGUIWindowFileManager::OnStart(CFileItem *pItem, const std::string &player) g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST_MUSIC); return; } - if (pItem->IsAudio() || pItem->IsVideo()) + if (pItem->IsAudio() || pItem->IsVideo() || pItem->IsGame()) { g_application.PlayFile(*pItem, player); return ; |