aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Brown <themagnificentmrb@gmail.com>2018-06-06 23:00:29 -0700
committerGitHub <noreply@github.com>2018-06-06 23:00:29 -0700
commit95da876587c466691eb9fc31b6bfb0c298a8dd8e (patch)
tree17bd98447f2fa9940fc8cfe2c0c27908732afa78
parent2ac986a3da535e10ff5bbdf8fce9130a3f23b259 (diff)
parent0f7960bf886c5edfbb8feb6b526b193a02dac013 (diff)
Merge pull request #13976 from garbear/game-api-streams
RetroPlayer: Add stream abstraction
-rw-r--r--cmake/treedata/common/games.txt1
-rw-r--r--cmake/treedata/common/retroplayer.txt2
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h6
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h382
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h96
-rw-r--r--xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h4
-rw-r--r--xbmc/cores/RetroPlayer/CMakeLists.txt4
-rw-r--r--xbmc/cores/RetroPlayer/RetroPlayer.cpp23
-rw-r--r--xbmc/cores/RetroPlayer/RetroPlayer.h6
-rw-r--r--xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp78
-rw-r--r--xbmc/cores/RetroPlayer/audio/AudioTranslator.cpp65
-rw-r--r--xbmc/cores/RetroPlayer/audio/AudioTranslator.h47
-rw-r--r--xbmc/cores/RetroPlayer/audio/CMakeLists.txt7
-rw-r--r--xbmc/cores/RetroPlayer/process/RPProcessInfo.h98
-rw-r--r--xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp48
-rw-r--r--xbmc/cores/RetroPlayer/rendering/RPRenderManager.h13
-rw-r--r--xbmc/cores/RetroPlayer/rendering/RenderVideoSettings.h3
-rw-r--r--xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp4
-rw-r--r--xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.h2
-rw-r--r--xbmc/cores/RetroPlayer/streams/CMakeLists.txt15
-rw-r--r--xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h78
-rw-r--r--xbmc/cores/RetroPlayer/streams/IStreamManager.h52
-rw-r--r--xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp82
-rw-r--r--xbmc/cores/RetroPlayer/streams/RPStreamManager.h53
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp (renamed from xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp)65
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h (renamed from xbmc/cores/RetroPlayer/RetroPlayerAudio.h)31
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp30
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h97
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp119
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h (renamed from xbmc/cores/RetroPlayer/RetroPlayerVideo.h)41
-rw-r--r--xbmc/games/addons/GameClient.cpp291
-rw-r--r--xbmc/games/addons/GameClient.h41
-rw-r--r--xbmc/games/addons/GameClientCallbacks.h31
-rw-r--r--xbmc/games/addons/GameClientSubsystem.cpp8
-rw-r--r--xbmc/games/addons/GameClientSubsystem.h3
-rw-r--r--xbmc/games/addons/GameClientTranslator.cpp95
-rw-r--r--xbmc/games/addons/GameClientTranslator.h38
-rw-r--r--xbmc/games/addons/streams/CMakeLists.txt12
-rw-r--r--xbmc/games/addons/streams/GameClientStreamAudio.cpp119
-rw-r--r--xbmc/games/addons/streams/GameClientStreamAudio.h62
-rw-r--r--xbmc/games/addons/streams/GameClientStreamVideo.cpp122
-rw-r--r--xbmc/games/addons/streams/GameClientStreamVideo.h58
-rw-r--r--xbmc/games/addons/streams/GameClientStreams.cpp119
-rw-r--r--xbmc/games/addons/streams/GameClientStreams.h66
-rw-r--r--xbmc/games/addons/streams/IGameClientStream.h85
45 files changed, 1994 insertions, 708 deletions
diff --git a/cmake/treedata/common/games.txt b/cmake/treedata/common/games.txt
index 7b63f6a538..4e7466c4a9 100644
--- a/cmake/treedata/common/games.txt
+++ b/cmake/treedata/common/games.txt
@@ -3,6 +3,7 @@ xbmc/games/addons games/addons
xbmc/games/addons/input games/addons/input
xbmc/games/addons/playback games/addons/playback
xbmc/games/addons/savestates games/addons/savestates
+xbmc/games/addons/streams games/addons/streams
xbmc/games/controllers games/controllers
xbmc/games/controllers/dialogs games/controllers/dialogs
xbmc/games/controllers/guicontrols games/controllers/guicontrols
diff --git a/cmake/treedata/common/retroplayer.txt b/cmake/treedata/common/retroplayer.txt
index 64db57fcf7..50ecebcda5 100644
--- a/cmake/treedata/common/retroplayer.txt
+++ b/cmake/treedata/common/retroplayer.txt
@@ -1,4 +1,5 @@
xbmc/cores/RetroPlayer cores/RetroPlayer
+xbmc/cores/RetroPlayer/audio cores/RetroPlayer/audio
xbmc/cores/RetroPlayer/buffers cores/RetroPlayer/buffers
xbmc/cores/RetroPlayer/buffers/video cores/RetroPlayer/buffers/video
xbmc/cores/RetroPlayer/guibridge cores/RetroPlayer/guibridge
@@ -8,3 +9,4 @@ xbmc/cores/RetroPlayer/process cores/RetroPlaye
xbmc/cores/RetroPlayer/rendering cores/RetroPlayer/rendering
xbmc/cores/RetroPlayer/rendering/VideoRenderers cores/RetroPlayer/rendering/VideoRenderers
xbmc/cores/RetroPlayer/rendering/VideoShaders cores/RetroPlayer/rendering/VideoShaders
+xbmc/cores/RetroPlayer/streams cores/RetroPlayer/streams
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
index b0639e3771..298456ad85 100644
--- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
@@ -69,13 +69,13 @@ GAME_ERROR LoadStandalone(void);
GAME_ERROR UnloadGame(void);
/*!
- * \brief Get information about the loaded game
+ * \brief Get timing information about the loaded game
*
* \param info The info structure to fill
*
* \return the error, or GAME_ERROR_NO_ERROR if info was filled
*/
-GAME_ERROR GetGameInfo(game_system_av_info* info);
+GAME_ERROR GetGameTiming(game_system_timing* timing_info);
/*!
* \brief Get region of the loaded game
@@ -311,7 +311,7 @@ void __declspec(dllexport) get_addon(void* ptr)
pClient->toAddon.LoadGameSpecial = LoadGameSpecial;
pClient->toAddon.LoadStandalone = LoadStandalone;
pClient->toAddon.UnloadGame = UnloadGame;
- pClient->toAddon.GetGameInfo = GetGameInfo;
+ pClient->toAddon.GetGameTiming = GetGameTiming;
pClient->toAddon.GetRegion = GetRegion;
pClient->toAddon.RequiresGameLoop = RequiresGameLoop;
pClient->toAddon.RunFrame = RunFrame;
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
index 758b0fae31..6c3a312a9f 100644
--- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
@@ -62,6 +62,8 @@
extern "C" {
#endif
+/// @name Add-on types
+///{
/*! Game add-on error codes */
typedef enum GAME_ERROR
{
@@ -74,50 +76,16 @@ typedef enum GAME_ERROR
GAME_ERROR_NOT_LOADED, // no game is loaded
GAME_ERROR_RESTRICTED, // game requires restricted resources
} GAME_ERROR;
+///}
-typedef enum GAME_STREAM_TYPE
-{
- GAME_STREAM_UNKNOWN,
- GAME_STREAM_AUDIO,
- GAME_STREAM_VIDEO,
-} GAME_STREAM_TYPE;
-
-typedef enum GAME_PIXEL_FORMAT
-{
- GAME_PIXEL_FORMAT_UNKNOWN,
- GAME_PIXEL_FORMAT_YUV420P,
- GAME_PIXEL_FORMAT_0RGB8888,
- GAME_PIXEL_FORMAT_RGB565,
- GAME_PIXEL_FORMAT_0RGB1555,
-} GAME_PIXEL_FORMAT;
-
-typedef enum GAME_VIDEO_CODEC
-{
- GAME_VIDEO_CODEC_UNKNOWN,
- GAME_VIDEO_CODEC_H264,
- GAME_VIDEO_CODEC_THEORA,
-} GAME_VIDEO_CODEC;
-
-typedef enum GAME_VIDEO_ROTATION // Counter-clockwise
-{
- GAME_VIDEO_ROTATION_0,
- GAME_VIDEO_ROTATION_90,
- GAME_VIDEO_ROTATION_180,
- GAME_VIDEO_ROTATION_270,
-} GAME_VIDEO_ROTATION;
-
+/// @name Audio stream
+///{
typedef enum GAME_PCM_FORMAT
{
GAME_PCM_FORMAT_UNKNOWN,
GAME_PCM_FORMAT_S16NE,
} GAME_PCM_FORMAT;
-typedef enum GAME_AUDIO_CODEC
-{
- GAME_AUDIO_CODEC_UNKNOWN,
- GAME_AUDIO_CODEC_OPUS,
-} GAME_AUDIO_CODEC;
-
typedef enum GAME_AUDIO_CHANNEL
{
GAME_CH_NULL, // Channel list terminator
@@ -143,50 +111,234 @@ typedef enum GAME_AUDIO_CHANNEL
GAME_CH_BROC,
} GAME_AUDIO_CHANNEL;
-// TODO
-typedef enum GAME_HW_FRAME_BUFFER
+typedef struct game_stream_audio_properties
+{
+ GAME_PCM_FORMAT format;
+ const GAME_AUDIO_CHANNEL* channel_map;
+} ATTRIBUTE_PACKED game_stream_audio_properties;
+
+typedef struct game_stream_audio_packet
{
- GAME_HW_FRAME_BUFFER_VALID, // Pass this to game_video_refresh if rendering to hardware
- GAME_HW_FRAME_BUFFER_DUPLICATE, // Passing NULL to game_video_refresh is still a frame dupe as normal
- GAME_HW_FRAME_BUFFER_RENDER,
-} GAME_HW_FRAME_BUFFER;
+ const uint8_t *data;
+ size_t size;
+} ATTRIBUTE_PACKED game_stream_audio_packet;
+///}
+/// @name Video stream
+///{
+typedef enum GAME_PIXEL_FORMAT
+{
+ GAME_PIXEL_FORMAT_UNKNOWN,
+ GAME_PIXEL_FORMAT_0RGB8888,
+ GAME_PIXEL_FORMAT_RGB565,
+ GAME_PIXEL_FORMAT_0RGB1555,
+} GAME_PIXEL_FORMAT;
+
+typedef enum GAME_VIDEO_ROTATION
+{
+ GAME_VIDEO_ROTATION_0,
+ GAME_VIDEO_ROTATION_90_CCW,
+ GAME_VIDEO_ROTATION_180_CCW,
+ GAME_VIDEO_ROTATION_270_CCW,
+} GAME_VIDEO_ROTATION;
+
+typedef struct game_stream_video_properties
+{
+ GAME_PIXEL_FORMAT format;
+ unsigned int nominal_width;
+ unsigned int nominal_height;
+ unsigned int max_width;
+ unsigned int max_height;
+ float aspect_ratio; // If aspect_ratio is <= 0.0, an aspect ratio of nominal_width / nominal_height is assumed
+} ATTRIBUTE_PACKED game_stream_video_properties;
+
+typedef struct game_stream_video_packet
+{
+ unsigned int width;
+ unsigned int height;
+ GAME_VIDEO_ROTATION rotation;
+ const uint8_t *data;
+ size_t size;
+} ATTRIBUTE_PACKED game_stream_video_packet;
+///}
+
+/// @name Hardware framebuffer stream
+///{
typedef enum GAME_HW_CONTEXT_TYPE
{
GAME_HW_CONTEXT_NONE,
- GAME_HW_CONTEXT_OPENGL, // OpenGL 2.x. Latest version available before 3.x+. Driver can choose to use latest compatibility context
- GAME_HW_CONTEXT_OPENGLES2, // GLES 2.0
- GAME_HW_CONTEXT_OPENGL_CORE, // Modern desktop core GL context. Use major/minor fields to set GL version
- GAME_HW_CONTEXT_OPENGLES3, // GLES 3.0
+
+ // OpenGL 2.x. Driver can choose to use latest compatibility context
+ GAME_HW_CONTEXT_OPENGL,
+
+ // OpenGL ES 2.0
+ GAME_HW_CONTEXT_OPENGLES2,
+
+ // Modern desktop core GL context. Use major/minor fields to set GL version
+ GAME_HW_CONTEXT_OPENGL_CORE,
+
+ // OpenGL ES 3.0
+ GAME_HW_CONTEXT_OPENGLES3,
+
+ // OpenGL ES 3.1+. Set major/minor fields.
+ GAME_HW_CONTEXT_OPENGLES_VERSION,
+
+ // Vulkan
+ GAME_HW_CONTEXT_VULKAN
} GAME_HW_CONTEXT_TYPE;
-typedef enum GAME_INPUT_EVENT_SOURCE
+typedef struct game_stream_hw_framebuffer_properties
{
- GAME_INPUT_EVENT_DIGITAL_BUTTON,
- GAME_INPUT_EVENT_ANALOG_BUTTON,
- GAME_INPUT_EVENT_AXIS,
- GAME_INPUT_EVENT_ANALOG_STICK,
- GAME_INPUT_EVENT_ACCELEROMETER,
- GAME_INPUT_EVENT_KEY,
- GAME_INPUT_EVENT_RELATIVE_POINTER,
- GAME_INPUT_EVENT_ABSOLUTE_POINTER,
- GAME_INPUT_EVENT_MOTOR,
-} GAME_INPUT_EVENT_SOURCE;
+ /*!
+ * The API to use.
+ */
+ GAME_HW_CONTEXT_TYPE context_type;
-typedef enum GAME_KEY_MOD
+ /*!
+ * Set if render buffers should have depth component attached.
+ *
+ * TODO: Obsolete
+ */
+ bool depth;
+
+ /*!
+ * Set if stencil buffers should be attached. If depth and stencil are true,
+ * a packed 24/8 buffer will be added. Only attaching stencil is invalid and
+ * will be ignored.
+ *
+ * TODO: Obsolete.
+ */
+ bool stencil;
+
+ /*!
+ * Use conventional bottom-left origin convention. If false, standard top-left
+ * origin semantics are used.
+ *
+ * TODO: Move to GL specific interface
+ */
+ bool bottom_left_origin;
+
+ /*!
+ * Major version number for core GL context or GLES 3.1+.
+ */
+ unsigned int version_major;
+
+ /*!
+ * Minor version number for core GL context or GLES 3.1+.
+ */
+ unsigned int version_minor;
+
+ /*!
+ * If this is true, the frontend will go very far to avoid resetting context
+ * in scenarios like toggling fullscreen, etc.
+ *
+ * TODO: Obsolete? Maybe frontend should just always assume this...
+ *
+ * The reset callback might still be called in extreme situations such as if
+ * the context is lost beyond recovery.
+ *
+ * For optimal stability, set this to false, and allow context to be reset at
+ * any time.
+ */
+ bool cache_context;
+
+ /*!
+ * Creates a debug context.
+ */
+ bool debug_context;
+} ATTRIBUTE_PACKED game_stream_hw_framebuffer_properties;
+
+typedef struct game_stream_hw_framebuffer_buffer
{
- GAME_KEY_MOD_NONE = 0x0000,
+ uintptr_t framebuffer;
+} ATTRIBUTE_PACKED game_stream_hw_framebuffer_buffer;
- GAME_KEY_MOD_SHIFT = 0x0001,
- GAME_KEY_MOD_CTRL = 0x0002,
- GAME_KEY_MOD_ALT = 0x0004,
- GAME_KEY_MOD_META = 0x0008,
- GAME_KEY_MOD_SUPER = 0x0010,
+typedef struct game_stream_hw_framebuffer_packet
+{
+ uintptr_t framebuffer;
+} ATTRIBUTE_PACKED game_stream_hw_framebuffer_packet;
- GAME_KEY_MOD_NUMLOCK = 0x0100,
- GAME_KEY_MOD_CAPSLOCK = 0x0200,
- GAME_KEY_MOD_SCROLLOCK = 0x0400,
-} GAME_KEY_MOD;
+typedef void (*game_proc_address_t)(void);
+///}
+
+/// @name Software framebuffer stream
+///{
+typedef game_stream_video_properties game_stream_sw_framebuffer_properties;
+
+typedef struct game_stream_sw_framebuffer_buffer
+{
+ GAME_PIXEL_FORMAT format;
+ uint8_t *data;
+ size_t size;
+} ATTRIBUTE_PACKED game_stream_sw_framebuffer_buffer;
+
+typedef game_stream_video_packet game_stream_sw_framebuffer_packet;
+///}
+
+/// @name Stream types
+///{
+typedef enum GAME_STREAM_TYPE
+{
+ GAME_STREAM_UNKNOWN,
+ GAME_STREAM_AUDIO,
+ GAME_STREAM_VIDEO,
+ GAME_STREAM_HW_FRAMEBUFFER,
+ GAME_STREAM_SW_FRAMEBUFFER,
+} GAME_STREAM_TYPE;
+
+/*!
+ * \brief Immutable stream metadata
+ *
+ * This metadata is provided when the stream is opened. If any stream
+ * properties change, a new stream must be opened.
+ */
+typedef struct game_stream_properties
+{
+ GAME_STREAM_TYPE type;
+ union
+ {
+ game_stream_audio_properties audio;
+ game_stream_video_properties video;
+ game_stream_hw_framebuffer_properties hw_framebuffer;
+ game_stream_sw_framebuffer_properties sw_framebuffer;
+ };
+} ATTRIBUTE_PACKED game_stream_properties;
+
+/*!
+ * \brief Stream buffers for hardware rendering and zero-copy support
+ */
+typedef struct game_stream_buffer
+{
+ GAME_STREAM_TYPE type;
+ union
+ {
+ game_stream_hw_framebuffer_buffer hw_framebuffer;
+ game_stream_sw_framebuffer_buffer sw_framebuffer;
+ };
+} ATTRIBUTE_PACKED game_stream_buffer;
+
+/*!
+ * \brief Stream packet and ephemeral metadata
+ *
+ * This packet contains stream data and accompanying metadata. The metadata
+ * is ephemeral, meaning it only applies to the current packet and can change
+ * from packet to packet in the same stream.
+ */
+typedef struct game_stream_packet
+{
+ GAME_STREAM_TYPE type;
+ union
+ {
+ game_stream_audio_packet audio;
+ game_stream_video_packet video;
+ game_stream_hw_framebuffer_packet hw_framebuffer;
+ game_stream_sw_framebuffer_packet sw_framebuffer;
+ };
+} ATTRIBUTE_PACKED game_stream_packet;
+///}
+
+/// @name Game types
+///{
/*! Returned from game_get_region() */
typedef enum GAME_REGION
@@ -262,14 +414,38 @@ typedef enum GAME_SIMD
GAME_SIMD_AVX2 = (1 << 12),
GAME_SIMD_VFPU = (1 << 13),
} GAME_SIMD;
+///}
-typedef enum GAME_ROTATION
+/// @name Input types
+///{
+
+typedef enum GAME_INPUT_EVENT_SOURCE
{
- GAME_ROTATION_0_CW,
- GAME_ROTATION_90_CW,
- GAME_ROTATION_180_CW,
- GAME_ROTATION_270_CW,
-} GAME_ROTATION;
+ GAME_INPUT_EVENT_DIGITAL_BUTTON,
+ GAME_INPUT_EVENT_ANALOG_BUTTON,
+ GAME_INPUT_EVENT_AXIS,
+ GAME_INPUT_EVENT_ANALOG_STICK,
+ GAME_INPUT_EVENT_ACCELEROMETER,
+ GAME_INPUT_EVENT_KEY,
+ GAME_INPUT_EVENT_RELATIVE_POINTER,
+ GAME_INPUT_EVENT_ABSOLUTE_POINTER,
+ GAME_INPUT_EVENT_MOTOR,
+} GAME_INPUT_EVENT_SOURCE;
+
+typedef enum GAME_KEY_MOD
+{
+ GAME_KEY_MOD_NONE = 0x0000,
+
+ GAME_KEY_MOD_SHIFT = 0x0001,
+ GAME_KEY_MOD_CTRL = 0x0002,
+ GAME_KEY_MOD_ALT = 0x0004,
+ GAME_KEY_MOD_META = 0x0008,
+ GAME_KEY_MOD_SUPER = 0x0010,
+
+ GAME_KEY_MOD_NUMLOCK = 0x0100,
+ GAME_KEY_MOD_CAPSLOCK = 0x0200,
+ GAME_KEY_MOD_SCROLLOCK = 0x0400,
+} GAME_KEY_MOD;
/*!
* \brief Type of port on the virtual game console
@@ -418,46 +594,16 @@ typedef struct game_input_event
struct game_motor_event motor;
};
} ATTRIBUTE_PACKED game_input_event;
+///}
-struct game_geometry
-{
- unsigned base_width; // Nominal video width of game
- unsigned base_height; // Nominal video height of game
- unsigned max_width; // Maximum possible width of game
- unsigned max_height; // Maximum possible height of game
- float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0,
- // an aspect ratio of base_width / base_height is assumed.
- // A frontend could override this setting if desired.
-};
-
+/// @name Environment types
+///{
struct game_system_timing
{
double fps; // FPS of video content.
double sample_rate; // Sampling rate of audio.
};
-
-struct game_system_av_info
-{
- struct game_geometry geometry;
- struct game_system_timing timing;
-};
-
-typedef void (*game_proc_address_t)(void);
-
-struct game_hw_info
-{
- GAME_HW_CONTEXT_TYPE context_type; // Which API to use. Set by game client
- bool depth; // Set if render buffers should have depth component attached
- bool stencil; // Set if stencil buffers should be attached
- // If depth and stencil are true, a packed 24/8 buffer will be added. Only attaching stencil is invalid and will be ignored
- bool bottom_left_origin; // Use conventional bottom-left origin convention. Is false, standard top-left origin semantics are used
- unsigned version_major; // Major version number for core GL context
- unsigned version_minor; // Minor version number for core GL context
- bool cache_context; // If this is true, the frontend will go very far to avoid resetting context in scenarios like toggling fullscreen, etc.
- // The reset callback might still be called in extreme situations such as if the context is lost beyond recovery
- // For optimal stability, set this to false, and allow context to be reset at any time
- bool debug_context; // Creates a debug context
-};
+///}
/*! Properties passed to the ADDON_Create() method of a game client */
typedef struct AddonProps_Game
@@ -512,7 +658,7 @@ typedef struct AddonProps_Game
} AddonProps_Game;
typedef AddonProps_Game game_client_properties;
-
+
/*! Structure to transfer the methods from kodi_game_dll.h to Kodi */
typedef struct AddonToKodiFuncTable_Game
@@ -520,16 +666,12 @@ typedef struct AddonToKodiFuncTable_Game
KODI_HANDLE kodiInstance;
void (*CloseGame)(void* kodiInstance);
- int (*OpenPixelStream)(void* kodiInstance, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
- int (*OpenVideoStream)(void* kodiInstance, GAME_VIDEO_CODEC codec);
- int (*OpenPCMStream)(void* kodiInstance, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map);
- int(*OpenAudioStream)(void* kodiInstance, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map);
- void (*AddStreamData)(void* kodiInstance, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
- void (*CloseStream)(void* kodiInstance, GAME_STREAM_TYPE stream);
- void (*EnableHardwareRendering)(void* kodiInstance, const game_hw_info* hw_info);
- uintptr_t (*HwGetCurrentFramebuffer)(void* kodiInstance);
+ void* (*OpenStream)(void*, const game_stream_properties*);
+ bool (*GetStreamBuffer)(void*, void*, unsigned int, unsigned int, game_stream_buffer*);
+ void (*AddStreamData)(void*, void*, const game_stream_packet*);
+ void (*ReleaseStreamBuffer)(void*, void*, game_stream_buffer*);
+ void (*CloseStream)(void*, void*);
game_proc_address_t (*HwGetProcAddress)(void* kodiInstance, const char* symbol);
- void (*RenderFrame)(void* kodiInstance);
bool (*InputEvent)(void* kodiInstance, const game_input_event* event);
} AddonToKodiFuncTable_Game;
@@ -540,7 +682,7 @@ typedef struct KodiToAddonFuncTable_Game
GAME_ERROR (__cdecl* LoadGameSpecial)(SPECIAL_GAME_TYPE, const char**, size_t);
GAME_ERROR (__cdecl* LoadStandalone)(void);
GAME_ERROR (__cdecl* UnloadGame)(void);
- GAME_ERROR (__cdecl* GetGameInfo)(game_system_av_info*);
+ GAME_ERROR (__cdecl* GetGameTiming)(game_system_timing*);
GAME_REGION (__cdecl* GetRegion)(void);
bool (__cdecl* RequiresGameLoop)(void);
GAME_ERROR (__cdecl* RunFrame)(void);
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
index 2774afd567..b6224f39a3 100644
--- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
@@ -65,72 +65,58 @@ public:
*/
void CloseGame(void)
{
- return m_callbacks->toKodi.CloseGame(m_callbacks->toKodi.kodiInstance);
+ m_callbacks->toKodi.CloseGame(m_callbacks->toKodi.kodiInstance);
}
/*!
- * \brief Create a video stream for pixel data
+ * \brief Create a stream for gameplay data
*
- * \param format The type of pixel data accepted by this stream
- * \param width The frame width
- * \param height The frame height
- * \param rotation The rotation (counter-clockwise) of the video frames
+ * \param properties The stream properties
*
- * \return 0 on success or -1 if a video stream is already created
+ * \return A stream handle, or NULL on failure
*/
- bool OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+ void* OpenStream(const game_stream_properties &properties)
{
- return m_callbacks->toKodi.OpenPixelStream(m_callbacks->toKodi.kodiInstance, format, width, height, rotation) == 0;
+ return m_callbacks->toKodi.OpenStream(m_callbacks->toKodi.kodiInstance, &properties);
}
/*!
- * \brief Create a video stream for encoded video data
+ * \brief Get a buffer for zero-copy stream data
*
- * \param codec The video format accepted by this stream
+ * \param stream The stream handle
+ * \param width The framebuffer width, or 0 for no width specified
+ * \param height The framebuffer height, or 0 for no height specified
+ * \param[out] buffer The buffer, or unmodified if false is returned
*
- * \return 0 on success or -1 if a video stream is already created
+ * If this returns true, buffer must be freed using ReleaseStreamBuffer().
+ *
+ * \return True if buffer was set, false otherwise
*/
- bool OpenVideoStream(GAME_VIDEO_CODEC codec)
+ bool GetStreamBuffer(void *stream, unsigned int width, unsigned int height, game_stream_buffer &buffer)
{
- return m_callbacks->toKodi.OpenVideoStream(m_callbacks->toKodi.kodiInstance, codec) == 0;
+ return m_callbacks->toKodi.GetStreamBuffer(m_callbacks->toKodi.kodiInstance, stream, width, height, &buffer);
}
/*!
- * \brief Create an audio stream for PCM audio data
- *
- * \param format The type of audio data accepted by this stream
- * \param channel_map The channel layout terminated by GAME_CH_NULL
+ * \brief Add a data packet to a stream
*
- * \return 0 on success or -1 if an audio stream is already created
+ * \param stream The target stream
+ * \param packet The data packet
*/
- bool OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map)
- {
- return m_callbacks->toKodi.OpenPCMStream(m_callbacks->toKodi.kodiInstance, format, channel_map) == 0;
- }
-
- /*!
- * \brief Create an audio stream for encoded audio data
- *
- * \param codec The audio format accepted by this stream
- * \param channel_map The channel layout terminated by GAME_CH_NULL
- *
- * \return 0 on success or -1 if an audio stream is already created
- */
- bool OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map)
+ void AddStreamData(void *stream, const game_stream_packet &packet)
{
- return m_callbacks->toKodi.OpenAudioStream(m_callbacks->toKodi.kodiInstance, codec, channel_map) == 0;
+ m_callbacks->toKodi.AddStreamData(m_callbacks->toKodi.kodiInstance, stream, &packet);
}
/*!
- * \brief Add a data packet to an audio or video stream
+ * \brief Free an allocated buffer
*
- * \param stream The target stream
- * \param data The data packet
- * \param size The size of the data
+ * \param stream The stream handle
+ * \param buffer The buffer returned from GetStreamBuffer()
*/
- void AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+ void ReleaseStreamBuffer(void *stream, game_stream_buffer &buffer)
{
- m_callbacks->toKodi.AddStreamData(m_callbacks->toKodi.kodiInstance, stream, data, size);
+ m_callbacks->toKodi.ReleaseStreamBuffer(m_callbacks->toKodi.kodiInstance, stream, &buffer);
}
/*!
@@ -138,7 +124,7 @@ public:
*
* \param stream The stream to close
*/
- void CloseStream(GAME_STREAM_TYPE stream)
+ void CloseStream(void *stream)
{
m_callbacks->toKodi.CloseStream(m_callbacks->toKodi.kodiInstance, stream);
}
@@ -146,26 +132,6 @@ public:
// -- Hardware rendering callbacks -------------------------------------------
/*!
- * \brief Enable hardware rendering
- *
- * \param hw_info A struct of properties for the hardware rendering system
- */
- void EnableHardwareRendering(const struct game_hw_info* hw_info)
- {
- return m_callbacks->toKodi.EnableHardwareRendering(m_callbacks->toKodi.kodiInstance, hw_info);
- }
-
- /*!
- * \brief Get the framebuffer for rendering
- *
- * \return The framebuffer
- */
- uintptr_t HwGetCurrentFramebuffer(void)
- {
- return m_callbacks->toKodi.HwGetCurrentFramebuffer(m_callbacks->toKodi.kodiInstance);
- }
-
- /*!
* \brief Get a symbol from the hardware context
*
* \param symbol The symbol's name
@@ -177,14 +143,6 @@ public:
return m_callbacks->toKodi.HwGetProcAddress(m_callbacks->toKodi.kodiInstance, sym);
}
- /*!
- * \brief Called when a frame is being rendered
- */
- void RenderFrame()
- {
- return m_callbacks->toKodi.RenderFrame(m_callbacks->toKodi.kodiInstance);
- }
-
// --- Input callbacks -------------------------------------------------------
/*!
diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
index 3c42dbaf0a..042b1932af 100644
--- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
+++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
@@ -86,8 +86,8 @@
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_XML_ID "kodi.binary.instance.audioencoder"
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_DEPENDS "addon-instance/AudioEncoder.h"
-#define ADDON_INSTANCE_VERSION_GAME "1.0.36"
-#define ADDON_INSTANCE_VERSION_GAME_MIN "1.0.36"
+#define ADDON_INSTANCE_VERSION_GAME "1.0.38"
+#define ADDON_INSTANCE_VERSION_GAME_MIN "1.0.38"
#define ADDON_INSTANCE_VERSION_GAME_XML_ID "kodi.binary.instance.game"
#define ADDON_INSTANCE_VERSION_GAME_DEPENDS "kodi_game_dll.h" \
"kodi_game_types.h" \
diff --git a/xbmc/cores/RetroPlayer/CMakeLists.txt b/xbmc/cores/RetroPlayer/CMakeLists.txt
index 96feb17dab..3e78c544dc 100644
--- a/xbmc/cores/RetroPlayer/CMakeLists.txt
+++ b/xbmc/cores/RetroPlayer/CMakeLists.txt
@@ -1,18 +1,14 @@
set(SOURCES RetroPlayer.cpp
- RetroPlayerAudio.cpp
RetroPlayerAutoSave.cpp
RetroPlayerInput.cpp
RetroPlayerUtils.cpp
- RetroPlayerVideo.cpp
)
set(HEADERS RetroPlayer.h
- RetroPlayerAudio.h
RetroPlayerAutoSave.h
RetroPlayerInput.h
RetroPlayerTypes.h
RetroPlayerUtils.h
- RetroPlayerVideo.h
)
core_add_library(retroplayer)
diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.cpp b/xbmc/cores/RetroPlayer/RetroPlayer.cpp
index 31dc97fafa..ad26263636 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayer.cpp
+++ b/xbmc/cores/RetroPlayer/RetroPlayer.cpp
@@ -19,15 +19,14 @@
*/
#include "RetroPlayer.h"
-#include "RetroPlayerAudio.h"
#include "RetroPlayerAutoSave.h"
#include "RetroPlayerInput.h"
-#include "RetroPlayerVideo.h"
#include "addons/AddonManager.h"
#include "cores/DataCacheCore.h"
#include "cores/RetroPlayer/guibridge/GUIGameRenderManager.h"
#include "cores/RetroPlayer/process/RPProcessInfo.h"
#include "cores/RetroPlayer/rendering/RPRenderManager.h"
+#include "cores/RetroPlayer/streams/RPStreamManager.h"
#include "dialogs/GUIDialogYesNo.h"
#include "filesystem/File.h"
#include "games/addons/input/GameClientInput.h"
@@ -125,20 +124,20 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
m_gameClient = std::static_pointer_cast<CGameClient>(addon);
if (m_gameClient->Initialize())
{
- m_audio.reset(new CRetroPlayerAudio(*m_processInfo));
- m_video.reset(new CRetroPlayerVideo(*m_renderManager, *m_processInfo));
+ m_streamManager.reset(new CRPStreamManager(*m_renderManager, *m_processInfo));
+
m_input.reset(new CRetroPlayerInput(CServiceBroker::GetPeripherals()));
if (!bStandalone)
{
std::string redactedPath = CURL::GetRedacted(fileCopy.GetPath());
CLog::Log(LOGINFO, "RetroPlayer[PLAYER]: Opening: %s", redactedPath.c_str());
- bSuccess = m_gameClient->OpenFile(fileCopy, m_audio.get(), m_video.get(), m_input.get());
+ bSuccess = m_gameClient->OpenFile(fileCopy, *m_streamManager, m_input.get());
}
else
{
CLog::Log(LOGINFO, "RetroPlayer[PLAYER]: Opening standalone");
- bSuccess = m_gameClient->OpenStandalone(m_audio.get(), m_video.get(), m_input.get());
+ bSuccess = m_gameClient->OpenStandalone(*m_streamManager, m_input.get());
}
if (bSuccess)
@@ -196,8 +195,7 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
{
m_gameClient.reset();
m_input.reset();
- m_audio.reset();
- m_video.reset();
+ m_streamManager.reset();
}
return bSuccess;
@@ -227,8 +225,7 @@ bool CRetroPlayer::CloseFile(bool reopen /* = false */)
}
m_input.reset();
- m_audio.reset();
- m_video.reset();
+ m_streamManager.reset();
m_renderManager.reset();
m_processInfo.reset();
@@ -336,8 +333,8 @@ float CRetroPlayer::GetCachePercentage()
void CRetroPlayer::SetMute(bool bOnOff)
{
- if (m_audio)
- m_audio->Enable(!bOnOff);
+ if (m_streamManager)
+ m_streamManager->EnableAudio(!bOnOff);
}
void CRetroPlayer::SeekTime(int64_t iTime /* = 0 */)
@@ -552,7 +549,7 @@ void CRetroPlayer::SetSpeedInternal(double speed)
void CRetroPlayer::OnSpeedChange(double newSpeed)
{
- m_audio->Enable(newSpeed == 1.0);
+ m_streamManager->EnableAudio(newSpeed == 1.0);
m_input->SetSpeed(newSpeed);
m_renderManager->SetSpeed(newSpeed);
m_processInfo->SetSpeed(static_cast<float>(newSpeed));
diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.h b/xbmc/cores/RetroPlayer/RetroPlayer.h
index 73d3352bff..1f3c6c1389 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayer.h
+++ b/xbmc/cores/RetroPlayer/RetroPlayer.h
@@ -35,12 +35,11 @@ namespace GAME
namespace RETRO
{
- class CRetroPlayerAudio;
class CRetroPlayerAutoSave;
class CRetroPlayerInput;
- class CRetroPlayerVideo;
class CRPProcessInfo;
class CRPRenderManager;
+ class CRPStreamManager;
class CRetroPlayer : public IPlayer, public IRenderLoop
{
@@ -169,8 +168,7 @@ namespace RETRO
double m_priorSpeed = 0.0f; // Speed of gameplay before entering OSD
std::unique_ptr<CRPProcessInfo> m_processInfo;
std::unique_ptr<CRPRenderManager> m_renderManager;
- std::unique_ptr<CRetroPlayerAudio> m_audio;
- std::unique_ptr<CRetroPlayerVideo> m_video;
+ std::unique_ptr<CRPStreamManager> m_streamManager;
std::unique_ptr<CRetroPlayerInput> m_input;
std::unique_ptr<CRetroPlayerAutoSave> m_autoSave;
GAME::GameClientPtr m_gameClient;
diff --git a/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp b/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp
deleted file mode 100644
index 34a13e50fa..0000000000
--- a/xbmc/cores/RetroPlayer/RetroPlayerVideo.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2012-2017 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 "cores/RetroPlayer/process/RPProcessInfo.h"
-#include "cores/RetroPlayer/rendering/RenderTranslator.h"
-#include "cores/RetroPlayer/rendering/RPRenderManager.h"
-#include "utils/log.h"
-
-using namespace KODI;
-using namespace RETRO;
-
-CRetroPlayerVideo::CRetroPlayerVideo(CRPRenderManager& renderManager, CRPProcessInfo& processInfo) :
- m_renderManager(renderManager),
- m_processInfo(processInfo)
-{
- CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Initializing video");
-
- m_renderManager.Initialize();
-}
-
-CRetroPlayerVideo::~CRetroPlayerVideo()
-{
- CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Deinitializing video");
-
- CloseStream();
- m_renderManager.Deinitialize();
-}
-
-bool CRetroPlayerVideo::OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, unsigned int orientationDeg)
-{
- CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Creating video stream - format %s, %ux%u, %u deg",
- CRenderTranslator::TranslatePixelFormat(pixfmt),
- width,
- height,
- orientationDeg);
-
- m_processInfo.SetVideoPixelFormat(pixfmt);
- m_processInfo.SetVideoDimensions(width, height);
-
- return m_renderManager.Configure(pixfmt, width, height, orientationDeg);
-}
-
-bool CRetroPlayerVideo::OpenEncodedStream(AVCodecID codec)
-{
- CLog::Log(LOGERROR, "RetroPlayer[VIDEO]: Encoded video stream not supported");
-
- return false; //! @todo
-}
-
-void CRetroPlayerVideo::AddData(const uint8_t* data, size_t size)
-{
- m_renderManager.AddFrame(data, size);
-}
-
-void CRetroPlayerVideo::CloseStream()
-{
- CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Closing video stream");
-
- m_renderManager.Flush();
-}
diff --git a/xbmc/cores/RetroPlayer/audio/AudioTranslator.cpp b/xbmc/cores/RetroPlayer/audio/AudioTranslator.cpp
new file mode 100644
index 0000000000..e5fcf6207d
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/audio/AudioTranslator.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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 "AudioTranslator.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+AEDataFormat CAudioTranslator::TranslatePCMFormat(PCMFormat format)
+{
+ switch (format)
+ {
+ case PCMFormat::FMT_S16NE: return AE_FMT_S16NE;
+ default:
+ break;
+ }
+ return AE_FMT_INVALID;
+}
+
+AEChannel CAudioTranslator::TranslateAudioChannel(AudioChannel channel)
+{
+ switch (channel)
+ {
+ case AudioChannel::CH_FL: return AE_CH_FL;
+ case AudioChannel::CH_FR: return AE_CH_FR;
+ case AudioChannel::CH_FC: return AE_CH_FC;
+ case AudioChannel::CH_LFE: return AE_CH_LFE;
+ case AudioChannel::CH_BL: return AE_CH_BL;
+ case AudioChannel::CH_BR: return AE_CH_BR;
+ case AudioChannel::CH_FLOC: return AE_CH_FLOC;
+ case AudioChannel::CH_FROC: return AE_CH_FROC;
+ case AudioChannel::CH_BC: return AE_CH_BC;
+ case AudioChannel::CH_SL: return AE_CH_SL;
+ case AudioChannel::CH_SR: return AE_CH_SR;
+ case AudioChannel::CH_TFL: return AE_CH_TFL;
+ case AudioChannel::CH_TFR: return AE_CH_TFR;
+ case AudioChannel::CH_TFC: return AE_CH_TFC;
+ case AudioChannel::CH_TC: return AE_CH_TC;
+ case AudioChannel::CH_TBL: return AE_CH_TBL;
+ case AudioChannel::CH_TBR: return AE_CH_TBR;
+ case AudioChannel::CH_TBC: return AE_CH_TBC;
+ case AudioChannel::CH_BLOC: return AE_CH_BLOC;
+ case AudioChannel::CH_BROC: return AE_CH_BROC;
+ default:
+ break;
+ }
+ return AE_CH_NULL;
+}
diff --git a/xbmc/cores/RetroPlayer/audio/AudioTranslator.h b/xbmc/cores/RetroPlayer/audio/AudioTranslator.h
new file mode 100644
index 0000000000..86f5f1e212
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/audio/AudioTranslator.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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/RetroPlayer/streams/RetroPlayerStreamTypes.h"
+#include "cores/AudioEngine/Utils/AEChannelData.h"
+
+namespace KODI
+{
+namespace RETRO
+{
+ class CAudioTranslator
+ {
+ public:
+ /*!
+ * \brief Translate audio PCM format (Game API to AudioEngine).
+ * \param format The audio PCM format to translate.
+ * \return Translated audio PCM format.
+ */
+ static AEDataFormat TranslatePCMFormat(PCMFormat format);
+
+ /*!
+ * \brief Translate audio channels (Game API to AudioEngine).
+ * \param format The audio channels to translate.
+ * \return Translated audio channels.
+ */
+ static AEChannel TranslateAudioChannel(AudioChannel channel);
+ };
+}
+}
diff --git a/xbmc/cores/RetroPlayer/audio/CMakeLists.txt b/xbmc/cores/RetroPlayer/audio/CMakeLists.txt
new file mode 100644
index 0000000000..1cbb113c42
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/audio/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES AudioTranslator.cpp
+)
+
+set(HEADERS AudioTranslator.h
+)
+
+core_add_library(rp_audio)
diff --git a/xbmc/cores/RetroPlayer/process/RPProcessInfo.h b/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
index 89ebdffa4a..d0df1002a0 100644
--- a/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
+++ b/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
@@ -42,18 +42,46 @@ namespace RETRO
class CRPProcessInfo;
class IRenderBufferPool;
+ /*!
+ * \brief Process info factory
+ */
using CreateRPProcessControl = CRPProcessInfo* (*)();
+ /*!
+ * \brief Rendering factory
+ */
class IRendererFactory
{
public:
virtual ~IRendererFactory() = default;
+ /*!
+ * \brief Get a description name of the rendering system
+ */
virtual std::string RenderSystemName() const = 0;
+
+ /*!
+ * \brief Create a renderer
+ *
+ * \param settings The renderer's initial settings
+ * \param context The rendering context
+ * \param bufferPool The buffer pool to which buffers are returned
+ */
virtual CRPBaseRenderer *CreateRenderer(const CRenderSettings &settings, CRenderContext &context, std::shared_ptr<IRenderBufferPool> bufferPool) = 0;
+
+ /*!
+ * \brief Create buffer pools to manager buffers
+ *
+ * \param context The rendering context shared with the buffer pools
+ *
+ * \return The buffer pools supported by the rendering system
+ */
virtual RenderBufferPoolVector CreateBufferPools(CRenderContext &context) = 0;
};
+ /*!
+ * \brief Player process info
+ */
class CRPProcessInfo
{
public:
@@ -63,37 +91,97 @@ namespace RETRO
virtual ~CRPProcessInfo();
+ /*!
+ * \brief Get the descriptive name of the platform
+ *
+ * \return The name of the platform as set by windowing
+ */
const std::string &GetPlatformName() const { return m_platformName; }
+
+ /*!
+ * \brief Get the descriptive name of the rendering system
+ *
+ * \param renderBufferPool A pool belonging to the rendering system
+ *
+ * \return The name of the rendering system as set by windowing
+ */
std::string GetRenderSystemName(IRenderBufferPool *renderBufferPool) const;
+ /*!
+ * \brief Create a renderer
+ *
+ * \param renderBufferPool The buffer pool used to return render buffers
+ * \param renderSettings The settings for this renderer
+ *
+ * \return The renderer, or nullptr on failure
+ */
CRPBaseRenderer *CreateRenderer(IRenderBufferPool *renderBufferPool, const CRenderSettings &renderSettings);
+ /*!
+ * \brief Set data cache
+ */
void SetDataCache(CDataCacheCore *cache);
+
+ /*!
+ * \brief Reset data cache info
+ */
void ResetInfo();
- // rendering info
+ /// @name Rendering functions
+ ///{
+
+ /*!
+ * \brief Get the context shared by the rendering system
+ */
CRenderContext &GetRenderContext() { return *m_renderContext; }
+
+ /*!
+ * \brief Get the buffer manager that owns the buffer pools
+ */
CRenderBufferManager &GetBufferManager() { return *m_renderBufferManager; }
+
+ /*!
+ * \brief Check if a buffer pool supports the given scaling method
+ */
bool HasScalingMethod(ESCALINGMETHOD scalingMethod) const;
+
+ /*!
+ * \brief Get the default scaling method for this rendering system
+ */
ESCALINGMETHOD GetDefaultScalingMethod() const { return m_defaultScalingMethod; }
+ ///}
- // player video
+ /// @name Player video info
+ ///{
void SetVideoPixelFormat(AVPixelFormat pixFormat);
void SetVideoDimensions(int width, int height);
void SetVideoFps(float fps);
+ ///}
- // player audio info
+ /// @name Player audio info
+ ///{
void SetAudioChannels(const std::string &channels);
void SetAudioSampleRate(int sampleRate);
void SetAudioBitsPerSample(int bitsPerSample);
+ ///}
- // player states
+ /// @name Player states
+ ///{
void SetSpeed(float speed);
void SetPlayTimes(time_t start, int64_t current, int64_t min, int64_t max);
+ ///}
protected:
+ /*!
+ * \brief Constructor
+ *
+ * \param platformName A descriptive name of the platform
+ */
CRPProcessInfo(std::string platformName);
+ /*!
+ * \brief Get all scaling methods available to the rendering system
+ */
static std::vector<ESCALINGMETHOD> GetScalingMethods();
// Static factories
@@ -104,7 +192,7 @@ namespace RETRO
// Construction parameters
const std::string m_platformName;
- // Process info parameters
+ // Info parameters
CDataCacheCore *m_dataCache = nullptr;
// Rendering parameters
diff --git a/xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp b/xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp
index 163dece617..88c6188d47 100644
--- a/xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp
+++ b/xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp
@@ -80,18 +80,20 @@ void CRPRenderManager::Deinitialize()
m_state = RENDER_STATE::UNCONFIGURED;
}
-bool CRPRenderManager::Configure(AVPixelFormat format, unsigned int width, unsigned int height, unsigned int orientation)
+bool CRPRenderManager::Configure(AVPixelFormat format, unsigned int nominalWidth, unsigned int nominalHeight, unsigned int maxWidth, unsigned int maxHeight)
{
- m_format = format;
- m_width = width;
- m_height = height;
- m_orientation = orientation;
-
- CLog::Log(LOGINFO, "RetroPlayer[RENDER]: Configuring format %s, %ux%u, %u deg",
+ CLog::Log(LOGINFO, "RetroPlayer[RENDER]: Configuring format %s, nominal %ux%u, max %ux%u",
CRenderTranslator::TranslatePixelFormat(format),
- width,
- height,
- orientation);
+ nominalWidth,
+ nominalHeight,
+ maxWidth,
+ maxHeight);
+
+ m_format = format;
+ m_maxWidth = maxWidth;
+ m_maxHeight = maxHeight;
+ m_width = nominalWidth; //! @todo Allow dimension changes
+ m_height = nominalHeight; //! @todo Allow dimension changes
CSingleLock lock(m_stateMutex);
@@ -100,10 +102,14 @@ bool CRPRenderManager::Configure(AVPixelFormat format, unsigned int width, unsig
return true;
}
-void CRPRenderManager::AddFrame(const uint8_t* data, size_t size)
+void CRPRenderManager::AddFrame(const uint8_t* data, size_t size, unsigned int width, unsigned int height, unsigned int orientationDegCCW)
{
// Validate parameters
- if (data == nullptr || size == 0)
+ if (data == nullptr || size == 0 || width == 0 || height == 0)
+ return;
+
+ //! @todo Allow dimension changes
+ if (width != m_width || height != m_height)
return;
// Copy frame to buffers with visible renderers
@@ -116,7 +122,7 @@ void CRPRenderManager::AddFrame(const uint8_t* data, size_t size)
IRenderBuffer *renderBuffer = bufferPool->GetBuffer(size);
if (renderBuffer != nullptr)
{
- CopyFrame(renderBuffer, data, size, m_format);
+ CopyFrame(renderBuffer, m_format, data, size, width, height);
renderBuffers.emplace_back(renderBuffer);
}
}
@@ -373,7 +379,7 @@ std::shared_ptr<CRPBaseRenderer> CRPRenderManager::GetRenderer(IRenderBufferPool
m_processInfo.GetRenderSystemName(bufferPool).c_str());
renderer.reset(m_processInfo.CreateRenderer(bufferPool, renderSettings));
- if (renderer && renderer->Configure(m_format, m_width, m_height, m_orientation))
+ if (renderer && renderer->Configure(m_format, m_width, m_height))
{
// Ensure we have a render buffer for this renderer
CreateRenderBuffer(renderer->GetBufferPool());
@@ -442,7 +448,7 @@ void CRPRenderManager::CreateRenderBuffer(IRenderBufferPool *bufferPool)
{
{
CSingleExit exit(m_bufferMutex);
- CopyFrame(renderBuffer, cachedFrame.data(), cachedFrame.size(), m_format);
+ CopyFrame(renderBuffer, m_format, cachedFrame.data(), cachedFrame.size(), m_width, m_height);
}
m_renderBuffers.emplace_back(renderBuffer);
}
@@ -474,14 +480,14 @@ void CRPRenderManager::UpdateResolution()
*/
}
-void CRPRenderManager::CopyFrame(IRenderBuffer *renderBuffer, const uint8_t *data, size_t size, AVPixelFormat format)
+void CRPRenderManager::CopyFrame(IRenderBuffer *renderBuffer, AVPixelFormat format, const uint8_t *data, size_t size, unsigned int width, unsigned int height)
{
const uint8_t *source = data;
uint8_t *target = renderBuffer->GetMemory();
if (target != nullptr)
{
- const unsigned int sourceStride = static_cast<unsigned int>(size / m_height);
+ const unsigned int sourceStride = static_cast<unsigned int>(size / height);
const unsigned int targetStride = static_cast<unsigned int>(renderBuffer->GetFrameSize() / renderBuffer->GetHeight());
if (m_format == renderBuffer->GetFormat())
@@ -490,10 +496,10 @@ void CRPRenderManager::CopyFrame(IRenderBuffer *renderBuffer, const uint8_t *dat
std::memcpy(target, source, size);
else
{
- const unsigned int widthBytes = CRenderTranslator::TranslateWidthToBytes(m_width, m_format);
+ const unsigned int widthBytes = CRenderTranslator::TranslateWidthToBytes(width, m_format);
if (widthBytes > 0)
{
- for (unsigned int i = 0; i < m_height; i++)
+ for (unsigned int i = 0; i < height; i++)
std::memcpy(target + targetStride * i, source + sourceStride * i, widthBytes);
}
}
@@ -502,7 +508,7 @@ void CRPRenderManager::CopyFrame(IRenderBuffer *renderBuffer, const uint8_t *dat
{
SwsContext *&scalerContext = m_scalers[renderBuffer->GetFormat()];
scalerContext = sws_getCachedContext(scalerContext,
- m_width, m_height, format,
+ width, height, format,
renderBuffer->GetWidth(), renderBuffer->GetHeight(), renderBuffer->GetFormat(),
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
@@ -513,7 +519,7 @@ void CRPRenderManager::CopyFrame(IRenderBuffer *renderBuffer, const uint8_t *dat
uint8_t *dst[] = { target, nullptr, nullptr, nullptr };
int dstStride[] = { static_cast<int>(targetStride), 0, 0, 0 };
- sws_scale(scalerContext, src, srcStride, 0, m_height, dst, dstStride);
+ sws_scale(scalerContext, src, srcStride, 0, height, dst, dstStride);
}
}
}
diff --git a/xbmc/cores/RetroPlayer/rendering/RPRenderManager.h b/xbmc/cores/RetroPlayer/rendering/RPRenderManager.h
index 0dffc27365..71769699e5 100644
--- a/xbmc/cores/RetroPlayer/rendering/RPRenderManager.h
+++ b/xbmc/cores/RetroPlayer/rendering/RPRenderManager.h
@@ -86,8 +86,8 @@ namespace RETRO
CGUIRenderTargetFactory *GetGUIRenderTargetFactory() { return m_renderControlFactory.get(); }
// Functions called from game loop
- bool Configure(AVPixelFormat format, unsigned int width, unsigned int height, unsigned int orientation);
- void AddFrame(const uint8_t* data, size_t size);
+ bool Configure(AVPixelFormat format, unsigned int nominalWidth, unsigned int nominalHeight, unsigned int maxWidth, unsigned int maxHeight);
+ void AddFrame(const uint8_t* data, size_t size, unsigned int width, unsigned int height, unsigned int orientationDegCW);
// Functions called from the player
void SetSpeed(double speed);
@@ -142,7 +142,7 @@ namespace RETRO
/*!
* \brief Utility function to copy a frame and rescale pixels if necessary
*/
- void CopyFrame(IRenderBuffer *renderBuffer, const uint8_t *data, size_t size, AVPixelFormat format);
+ void CopyFrame(IRenderBuffer *renderBuffer, AVPixelFormat format, const uint8_t *data, size_t size, unsigned int width, unsigned int height);
CRenderVideoSettings GetEffectiveSettings(const IGUIRenderSettings *settings) const;
@@ -152,9 +152,10 @@ namespace RETRO
// Stream properties
AVPixelFormat m_format = AV_PIX_FMT_NONE;
- unsigned int m_width = 0;
- unsigned int m_height = 0;
- unsigned int m_orientation = 0; // Degrees counter-clockwise
+ unsigned int m_maxWidth = 0;
+ unsigned int m_maxHeight = 0;
+ unsigned int m_width = 0; //! @todo Remove me when dimension changing is implemented
+ unsigned int m_height = 0; //! @todo Remove me when dimension changing is implemented
// Render properties
enum class RENDER_STATE
diff --git a/xbmc/cores/RetroPlayer/rendering/RenderVideoSettings.h b/xbmc/cores/RetroPlayer/rendering/RenderVideoSettings.h
index f0249e7885..02e37d96a1 100644
--- a/xbmc/cores/RetroPlayer/rendering/RenderVideoSettings.h
+++ b/xbmc/cores/RetroPlayer/rendering/RenderVideoSettings.h
@@ -25,6 +25,9 @@ namespace KODI
{
namespace RETRO
{
+ /*!
+ * \brief Video settings provided by the rendering system
+ */
class CRenderVideoSettings
{
public:
diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp
index bb5fa5f94b..2bf2130d64 100644
--- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp
+++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp
@@ -68,13 +68,13 @@ bool CRPBaseRenderer::IsCompatible(const CRenderVideoSettings &settings) const
return true;
}
-bool CRPBaseRenderer::Configure(AVPixelFormat format, unsigned int width, unsigned int height, unsigned int orientation)
+bool CRPBaseRenderer::Configure(AVPixelFormat format, unsigned int width, unsigned int height)
{
m_format = format;
m_sourceWidth = width;
m_sourceHeight = height;
m_sourceFrameRatio = static_cast<float>(width) / static_cast<float>(height);
- m_renderOrientation = orientation;
+ m_renderOrientation = 0; //! @todo
if (!m_bufferPool->IsConfigured())
{
diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.h b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.h
index b7ae3baef0..7392fd779a 100644
--- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.h
+++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.h
@@ -49,7 +49,7 @@ namespace RETRO
IRenderBufferPool *GetBufferPool() { return m_bufferPool.get(); }
// Player functions
- bool Configure(AVPixelFormat format, unsigned int width, unsigned int height, unsigned int orientation);
+ bool Configure(AVPixelFormat format, unsigned int width, unsigned int height);
void FrameMove();
/*!
* \brief Performs whatever necessary before rendering the frame
diff --git a/xbmc/cores/RetroPlayer/streams/CMakeLists.txt b/xbmc/cores/RetroPlayer/streams/CMakeLists.txt
new file mode 100644
index 0000000000..b5acbc9172
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SOURCES RetroPlayerAudio.cpp
+ RetroPlayerStreamTypes.cpp
+ RetroPlayerVideo.cpp
+ RPStreamManager.cpp
+)
+
+set(HEADERS IRetroPlayerStream.h
+ IStreamManager.h
+ RetroPlayerAudio.h
+ RetroPlayerStreamTypes.h
+ RetroPlayerVideo.h
+ RPStreamManager.h
+)
+
+core_add_library(retroplayer_streams)
diff --git a/xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h b/xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h
new file mode 100644
index 0000000000..1f1ef6cdb8
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 "RetroPlayerStreamTypes.h"
+
+namespace KODI
+{
+namespace RETRO
+{
+
+struct StreamProperties
+{
+};
+
+struct StreamBuffer
+{
+};
+
+struct StreamPacket
+{
+};
+
+class IRetroPlayerStream
+{
+public:
+ virtual ~IRetroPlayerStream() = default;
+
+ /*!
+ * \brief Open a stream
+ *
+ * \return True if the stream was opened, false otherwise
+ */
+ virtual bool OpenStream(const StreamProperties& properties) = 0;
+
+ /*!
+ * \brief Get a buffer for zero-copy stream data
+ *
+ * \param width The framebuffer width, or 0 for no width specified
+ * \param height The framebuffer height, or 0 for no height specified
+ * \param[out] buffer The buffer, or unmodified if false is returned
+ *
+ * \return True if a buffer was returned, false otherwise
+ */
+ virtual bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) = 0;
+
+ /*!
+ * \brief Add a data packet to a stream
+ *
+ * \param packet The data packet
+ */
+ virtual void AddStreamData(const StreamPacket& packet) = 0;
+
+ /*!
+ * \brief Close the stream
+ */
+ virtual void CloseStream() = 0;
+};
+
+}
+}
diff --git a/xbmc/cores/RetroPlayer/streams/IStreamManager.h b/xbmc/cores/RetroPlayer/streams/IStreamManager.h
new file mode 100644
index 0000000000..c4302e806b
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/IStreamManager.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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 "RetroPlayerStreamTypes.h"
+
+namespace KODI
+{
+namespace RETRO
+{
+
+class IStreamManager
+{
+public:
+ virtual ~IStreamManager() = default;
+
+ /*!
+ * \brief Create a stream for gameplay data
+ *
+ * \param streamType The stream type
+ *
+ * \return A stream handle, or empty on failure
+ */
+ virtual StreamPtr CreateStream(StreamType streamType) = 0;
+
+ /*!
+ * \brief Free the specified stream
+ *
+ * \param stream The stream to close
+ */
+ virtual void CloseStream(StreamPtr stream) = 0;
+};
+
+}
+}
diff --git a/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp b/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp
new file mode 100644
index 0000000000..41df4f92ad
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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 "RPStreamManager.h"
+#include "IRetroPlayerStream.h"
+#include "RetroPlayerAudio.h"
+//#include "RetroPlayerHardwareBuffer.h" //! @todo
+//#include "RetroPlayerSoftwareBuffer.h" //! @todo
+#include "RetroPlayerVideo.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+CRPStreamManager::CRPStreamManager(CRPRenderManager& renderManager, CRPProcessInfo& processInfo) :
+ m_renderManager(renderManager),
+ m_processInfo(processInfo)
+{
+}
+
+void CRPStreamManager::EnableAudio(bool bEnable)
+{
+ if (m_audioStream != nullptr)
+ m_audioStream->Enable(bEnable);
+}
+
+StreamPtr CRPStreamManager::CreateStream(StreamType streamType)
+{
+ switch (streamType)
+ {
+ case StreamType::AUDIO:
+ {
+ // Save pointer to audio stream
+ m_audioStream = new CRetroPlayerAudio(m_processInfo);
+
+ return StreamPtr(m_audioStream);
+ }
+ case StreamType::VIDEO:
+ {
+ return StreamPtr(new CRetroPlayerVideo(m_renderManager, m_processInfo));
+ }
+ case StreamType::SW_BUFFER:
+ {
+ //return StreamPtr(new CRetroPlayerSoftware(m_renderManager, m_processInfo)); //! @todo
+ }
+ case StreamType::HW_BUFFER:
+ {
+ //return StreamPtr(new CRetroPlayerHardware(m_renderManager, m_processInfo)); //! @todo
+ }
+ default:
+ break;
+ }
+
+ return StreamPtr();
+}
+
+void CRPStreamManager::CloseStream(StreamPtr stream)
+{
+ if (stream)
+ {
+ if (stream.get() == m_audioStream)
+ m_audioStream = nullptr;
+
+ stream->CloseStream();
+ }
+}
diff --git a/xbmc/cores/RetroPlayer/streams/RPStreamManager.h b/xbmc/cores/RetroPlayer/streams/RPStreamManager.h
new file mode 100644
index 0000000000..9e75aab840
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RPStreamManager.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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 "IStreamManager.h"
+
+namespace KODI
+{
+namespace RETRO
+{
+ class CRetroPlayerAudio;
+ class CRPProcessInfo;
+ class CRPRenderManager;
+
+ class CRPStreamManager : public IStreamManager
+ {
+ public:
+ CRPStreamManager(CRPRenderManager& renderManager, CRPProcessInfo& processInfo);
+ ~CRPStreamManager() override = default;
+
+ void EnableAudio(bool bEnable);
+
+ // Implementation of IStreamManager
+ StreamPtr CreateStream(StreamType streamType) override;
+ void CloseStream(StreamPtr stream) override;
+
+ private:
+ // Construction parameters
+ CRPRenderManager& m_renderManager;
+ CRPProcessInfo& m_processInfo;
+
+ // Stream parameters
+ CRetroPlayerAudio* m_audioStream = nullptr;
+ };
+}
+}
diff --git a/xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp
index 80649fde15..8d616e4b42 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayerAudio.cpp
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp
@@ -18,15 +18,17 @@
*
*/
-#include "ServiceBroker.h"
#include "RetroPlayerAudio.h"
#include "cores/AudioEngine/Interfaces/AE.h"
#include "cores/AudioEngine/Interfaces/AEStream.h"
#include "cores/AudioEngine/Utils/AEChannelInfo.h"
#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "cores/RetroPlayer/audio/AudioTranslator.h"
#include "cores/RetroPlayer/process/RPProcessInfo.h"
-#include "threads/Thread.h"
#include "utils/log.h"
+#include "ServiceBroker.h"
+
+#include <cmath>
using namespace KODI;
using namespace RETRO;
@@ -46,20 +48,56 @@ CRetroPlayerAudio::~CRetroPlayerAudio()
CloseStream();
}
-bool CRetroPlayerAudio::OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout)
+bool CRetroPlayerAudio::OpenStream(const StreamProperties& properties)
{
+ const AudioStreamProperties& audioProperties = reinterpret_cast<const AudioStreamProperties&>(properties);
+
+ const AEDataFormat pcmFormat = CAudioTranslator::TranslatePCMFormat(audioProperties.format);
+ if (pcmFormat == AE_FMT_INVALID)
+ {
+ CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Unknown PCM format: %d", static_cast<int>(audioProperties.format));
+ return false;
+ }
+
+ unsigned int iSampleRate = static_cast<unsigned int>(std::round(audioProperties.sampleRate));
+ if (iSampleRate == 0)
+ {
+ CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Invalid samplerate: %f", audioProperties.sampleRate);
+ return false;
+ }
+
+ CAEChannelInfo channelLayout;
+ for (auto it = audioProperties.channelMap.begin(); it != audioProperties.channelMap.end(); ++it)
+ {
+ AEChannel channel = CAudioTranslator::TranslateAudioChannel(*it);
+ if (channel == AE_CH_NULL)
+ break;
+
+ channelLayout += channel;
+ }
+
+ if (!channelLayout.IsLayoutValid())
+ {
+ CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Empty channel layout");
+ return false;
+ }
+
if (m_pAudioStream != nullptr)
CloseStream();
- CLog::Log(LOGINFO, "RetroPlayer[AUDIO]: Creating audio stream, sample rate = %d", samplerate);
+ CLog::Log(LOGINFO, "RetroPlayer[AUDIO]: Creating audio stream, sample rate = %d", iSampleRate);
+
+ IAE* audioEngine = CServiceBroker::GetActiveAE();
+ if (audioEngine == nullptr)
+ return false;
AEAudioFormat audioFormat;
- audioFormat.m_dataFormat = format;
- audioFormat.m_sampleRate = samplerate;
+ audioFormat.m_dataFormat = pcmFormat;
+ audioFormat.m_sampleRate = iSampleRate;
audioFormat.m_channelLayout = channelLayout;
- m_pAudioStream = CServiceBroker::GetActiveAE()->MakeStream(audioFormat);
+ m_pAudioStream = audioEngine->MakeStream(audioFormat);
- if (!m_pAudioStream)
+ if (m_pAudioStream == nullptr)
{
CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Failed to create audio stream");
return false;
@@ -72,21 +110,16 @@ bool CRetroPlayerAudio::OpenPCMStream(AEDataFormat format, unsigned int samplera
return true;
}
-bool CRetroPlayerAudio::OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout)
+void CRetroPlayerAudio::AddStreamData(const StreamPacket &packet)
{
- CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Encoded audio stream not supported");
+ const AudioStreamPacket& audioPacket = reinterpret_cast<const AudioStreamPacket&>(packet);
- return true; //! @todo
-}
-
-void CRetroPlayerAudio::AddData(const uint8_t* data, size_t size)
-{
if (m_bAudioEnabled)
{
if (m_pAudioStream)
{
const size_t frameSize = m_pAudioStream->GetChannelCount() * (CAEUtil::DataFormatToBits(m_pAudioStream->GetDataFormat()) >> 3);
- m_pAudioStream->AddData(&data, 0, static_cast<unsigned int>(size / frameSize));
+ m_pAudioStream->AddData(&audioPacket.data, 0, static_cast<unsigned int>(audioPacket.size / frameSize));
}
}
}
diff --git a/xbmc/cores/RetroPlayer/RetroPlayerAudio.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h
index 3d6d4a911b..3183a54439 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayerAudio.h
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h
@@ -19,7 +19,7 @@
*/
#pragma once
-#include "games/addons/GameClientCallbacks.h"
+#include "IRetroPlayerStream.h"
#include <memory>
@@ -31,24 +31,37 @@ namespace RETRO
{
class CRPProcessInfo;
- class CRetroPlayerAudio : public GAME::IGameAudioCallback
+ struct AudioStreamProperties
+ {
+ PCMFormat format;
+ double sampleRate;
+ AudioChannelMap channelMap;
+ };
+
+ struct AudioStreamPacket
+ {
+ const uint8_t* data;
+ size_t size;
+ };
+
+ class CRetroPlayerAudio : public IRetroPlayerStream
{
public:
explicit CRetroPlayerAudio(CRPProcessInfo& processInfo);
~CRetroPlayerAudio() override;
- // implementation of IGameAudioCallback
- bool OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout) override;
- bool OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout) override;
- void AddData(const uint8_t* data, size_t size) override;
- void CloseStream() override;
-
void Enable(bool bEnabled) { m_bAudioEnabled = bEnabled; }
+ // implementation of IRetroPlayerStream
+ bool OpenStream(const StreamProperties& properties) override;
+ bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) override { return false; }
+ void AddStreamData(const StreamPacket& packet) override;
+ void CloseStream() override;
+
private:
CRPProcessInfo& m_processInfo;
IAEStream* m_pAudioStream;
- bool m_bAudioEnabled;
+ bool m_bAudioEnabled;
};
}
}
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp
new file mode 100644
index 0000000000..5967c37676
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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 "RetroPlayerStreamTypes.h"
+#include "IRetroPlayerStream.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+void DeleteStream::operator()(IRetroPlayerStream* stream)
+{
+ delete stream;
+}
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h
new file mode 100644
index 0000000000..ea3c8e82fa
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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 <array>
+#include <memory>
+
+namespace KODI
+{
+namespace RETRO
+{
+class IRetroPlayerStream;
+
+struct DeleteStream
+{
+ void operator()(IRetroPlayerStream* stream);
+};
+
+using StreamPtr = std::unique_ptr<IRetroPlayerStream, DeleteStream>;
+
+enum class StreamType
+{
+ AUDIO,
+ VIDEO,
+ SW_BUFFER,
+ HW_BUFFER,
+};
+
+enum class PCMFormat
+{
+ FMT_UNKNOWN,
+ FMT_S16NE,
+};
+
+enum class AudioChannel
+{
+ CH_NULL, // Channel list terminator
+ CH_FL,
+ CH_FR,
+ CH_FC,
+ CH_LFE,
+ CH_BL,
+ CH_BR,
+ CH_FLOC,
+ CH_FROC,
+ CH_BC,
+ CH_SL,
+ CH_SR,
+ CH_TFL,
+ CH_TFR,
+ CH_TFC,
+ CH_TC,
+ CH_TBL,
+ CH_TBR,
+ CH_TBC,
+ CH_BLOC,
+ CH_BROC,
+ CH_COUNT
+};
+
+using AudioChannelMap = std::array<AudioChannel, static_cast<unsigned int>(AudioChannel::CH_COUNT)>;
+
+enum class PixelFormat
+{
+ FMT_UNKNOWN,
+ FMT_0RGB8888,
+ FMT_RGB565,
+ FMT_0RGB1555,
+};
+
+enum class VideoRotation
+{
+ ROTATION_0,
+ ROTATION_90_CCW,
+ ROTATION_180_CCW,
+ ROTATION_270_CCW,
+};
+
+}
+}
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp
new file mode 100644
index 0000000000..85b384a496
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012-2017 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 "cores/RetroPlayer/process/RPProcessInfo.h"
+#include "cores/RetroPlayer/rendering/RenderTranslator.h"
+#include "cores/RetroPlayer/rendering/RPRenderManager.h"
+#include "utils/log.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+CRetroPlayerVideo::CRetroPlayerVideo(CRPRenderManager& renderManager, CRPProcessInfo& processInfo) :
+ m_renderManager(renderManager),
+ m_processInfo(processInfo)
+{
+ CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Initializing video");
+
+ m_renderManager.Initialize();
+}
+
+CRetroPlayerVideo::~CRetroPlayerVideo()
+{
+ CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Deinitializing video");
+
+ CloseStream();
+ m_renderManager.Deinitialize();
+}
+
+bool CRetroPlayerVideo::OpenStream(const StreamProperties& properties)
+{
+ const VideoStreamProperties& videoProperties = reinterpret_cast<const VideoStreamProperties&>(properties);
+
+ if (m_bOpen)
+ {
+ CloseStream();
+ m_bOpen = false;
+ }
+
+ const AVPixelFormat pixfmt = videoProperties.pixfmt;
+ const unsigned int nominalWidth = videoProperties.nominalWidth;
+ const unsigned int nominalHeight = videoProperties.nominalHeight;
+ const unsigned int maxWidth = videoProperties.maxWidth;
+ const unsigned int maxHeight = videoProperties.maxHeight;
+ //const float pixelAspectRatio = videoProperties.pixelAspectRatio; //! @todo
+
+ CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Creating video stream - format %s, nominal %ux%u, max %ux%u",
+ CRenderTranslator::TranslatePixelFormat(pixfmt),
+ nominalWidth,
+ nominalHeight,
+ maxWidth,
+ maxHeight);
+
+ m_processInfo.SetVideoPixelFormat(pixfmt);
+ m_processInfo.SetVideoDimensions(nominalWidth, nominalHeight); // Report nominal height for now
+
+ if (m_renderManager.Configure(pixfmt, nominalWidth, nominalHeight, maxWidth, maxHeight))
+ m_bOpen = true;
+
+ return m_bOpen;
+}
+
+void CRetroPlayerVideo::AddStreamData(const StreamPacket &packet)
+{
+ const VideoStreamPacket& videoPacket = reinterpret_cast<const VideoStreamPacket&>(packet);
+
+ if (m_bOpen)
+ {
+ unsigned int orientationDegCCW = 0;
+ switch (videoPacket.rotation)
+ {
+ case VideoRotation::ROTATION_90_CCW:
+ orientationDegCCW = 90;
+ break;
+ case VideoRotation::ROTATION_180_CCW:
+ orientationDegCCW = 180;
+ break;
+ case VideoRotation::ROTATION_270_CCW:
+ orientationDegCCW = 270;
+ break;
+ default:
+ break;
+ }
+
+ m_renderManager.AddFrame(videoPacket.data,
+ videoPacket.size,
+ videoPacket.width,
+ videoPacket.height,
+ orientationDegCCW);
+ }
+}
+
+void CRetroPlayerVideo::CloseStream()
+{
+ if (m_bOpen)
+ {
+ CLog::Log(LOGDEBUG, "RetroPlayer[VIDEO]: Closing video stream");
+
+ m_renderManager.Flush();
+ m_bOpen = false;
+ }
+}
diff --git a/xbmc/cores/RetroPlayer/RetroPlayerVideo.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h
index 2472291c91..6cad4f3024 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayerVideo.h
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h
@@ -19,9 +19,11 @@
*/
#pragma once
-#include "games/addons/GameClientCallbacks.h"
+#include "IRetroPlayerStream.h"
-class CPixelConverter;
+extern "C" {
+#include "libavutil/pixfmt.h"
+}
namespace KODI
{
@@ -30,28 +32,49 @@ namespace RETRO
class CRPProcessInfo;
class CRPRenderManager;
+ struct VideoStreamProperties
+ {
+ AVPixelFormat pixfmt;
+ unsigned int nominalWidth;
+ unsigned int nominalHeight;
+ unsigned int maxWidth;
+ unsigned int maxHeight;
+ float pixelAspectRatio;
+ };
+
+ struct VideoStreamPacket
+ {
+ unsigned int width;
+ unsigned int height;
+ VideoRotation rotation;
+ const uint8_t *data;
+ size_t size;
+ };
+
/*!
* \brief Renders video frames provided by the game loop
*
* \sa CRPRenderManager
*/
- class CRetroPlayerVideo : public GAME::IGameVideoCallback
+ class CRetroPlayerVideo : public IRetroPlayerStream
{
public:
CRetroPlayerVideo(CRPRenderManager& m_renderManager, CRPProcessInfo& m_processInfo);
-
~CRetroPlayerVideo() override;
- // implementation of IGameVideoCallback
- bool OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, unsigned int orientationDeg) override;
- bool OpenEncodedStream(AVCodecID codec) override;
- void AddData(const uint8_t* data, size_t size) override;
+ // implementation of IRetroPlayerStream
+ bool OpenStream(const StreamProperties& properties) override;
+ bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) override { return false; }
+ void AddStreamData(const StreamPacket &packet) override;
void CloseStream() override;
private:
// Construction parameters
CRPRenderManager& m_renderManager;
- CRPProcessInfo& m_processInfo;
+ CRPProcessInfo& m_processInfo;
+
+ // Stream properties
+ bool m_bOpen = false;
};
}
}
diff --git a/xbmc/games/addons/GameClient.cpp b/xbmc/games/addons/GameClient.cpp
index 88cd818c6a..b090fc5995 100644
--- a/xbmc/games/addons/GameClient.cpp
+++ b/xbmc/games/addons/GameClient.cpp
@@ -32,6 +32,8 @@
#include "games/addons/input/GameClientInput.h"
#include "games/addons/playback/GameClientRealtimePlayback.h"
#include "games/addons/playback/GameClientReversiblePlayback.h"
+#include "games/addons/streams/GameClientStreams.h"
+#include "games/addons/streams/IGameClientStream.h"
#include "games/GameServices.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
@@ -118,8 +120,6 @@ CGameClient::CGameClient(ADDON::CAddonInfo addonInfo) :
m_bSupportsAllExtensions(false),
m_bIsPlaying(false),
m_serializeSize(0),
- m_audio(nullptr),
- m_video(nullptr),
m_region(GAME_REGION_UNKNOWN)
{
const ADDON::InfoMap& extraInfo = m_addonInfo.ExtraInfo();
@@ -208,16 +208,12 @@ bool CGameClient::Initialize(void)
m_struct.toKodi.kodiInstance = this;
m_struct.toKodi.CloseGame = cb_close_game;
- m_struct.toKodi.OpenPixelStream = cb_open_pixel_stream;
- m_struct.toKodi.OpenVideoStream = cb_open_video_stream;
- m_struct.toKodi.OpenPCMStream = cb_open_pcm_stream;
- m_struct.toKodi.OpenAudioStream = cb_open_audio_stream;
+ m_struct.toKodi.OpenStream = cb_open_stream;
+ m_struct.toKodi.GetStreamBuffer = cb_get_stream_buffer;
m_struct.toKodi.AddStreamData = cb_add_stream_data;
+ m_struct.toKodi.ReleaseStreamBuffer = cb_release_stream_buffer;
m_struct.toKodi.CloseStream = cb_close_stream;
- m_struct.toKodi.EnableHardwareRendering = cb_enable_hardware_rendering;
- m_struct.toKodi.HwGetCurrentFramebuffer = cb_hw_get_current_framebuffer;
m_struct.toKodi.HwGetProcAddress = cb_hw_get_proc_address;
- m_struct.toKodi.RenderFrame = cb_render_frame;
m_struct.toKodi.InputEvent = cb_input_event;
if (Create(ADDON_INSTANCE_GAME, &m_struct, &m_struct.props) == ADDON_STATUS_OK)
@@ -237,11 +233,8 @@ void CGameClient::Unload()
Destroy();
}
-bool CGameClient::OpenFile(const CFileItem& file, IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input)
+bool CGameClient::OpenFile(const CFileItem& file, RETRO::IStreamManager& streamManager, IGameInputCallback *input)
{
- if (audio == nullptr || video == nullptr)
- return false;
-
// Check if we should open in standalone mode
if (file.GetPath().empty())
return false;
@@ -287,13 +280,13 @@ bool CGameClient::OpenFile(const CFileItem& file, IGameAudioCallback* audio, IGa
return false;
}
- if (!InitializeGameplay(file.GetPath(), audio, video, input))
+ if (!InitializeGameplay(file.GetPath(), streamManager, input))
return false;
return true;
}
-bool CGameClient::OpenStandalone(IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input)
+bool CGameClient::OpenStandalone(RETRO::IStreamManager& streamManager, IGameInputCallback *input)
{
CLog::Log(LOGDEBUG, "GameClient: Loading %s in standalone mode", ID().c_str());
@@ -315,21 +308,21 @@ bool CGameClient::OpenStandalone(IGameAudioCallback* audio, IGameVideoCallback*
return false;
}
- if (!InitializeGameplay(ID(), audio, video, input))
+ if (!InitializeGameplay(ID(), streamManager, input))
return false;
return true;
}
-bool CGameClient::InitializeGameplay(const std::string& gamePath, IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input)
+bool CGameClient::InitializeGameplay(const std::string& gamePath, RETRO::IStreamManager& streamManager, IGameInputCallback *input)
{
if (LoadGameInfo())
{
+ Streams().Initialize(streamManager);
+
m_bIsPlaying = true;
m_gamePath = gamePath;
m_serializeSize = GetSerializeSize();
- m_audio = audio;
- m_video = video;
m_input = input;
m_inGameSaves.reset(new CGameClientInGameSaves(this, &m_struct.toAddon));
@@ -346,34 +339,32 @@ bool CGameClient::InitializeGameplay(const std::string& gamePath, IGameAudioCall
bool CGameClient::LoadGameInfo()
{
- // Get information about system audio/video timings and geometry
+ // Get information about system timings
// Can be called only after retro_load_game()
- game_system_av_info av_info = { };
+ game_system_timing timingInfo = { };
bool bSuccess = false;
- try { bSuccess = LogError(m_struct.toAddon.GetGameInfo(&av_info), "GetGameInfo()"); }
- catch (...) { LogException("GetGameInfo()"); }
+ try { bSuccess = LogError(m_struct.toAddon.GetGameTiming(&timingInfo), "GetGameTiming()"); }
+ catch (...) { LogException("GetGameTiming()"); }
if (!bSuccess)
+ {
+ CLog::Log(LOGERROR, "GameClient: Failed to get timing info");
return false;
+ }
GAME_REGION region;
try { region = m_struct.toAddon.GetRegion(); }
catch (...) { LogException("GetRegion()"); return false; }
CLog::Log(LOGINFO, "GAME: ---------------------------------------");
- CLog::Log(LOGINFO, "GAME: Base Width: %u", av_info.geometry.base_width);
- CLog::Log(LOGINFO, "GAME: Base Height: %u", av_info.geometry.base_height);
- CLog::Log(LOGINFO, "GAME: Max Width: %u", av_info.geometry.max_width);
- CLog::Log(LOGINFO, "GAME: Max Height: %u", av_info.geometry.max_height);
- CLog::Log(LOGINFO, "GAME: Aspect Ratio: %f", av_info.geometry.aspect_ratio);
- CLog::Log(LOGINFO, "GAME: FPS: %f", av_info.timing.fps);
- CLog::Log(LOGINFO, "GAME: Sample Rate: %f", av_info.timing.sample_rate);
- CLog::Log(LOGINFO, "GAME: Region: %s", CGameClientTranslator::TranslateRegion(region));
+ CLog::Log(LOGINFO, "GAME: FPS: %f", timingInfo.fps);
+ CLog::Log(LOGINFO, "GAME: Sample Rate: %f", timingInfo.sample_rate);
+ CLog::Log(LOGINFO, "GAME: Region: %s", CGameClientTranslator::TranslateRegion(region));
CLog::Log(LOGINFO, "GAME: ---------------------------------------");
- m_framerate = av_info.timing.fps;
- m_samplerate = av_info.timing.sample_rate;
+ m_framerate = timingInfo.fps;
+ m_samplerate = timingInfo.sample_rate;
m_region = region;
return true;
@@ -480,10 +471,9 @@ void CGameClient::CloseFile()
m_bIsPlaying = false;
m_gamePath.clear();
m_serializeSize = 0;
-
- m_audio = nullptr;
- m_video = nullptr;
m_input = nullptr;
+
+ Streams().Deinitialize();
}
void CGameClient::RunFrame()
@@ -507,148 +497,6 @@ void CGameClient::RunFrame()
}
}
-bool CGameClient::OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
-{
- if (!m_video)
- return false;
-
- AVPixelFormat pixelFormat = CGameClientTranslator::TranslatePixelFormat(format);
- if (pixelFormat == AV_PIX_FMT_NONE)
- {
- CLog::Log(LOGERROR, "GAME: Unknown pixel format: %d", format);
- return false;
- }
-
- unsigned int orientation = 0;
- switch (rotation)
- {
- case GAME_VIDEO_ROTATION_90:
- orientation = 360 - 90;
- break;
- case GAME_VIDEO_ROTATION_180:
- orientation = 360 - 180;
- break;
- case GAME_VIDEO_ROTATION_270:
- orientation = 360 - 270;
- break;
- default:
- break;
- }
-
- return m_video->OpenPixelStream(pixelFormat, width, height, orientation);
-}
-
-bool CGameClient::OpenVideoStream(GAME_VIDEO_CODEC codec)
-{
- if (!m_video)
- return false;
-
- AVCodecID videoCodec = CGameClientTranslator::TranslateVideoCodec(codec);
- if (videoCodec == AV_CODEC_ID_NONE)
- {
- CLog::Log(LOGERROR, "GAME: Unknown video format: %d", codec);
- return false;
- }
-
- return m_video->OpenEncodedStream(videoCodec);
-}
-
-bool CGameClient::OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channelMap)
-{
- if (!m_audio || channelMap == nullptr)
- return false;
-
- AEDataFormat pcmFormat = CGameClientTranslator::TranslatePCMFormat(format);
- if (pcmFormat == AE_FMT_INVALID)
- {
- CLog::Log(LOGERROR, "GAME: Unknown PCM format: %d", format);
- return false;
- }
-
- CAEChannelInfo channelLayout;
- for (const GAME_AUDIO_CHANNEL* channelPtr = channelMap; *channelPtr != GAME_CH_NULL; channelPtr++)
- {
- AEChannel channel = CGameClientTranslator::TranslateAudioChannel(*channelPtr);
- if (channel == AE_CH_NULL)
- {
- CLog::Log(LOGERROR, "GAME: Unknown channel ID: %d", *channelPtr);
- return false;
- }
- channelLayout += channel;
- }
-
- return m_audio->OpenPCMStream(pcmFormat, static_cast<unsigned int>(m_samplerate), channelLayout);
-}
-
-bool CGameClient::OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channelMap)
-{
- if (!m_audio)
- return false;
-
- AVCodecID audioCodec = CGameClientTranslator::TranslateAudioCodec(codec);
- if (audioCodec == AV_CODEC_ID_NONE)
- {
- CLog::Log(LOGERROR, "GAME: Unknown audio codec: %d", codec);
- return false;
- }
-
- CAEChannelInfo channelLayout;
- for (const GAME_AUDIO_CHANNEL* channelPtr = channelMap; *channelPtr != GAME_CH_NULL; channelPtr++)
- {
- AEChannel channel = CGameClientTranslator::TranslateAudioChannel(*channelPtr);
- if (channel == AE_CH_NULL)
- {
- CLog::Log(LOGERROR, "GAME: Unknown channel ID: %d", *channelPtr);
- return false;
- }
- channelLayout += channel;
- }
-
- return m_audio->OpenEncodedStream(audioCodec, static_cast<unsigned int>(m_samplerate), channelLayout);
-}
-
-void CGameClient::AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
-{
- switch (stream)
- {
- case GAME_STREAM_AUDIO:
- {
- if (m_audio)
- m_audio->AddData(data, size);
- break;
- }
- case GAME_STREAM_VIDEO:
- {
- if (m_video)
- m_video->AddData(data, size);
- break;
- }
- default:
- break;
- }
-}
-
-void CGameClient::CloseStream(GAME_STREAM_TYPE stream)
-{
- switch (stream)
- {
- case GAME_STREAM_AUDIO:
- {
- if (m_audio)
- m_audio->CloseStream();
- break;
- }
- case GAME_STREAM_VIDEO:
- {
- if (m_video)
- m_video->CloseStream();
- break;
- }
- default:
- break;
- }
-}
-
size_t CGameClient::GetSerializeSize()
{
CSingleLock lock(m_critSection);
@@ -734,77 +582,65 @@ void CGameClient::cb_close_game(void* kodiInstance)
CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_STOP)));
}
-int CGameClient::cb_open_pixel_stream(void* kodiInstance, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation)
+void* CGameClient::cb_open_stream(void* kodiInstance, const game_stream_properties *properties)
{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return -1;
-
- return gameClient->OpenPixelStream(format, width, height, rotation) ? 0 : -1;
-}
+ if (properties == nullptr)
+ return nullptr;
-int CGameClient::cb_open_video_stream(void* kodiInstance, GAME_VIDEO_CODEC codec)
-{
CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return -1;
+ if (gameClient == nullptr)
+ return nullptr;
- return gameClient->OpenVideoStream(codec) ? 0 : -1;
+ return gameClient->Streams().OpenStream(*properties);
}
-int CGameClient::cb_open_pcm_stream(void* kodiInstance, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map)
+bool CGameClient::cb_get_stream_buffer(void* kodiInstance, void *stream, unsigned int width, unsigned int height, game_stream_buffer *buffer)
{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return -1;
-
- return gameClient->OpenPCMStream(format, channel_map) ? 0 : -1;
-}
+ if (buffer == nullptr)
+ return false;
-int CGameClient::cb_open_audio_stream(void* kodiInstance, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map)
-{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return -1;
+ IGameClientStream *gameClientStream = static_cast<IGameClientStream*>(stream);
+ if (gameClientStream == nullptr)
+ return false;
- return gameClient->OpenAudioStream(codec, channel_map) ? 0 : -1;
+ return gameClientStream->GetBuffer(width, height, *buffer);
}
-void CGameClient::cb_add_stream_data(void* kodiInstance, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size)
+void CGameClient::cb_add_stream_data(void* kodiInstance, void *stream, const game_stream_packet *packet)
{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
+ if (packet == nullptr)
return;
- gameClient->AddStreamData(stream, data, size);
-}
-
-void CGameClient::cb_close_stream(void* kodiInstance, GAME_STREAM_TYPE stream)
-{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
+ IGameClientStream *gameClientStream = static_cast<IGameClientStream*>(stream);
+ if (gameClientStream == nullptr)
return;
- gameClient->CloseStream(stream);
+ gameClientStream->AddData(*packet);
}
-void CGameClient::cb_enable_hardware_rendering(void* kodiInstance, const game_hw_info *hw_info)
+void CGameClient::cb_release_stream_buffer(void* kodiInstance, void *stream, game_stream_buffer *buffer)
{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
+ if (buffer == nullptr)
return;
- //! @todo
+ IGameClientStream *gameClientStream = static_cast<IGameClientStream*>(stream);
+ if (gameClientStream == nullptr)
+ return;
+
+ gameClientStream->ReleaseBuffer(*buffer);
}
-uintptr_t CGameClient::cb_hw_get_current_framebuffer(void* kodiInstance)
+void CGameClient::cb_close_stream(void* kodiInstance, void *stream)
{
CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return 0;
+ if (gameClient == nullptr)
+ return;
- //! @todo
- return 0;
+ IGameClientStream *gameClientStream = static_cast<IGameClientStream*>(stream);
+ if (gameClientStream == nullptr)
+ return;
+
+ gameClient->Streams().CloseStream(gameClientStream);
}
game_proc_address_t CGameClient::cb_hw_get_proc_address(void* kodiInstance, const char *sym)
@@ -817,15 +653,6 @@ game_proc_address_t CGameClient::cb_hw_get_proc_address(void* kodiInstance, cons
return nullptr;
}
-void CGameClient::cb_render_frame(void* kodiInstance)
-{
- CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
- if (!gameClient)
- return;
-
- //! @todo
-}
-
bool CGameClient::cb_input_event(void* kodiInstance, const game_input_event* event)
{
CGameClient *gameClient = static_cast<CGameClient*>(kodiInstance);
diff --git a/xbmc/games/addons/GameClient.h b/xbmc/games/addons/GameClient.h
index ab4ec26fc6..fc70397d99 100644
--- a/xbmc/games/addons/GameClient.h
+++ b/xbmc/games/addons/GameClient.h
@@ -30,22 +30,24 @@
#include <set>
#include <stdint.h>
#include <string>
-#include <vector>
class CFileItem;
namespace KODI
{
+namespace RETRO
+{
+ class IStreamManager;
+}
+
namespace GAME
{
class CGameClientInGameSaves;
class CGameClientInput;
class CGameClientProperties;
-class IGameAudioCallback;
class IGameClientPlayback;
class IGameInputCallback;
-class IGameVideoCallback;
/*!
* \ingroup games
@@ -63,10 +65,12 @@ public:
// Game subsystems (const)
const CGameClientInput &Input() const { return *m_subsystems.Input; }
const CGameClientProperties &AddonProperties() const { return *m_subsystems.AddonProperties; }
+ const CGameClientStreams &Streams() const { return *m_subsystems.Streams; }
// Game subsystems (mutable)
CGameClientInput &Input() { return *m_subsystems.Input; }
CGameClientProperties &AddonProperties() { return *m_subsystems.AddonProperties; }
+ CGameClientStreams &Streams() { return *m_subsystems.Streams; }
// Implementation of IAddon via CAddonDll
virtual std::string LibPath() const override;
@@ -83,8 +87,8 @@ public:
// Start/stop gameplay
bool Initialize(void);
void Unload();
- bool OpenFile(const CFileItem& file, IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input);
- bool OpenStandalone(IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input);
+ bool OpenFile(const CFileItem& file, RETRO::IStreamManager& streamManager, IGameInputCallback *input);
+ bool OpenStandalone(RETRO::IStreamManager& streamManager, IGameInputCallback *input);
void Reset();
void CloseFile();
const std::string& GetGamePath() const { return m_gamePath; }
@@ -93,16 +97,9 @@ public:
bool IsPlaying() const { return m_bIsPlaying; }
IGameClientPlayback* GetPlayback() { return m_playback.get(); }
double GetFrameRate() const { return m_framerate; }
+ double GetSampleRate() const { return m_samplerate; }
void RunFrame();
- // Audio/video callbacks
- bool OpenPixelStream(GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
- bool OpenVideoStream(GAME_VIDEO_CODEC codec);
- bool OpenPCMStream(GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channelMap);
- bool OpenAudioStream(GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channelMap);
- void AddStreamData(GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
- void CloseStream(GAME_STREAM_TYPE stream);
-
// Access memory
size_t SerializeSize() const { return m_serializeSize; }
bool Serialize(uint8_t* data, size_t size);
@@ -121,7 +118,7 @@ public:
private:
// Private gameplay functions
- bool InitializeGameplay(const std::string& gamePath, IGameAudioCallback* audio, IGameVideoCallback* video, IGameInputCallback *input);
+ bool InitializeGameplay(const std::string& gamePath, RETRO::IStreamManager& streamManager, IGameInputCallback *input);
bool LoadGameInfo();
void NotifyError(GAME_ERROR error);
std::string GetMissingResource();
@@ -139,16 +136,12 @@ private:
*/
//@{
static void cb_close_game(void* kodiInstance);
- static int cb_open_pixel_stream(void* kodiInstance, GAME_PIXEL_FORMAT format, unsigned int width, unsigned int height, GAME_VIDEO_ROTATION rotation);
- static int cb_open_video_stream(void* kodiInstance, GAME_VIDEO_CODEC codec);
- static int cb_open_pcm_stream(void* kodiInstance, GAME_PCM_FORMAT format, const GAME_AUDIO_CHANNEL* channel_map);
- static int cb_open_audio_stream(void* kodiInstance, GAME_AUDIO_CODEC codec, const GAME_AUDIO_CHANNEL* channel_map);
- static void cb_add_stream_data(void* kodiInstance, GAME_STREAM_TYPE stream, const uint8_t* data, unsigned int size);
- static void cb_close_stream(void* kodiInstance, GAME_STREAM_TYPE stream);
- static void cb_enable_hardware_rendering(void* kodiInstance, const game_hw_info* hw_info);
- static uintptr_t cb_hw_get_current_framebuffer(void* kodiInstance);
+ static void* cb_open_stream(void* kodiInstance, const game_stream_properties *properties);
+ static bool cb_get_stream_buffer(void* kodiInstance, void *stream, unsigned int width, unsigned int height, game_stream_buffer *buffer);
+ static void cb_add_stream_data(void* kodiInstance, void *stream, const game_stream_packet *packet);
+ static void cb_release_stream_buffer(void* kodiInstance, void *stream, game_stream_buffer *buffer);
+ static void cb_close_stream(void* kodiInstance, void *stream);
static game_proc_address_t cb_hw_get_proc_address(void* kodiInstance, const char* sym);
- static void cb_render_frame(void* kodiInstance);
static bool cb_input_event(void* kodiInstance, const game_input_event* event);
//@}
@@ -166,8 +159,6 @@ private:
std::atomic_bool m_bIsPlaying; // True between OpenFile() and CloseFile()
std::string m_gamePath;
size_t m_serializeSize;
- IGameAudioCallback* m_audio; // The audio callback passed to OpenFile()
- IGameVideoCallback* m_video; // The video callback passed to OpenFile()
IGameInputCallback* m_input = nullptr; // The input callback passed to OpenFile()
double m_framerate = 0.0; // Video frame rate (fps)
double m_samplerate = 0.0; // Audio sample rate (Hz)
diff --git a/xbmc/games/addons/GameClientCallbacks.h b/xbmc/games/addons/GameClientCallbacks.h
index c2fb9e94d6..4287e16923 100644
--- a/xbmc/games/addons/GameClientCallbacks.h
+++ b/xbmc/games/addons/GameClientCallbacks.h
@@ -19,41 +19,10 @@
*/
#pragma once
-#include "cores/AudioEngine/Utils/AEChannelData.h"
-
-#include "libavcodec/avcodec.h"
-#include "libavutil/pixfmt.h"
-
-#include <stdint.h>
-
-class CAEChannelInfo;
-
namespace KODI
{
namespace GAME
{
- class IGameAudioCallback
- {
- public:
- virtual ~IGameAudioCallback() = default;
-
- virtual bool OpenPCMStream(AEDataFormat format, unsigned int samplerate, const CAEChannelInfo& channelLayout) = 0;
- virtual bool OpenEncodedStream(AVCodecID codec, unsigned int samplerate, const CAEChannelInfo& channelLayout) = 0;
- virtual void AddData(const uint8_t* data, size_t size) = 0;
- virtual void CloseStream() = 0;
- };
-
- class IGameVideoCallback
- {
- public:
- virtual ~IGameVideoCallback() = default;
-
- virtual bool OpenPixelStream(AVPixelFormat pixfmt, unsigned int width, unsigned int height, unsigned int orientationDeg) = 0;
- virtual bool OpenEncodedStream(AVCodecID codec) = 0;
- virtual void AddData(const uint8_t* data, size_t size) = 0;
- virtual void CloseStream() = 0;
- };
-
class IGameInputCallback
{
public:
diff --git a/xbmc/games/addons/GameClientSubsystem.cpp b/xbmc/games/addons/GameClientSubsystem.cpp
index a063566bc6..5a6240c2de 100644
--- a/xbmc/games/addons/GameClientSubsystem.cpp
+++ b/xbmc/games/addons/GameClientSubsystem.cpp
@@ -23,6 +23,7 @@
#include "GameClientProperties.h"
#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
#include "games/addons/input/GameClientInput.h"
+#include "games/addons/streams/GameClientStreams.h"
using namespace KODI;
using namespace GAME;
@@ -44,6 +45,7 @@ GameClientSubsystems CGameClientSubsystem::CreateSubsystems(CGameClient &gameCli
subsystems.Input.reset(new CGameClientInput(gameClient, gameStruct, clientAccess));
subsystems.AddonProperties.reset(new CGameClientProperties(gameClient, gameStruct.props));
+ subsystems.Streams.reset(new CGameClientStreams(gameClient));
return subsystems;
}
@@ -52,6 +54,7 @@ void CGameClientSubsystem::DestroySubsystems(GameClientSubsystems &subsystems)
{
subsystems.Input.reset();
subsystems.AddonProperties.reset();
+ subsystems.Streams.reset();
}
CGameClientInput &CGameClientSubsystem::Input() const
@@ -63,3 +66,8 @@ CGameClientProperties &CGameClientSubsystem::AddonProperties() const
{
return m_gameClient.AddonProperties();
}
+
+CGameClientStreams &CGameClientSubsystem::Streams() const
+{
+ return m_gameClient.Streams();
+}
diff --git a/xbmc/games/addons/GameClientSubsystem.h b/xbmc/games/addons/GameClientSubsystem.h
index dc11f0dd0a..28978bad55 100644
--- a/xbmc/games/addons/GameClientSubsystem.h
+++ b/xbmc/games/addons/GameClientSubsystem.h
@@ -31,11 +31,13 @@ namespace GAME
class CGameClient;
class CGameClientInput;
class CGameClientProperties;
+ class CGameClientStreams;
struct GameClientSubsystems
{
std::unique_ptr<CGameClientInput> Input;
std::unique_ptr<CGameClientProperties> AddonProperties;
+ std::unique_ptr<CGameClientStreams> Streams;
};
/*!
@@ -73,6 +75,7 @@ namespace GAME
// Subsystems
CGameClientInput &Input() const;
CGameClientProperties &AddonProperties() const;
+ CGameClientStreams &Streams() const;
// Construction parameters
CGameClient &m_gameClient;
diff --git a/xbmc/games/addons/GameClientTranslator.cpp b/xbmc/games/addons/GameClientTranslator.cpp
index 51afd5e65e..b0ab3e3ef1 100644
--- a/xbmc/games/addons/GameClientTranslator.cpp
+++ b/xbmc/games/addons/GameClientTranslator.cpp
@@ -59,81 +59,96 @@ const char* CGameClientTranslator::ToString(GAME_MEMORY memory)
return "unknown memory";
}
-AVPixelFormat CGameClientTranslator::TranslatePixelFormat(GAME_PIXEL_FORMAT format)
+bool CGameClientTranslator::TranslateStreamType(GAME_STREAM_TYPE gameType, RETRO::StreamType &retroType)
{
- switch (format)
+ switch (gameType)
{
- case GAME_PIXEL_FORMAT_YUV420P: return AV_PIX_FMT_YUV420P;
- case GAME_PIXEL_FORMAT_0RGB8888: return AV_PIX_FMT_0RGB32;
- case GAME_PIXEL_FORMAT_RGB565: return AV_PIX_FMT_RGB565;
- case GAME_PIXEL_FORMAT_0RGB1555: return AV_PIX_FMT_RGB555;
+ case GAME_STREAM_AUDIO:
+ retroType = RETRO::StreamType::AUDIO;
+ return true;
+ case GAME_STREAM_VIDEO:
+ retroType = RETRO::StreamType::VIDEO;
+ return true;
+ case GAME_STREAM_SW_FRAMEBUFFER:
+ retroType = RETRO::StreamType::SW_BUFFER;
+ return true;
+ case GAME_STREAM_HW_FRAMEBUFFER:
+ retroType = RETRO::StreamType::HW_BUFFER;
+ return true;
default:
break;
}
- return AV_PIX_FMT_NONE;
+ return false;
}
-AVCodecID CGameClientTranslator::TranslateVideoCodec(GAME_VIDEO_CODEC codec)
+AVPixelFormat CGameClientTranslator::TranslatePixelFormat(GAME_PIXEL_FORMAT format)
{
- switch (codec)
+ switch (format)
{
- case GAME_VIDEO_CODEC_H264: return AV_CODEC_ID_H264;
+ case GAME_PIXEL_FORMAT_0RGB8888: return AV_PIX_FMT_0RGB32;
+ case GAME_PIXEL_FORMAT_RGB565: return AV_PIX_FMT_RGB565;
+ case GAME_PIXEL_FORMAT_0RGB1555: return AV_PIX_FMT_RGB555;
default:
break;
}
- return AV_CODEC_ID_NONE;
+ return AV_PIX_FMT_NONE;
}
-AEDataFormat CGameClientTranslator::TranslatePCMFormat(GAME_PCM_FORMAT format)
+RETRO::PCMFormat CGameClientTranslator::TranslatePCMFormat(GAME_PCM_FORMAT format)
{
switch (format)
{
- case GAME_PCM_FORMAT_S16NE: return AE_FMT_S16NE;
+ case GAME_PCM_FORMAT_S16NE: return RETRO::PCMFormat::FMT_S16NE;
default:
break;
}
- return AE_FMT_INVALID;
+ return RETRO::PCMFormat::FMT_UNKNOWN;
}
-AEChannel CGameClientTranslator::TranslateAudioChannel(GAME_AUDIO_CHANNEL channel)
+RETRO::AudioChannel CGameClientTranslator::TranslateAudioChannel(GAME_AUDIO_CHANNEL channel)
{
switch (channel)
{
- case GAME_CH_FL: return AE_CH_FL;
- case GAME_CH_FR: return AE_CH_FR;
- case GAME_CH_FC: return AE_CH_FC;
- case GAME_CH_LFE: return AE_CH_LFE;
- case GAME_CH_BL: return AE_CH_BL;
- case GAME_CH_BR: return AE_CH_BR;
- case GAME_CH_FLOC: return AE_CH_FLOC;
- case GAME_CH_FROC: return AE_CH_FROC;
- case GAME_CH_BC: return AE_CH_BC;
- case GAME_CH_SL: return AE_CH_SL;
- case GAME_CH_SR: return AE_CH_SR;
- case GAME_CH_TFL: return AE_CH_TFL;
- case GAME_CH_TFR: return AE_CH_TFR;
- case GAME_CH_TFC: return AE_CH_TFC;
- case GAME_CH_TC: return AE_CH_TC;
- case GAME_CH_TBL: return AE_CH_TBL;
- case GAME_CH_TBR: return AE_CH_TBR;
- case GAME_CH_TBC: return AE_CH_TBC;
- case GAME_CH_BLOC: return AE_CH_BLOC;
- case GAME_CH_BROC: return AE_CH_BROC;
+ case GAME_CH_FL: return RETRO::AudioChannel::CH_FL;
+ case GAME_CH_FR: return RETRO::AudioChannel::CH_FR;
+ case GAME_CH_FC: return RETRO::AudioChannel::CH_FC;
+ case GAME_CH_LFE: return RETRO::AudioChannel::CH_LFE;
+ case GAME_CH_BL: return RETRO::AudioChannel::CH_BL;
+ case GAME_CH_BR: return RETRO::AudioChannel::CH_BR;
+ case GAME_CH_FLOC: return RETRO::AudioChannel::CH_FLOC;
+ case GAME_CH_FROC: return RETRO::AudioChannel::CH_FROC;
+ case GAME_CH_BC: return RETRO::AudioChannel::CH_BC;
+ case GAME_CH_SL: return RETRO::AudioChannel::CH_SL;
+ case GAME_CH_SR: return RETRO::AudioChannel::CH_SR;
+ case GAME_CH_TFL: return RETRO::AudioChannel::CH_TFL;
+ case GAME_CH_TFR: return RETRO::AudioChannel::CH_TFR;
+ case GAME_CH_TFC: return RETRO::AudioChannel::CH_TFC;
+ case GAME_CH_TC: return RETRO::AudioChannel::CH_TC;
+ case GAME_CH_TBL: return RETRO::AudioChannel::CH_TBL;
+ case GAME_CH_TBR: return RETRO::AudioChannel::CH_TBR;
+ case GAME_CH_TBC: return RETRO::AudioChannel::CH_TBC;
+ case GAME_CH_BLOC: return RETRO::AudioChannel::CH_BLOC;
+ case GAME_CH_BROC: return RETRO::AudioChannel::CH_BROC;
default:
break;
}
- return AE_CH_NULL;
+ return RETRO::AudioChannel::CH_NULL;
}
-AVCodecID CGameClientTranslator::TranslateAudioCodec(GAME_AUDIO_CODEC codec)
+RETRO::VideoRotation CGameClientTranslator::TranslateRotation(GAME_VIDEO_ROTATION rotation)
{
- switch (codec)
+ switch (rotation)
{
- case GAME_AUDIO_CODEC_OPUS: return AV_CODEC_ID_OPUS;
+ case GAME_VIDEO_ROTATION_90_CCW:
+ return RETRO::VideoRotation::ROTATION_90_CCW;
+ case GAME_VIDEO_ROTATION_180_CCW:
+ return RETRO::VideoRotation::ROTATION_180_CCW;
+ case GAME_VIDEO_ROTATION_270_CCW:
+ return RETRO::VideoRotation::ROTATION_270_CCW;
default:
break;
}
- return AV_CODEC_ID_NONE;
+ return RETRO::VideoRotation::ROTATION_0;
}
GAME_KEY_MOD CGameClientTranslator::GetModifiers(KEYBOARD::Modifier modifier)
diff --git a/xbmc/games/addons/GameClientTranslator.h b/xbmc/games/addons/GameClientTranslator.h
index 154089f9cd..f47eaaa213 100644
--- a/xbmc/games/addons/GameClientTranslator.h
+++ b/xbmc/games/addons/GameClientTranslator.h
@@ -20,12 +20,13 @@
#pragma once
#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
-#include "cores/AudioEngine/Utils/AEChannelData.h"
+#include "cores/RetroPlayer/streams/RetroPlayerStreamTypes.h"
#include "games/controllers/ControllerTypes.h"
#include "input/keyboard/KeyboardTypes.h"
-#include "libavcodec/avcodec.h"
+extern "C" {
#include "libavutil/pixfmt.h"
+}
namespace KODI
{
@@ -57,39 +58,40 @@ namespace GAME
static const char* ToString(GAME_MEMORY error);
/*!
- * \brief Translate pixel format (Game API to FFMPEG).
- * \param format The pixel format to translate.
- * \return Translated pixel format.
+ * \brief Translate stream type (Game API to RetroPlayer).
+ * \param gameType The stream type to translate.
+ * \param[out] retroType The translated stream type.
+ * \return True if the Game API type was translated to a valid RetroPlayer type
*/
- static AVPixelFormat TranslatePixelFormat(GAME_PIXEL_FORMAT format);
+ static bool TranslateStreamType(GAME_STREAM_TYPE gameType, RETRO::StreamType &retroType);
/*!
- * \brief Translate video codec (Game API to FFMPEG).
- * \param format The video codec to translate.
- * \return Translated video codec format.
+ * \brief Translate pixel format (Game API to RetroPlayer/FFMPEG).
+ * \param format The pixel format to translate.
+ * \return Translated pixel format.
*/
- static AVCodecID TranslateVideoCodec(GAME_VIDEO_CODEC codec);
+ static AVPixelFormat TranslatePixelFormat(GAME_PIXEL_FORMAT format);
/*!
- * \brief Translate audio PCM format (Game API to AudioEngine).
+ * \brief Translate audio PCM format (Game API to RetroPlayer).
* \param format The audio PCM format to translate.
* \return Translated audio PCM format.
*/
- static AEDataFormat TranslatePCMFormat(GAME_PCM_FORMAT format);
+ static RETRO::PCMFormat TranslatePCMFormat(GAME_PCM_FORMAT format);
/*!
- * \brief Translate audio channels (Game API to AudioEngine).
+ * \brief Translate audio channels (Game API to RetroPlayer).
* \param format The audio channels to translate.
* \return Translated audio channels.
*/
- static AEChannel TranslateAudioChannel(GAME_AUDIO_CHANNEL channel);
+ static RETRO::AudioChannel TranslateAudioChannel(GAME_AUDIO_CHANNEL channel);
/*!
- * \brief Translate audio codec (Game API to FFMPEG).
- * \param format The audio codec to translate.
- * \return Translated audio codec format.
+ * \brief Translate video rotation (Game API to RetroPlayer).
+ * \param rotation The video rotation to translate.
+ * \return Translated video rotation.
*/
- static AVCodecID TranslateAudioCodec(GAME_AUDIO_CODEC codec);
+ static RETRO::VideoRotation TranslateRotation(GAME_VIDEO_ROTATION rotation);
/*!
* \brief Translate key modifiers (Kodi to Game API).
diff --git a/xbmc/games/addons/streams/CMakeLists.txt b/xbmc/games/addons/streams/CMakeLists.txt
new file mode 100644
index 0000000000..bd821a14b7
--- /dev/null
+++ b/xbmc/games/addons/streams/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES GameClientStreamAudio.cpp
+ GameClientStreams.cpp
+ GameClientStreamVideo.cpp
+)
+
+set(HEADERS GameClientStreamAudio.h
+ GameClientStreams.h
+ GameClientStreamVideo.h
+ IGameClientStream.h
+)
+
+core_add_library(game_addon_streams)
diff --git a/xbmc/games/addons/streams/GameClientStreamAudio.cpp b/xbmc/games/addons/streams/GameClientStreamAudio.cpp
new file mode 100644
index 0000000000..d227508ff3
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreamAudio.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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 "GameClientStreamAudio.h"
+#include "cores/RetroPlayer/streams/RetroPlayerAudio.h"
+#include "games/addons/GameClientTranslator.h"
+#include "utils/log.h"
+
+using namespace KODI;
+using namespace GAME;
+
+CGameClientStreamAudio::CGameClientStreamAudio(double sampleRate) :
+ m_sampleRate(sampleRate)
+{
+}
+
+bool CGameClientStreamAudio::OpenStream(RETRO::IRetroPlayerStream* stream, const game_stream_properties& properties)
+{
+ RETRO::CRetroPlayerAudio* audioStream = dynamic_cast<RETRO::CRetroPlayerAudio*>(stream);
+ if (audioStream == nullptr)
+ {
+ CLog::Log(LOGERROR, "GAME: RetroPlayer stream is not an audio stream");
+ return false;
+ }
+
+ std::unique_ptr<RETRO::AudioStreamProperties> audioProperties(TranslateProperties(properties.audio, m_sampleRate));
+ if (audioProperties)
+ {
+ if (audioStream->OpenStream(reinterpret_cast<const RETRO::StreamProperties&>(*audioProperties)))
+ m_stream = stream;
+ }
+
+ return m_stream != nullptr;
+}
+
+void CGameClientStreamAudio::CloseStream()
+{
+ m_stream->CloseStream();
+ m_stream = nullptr;
+}
+
+void CGameClientStreamAudio::AddData(const game_stream_packet &packet)
+{
+ if (packet.type != GAME_STREAM_AUDIO)
+ return;
+
+ if (m_stream != nullptr)
+ {
+ const game_stream_audio_packet &audio = packet.audio;
+
+ RETRO::AudioStreamPacket audioPacket{
+ audio.data,
+ audio.size
+ };
+
+ m_stream->AddStreamData(reinterpret_cast<RETRO::StreamPacket&>(audioPacket));
+ }
+}
+
+RETRO::AudioStreamProperties* CGameClientStreamAudio::TranslateProperties(const game_stream_audio_properties &properties, double sampleRate)
+{
+ const RETRO::PCMFormat pcmFormat = CGameClientTranslator::TranslatePCMFormat(properties.format);
+ if (pcmFormat == RETRO::PCMFormat::FMT_UNKNOWN)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown PCM format: %d", static_cast<int>(properties.format));
+ return nullptr;
+ }
+
+ RETRO::AudioChannelMap channelMap = { { RETRO::AudioChannel::CH_NULL } };
+ unsigned int i = 0;
+ if (properties.channel_map != nullptr)
+ {
+ for (const GAME_AUDIO_CHANNEL* channelPtr = properties.channel_map;
+ *channelPtr != GAME_CH_NULL;
+ channelPtr++)
+ {
+ RETRO::AudioChannel channel = CGameClientTranslator::TranslateAudioChannel(*channelPtr);
+ if (channel == RETRO::AudioChannel::CH_NULL)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown channel ID: %d", static_cast<int>(*channelPtr));
+ return nullptr;
+ }
+
+ channelMap[i++] = channel;
+ if (i + 1 >= channelMap.size())
+ break;
+ }
+ }
+ channelMap[i] = RETRO::AudioChannel::CH_NULL;
+
+ if (channelMap[0] == RETRO::AudioChannel::CH_NULL)
+ {
+ CLog::Log(LOGERROR, "GAME: Empty channel layout");
+ return nullptr;
+ }
+
+ return new RETRO::AudioStreamProperties{
+ pcmFormat,
+ sampleRate,
+ channelMap
+ };
+}
diff --git a/xbmc/games/addons/streams/GameClientStreamAudio.h b/xbmc/games/addons/streams/GameClientStreamAudio.h
new file mode 100644
index 0000000000..0b4725319c
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreamAudio.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 "IGameClientStream.h"
+#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+
+#include <vector>
+
+namespace KODI
+{
+namespace RETRO
+{
+ class IRetroPlayerStream;
+ struct AudioStreamProperties;
+}
+
+namespace GAME
+{
+
+class CGameClientStreamAudio : public IGameClientStream
+{
+public:
+ CGameClientStreamAudio(double sampleRate);
+ ~CGameClientStreamAudio() override = default;
+
+ // Implementation of IGameClientStream
+ bool OpenStream(RETRO::IRetroPlayerStream* stream,
+ const game_stream_properties& properties) override;
+ void CloseStream() override;
+ void AddData(const game_stream_packet &packet) override;
+
+private:
+ // Utility functions
+ static RETRO::AudioStreamProperties* TranslateProperties(const game_stream_audio_properties &properties, double sampleRate);
+
+ // Construction parameters
+ double m_sampleRate;
+
+ // Stream parameters
+ RETRO::IRetroPlayerStream* m_stream;
+};
+
+} // namespace GAME
+} // namespace KODI
diff --git a/xbmc/games/addons/streams/GameClientStreamVideo.cpp b/xbmc/games/addons/streams/GameClientStreamVideo.cpp
new file mode 100644
index 0000000000..7b829c378f
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreamVideo.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 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 "GameClientStreamVideo.h"
+#include "cores/RetroPlayer/streams/RetroPlayerVideo.h"
+#include "games/addons/GameClientTranslator.h"
+#include "utils/log.h"
+
+using namespace KODI;
+using namespace GAME;
+
+bool CGameClientStreamVideo::OpenStream(RETRO::IRetroPlayerStream* stream, const game_stream_properties& properties)
+{
+ RETRO::CRetroPlayerVideo* videoStream = dynamic_cast<RETRO::CRetroPlayerVideo*>(stream);
+ if (videoStream == nullptr)
+ {
+ CLog::Log(LOGERROR, "GAME: RetroPlayer stream is not a video stream");
+ return false;
+ }
+
+ std::unique_ptr<RETRO::VideoStreamProperties> videoProperties(TranslateProperties(properties.video));
+ if (videoProperties)
+ {
+ if (videoStream->OpenStream(reinterpret_cast<const RETRO::StreamProperties&>(*videoProperties)))
+ m_stream = stream;
+ }
+
+ return m_stream != nullptr;
+}
+
+void CGameClientStreamVideo::CloseStream()
+{
+ m_stream->CloseStream();
+ m_stream = nullptr;
+}
+
+void CGameClientStreamVideo::AddData(const game_stream_packet& packet)
+{
+ if (packet.type != GAME_STREAM_VIDEO)
+ return;
+
+ if (m_stream != nullptr)
+ {
+ const game_stream_video_packet& video = packet.video;
+
+ RETRO::VideoRotation rotation = CGameClientTranslator::TranslateRotation(video.rotation);
+
+ RETRO::VideoStreamPacket videoPacket{
+ video.width,
+ video.height,
+ rotation,
+ video.data,
+ video.size,
+ };
+
+ m_stream->AddStreamData(reinterpret_cast<const RETRO::StreamPacket&>(videoPacket));
+ }
+}
+
+RETRO::VideoStreamProperties* CGameClientStreamVideo::TranslateProperties(const game_stream_video_properties &properties)
+{
+ const AVPixelFormat pixelFormat = CGameClientTranslator::TranslatePixelFormat(properties.format);
+ if (pixelFormat == AV_PIX_FMT_NONE)
+ {
+ CLog::Log(LOGERROR, "GAME: Unknown pixel format: %d", properties.format);
+ return nullptr;
+ }
+
+ const unsigned int nominalWidth = properties.nominal_width;
+ const unsigned int nominalHeight = properties.nominal_height;
+ if (nominalWidth == 0 || nominalHeight == 0)
+ {
+ CLog::Log(LOGERROR, "GAME: Invalid nominal dimensions: %ux%u", nominalWidth, nominalHeight);
+ return nullptr;
+ }
+
+ const unsigned int maxWidth = properties.max_width;
+ const unsigned int maxHeight = properties.max_height;
+ if (maxWidth == 0 || maxHeight == 0)
+ {
+ CLog::Log(LOGERROR, "GAME: Invalid max dimensions: %ux%u", maxWidth, maxHeight);
+ return nullptr;
+ }
+
+ if (nominalWidth > maxWidth || nominalHeight > maxHeight)
+ CLog::Log(LOGERROR, "GAME: Nominal dimensions (%ux%u) bigger than max dimensions (%ux%u)", nominalWidth, nominalHeight, maxWidth, maxHeight);
+
+ float pixelAspectRatio;
+
+ // Game API: If aspect_ratio is <= 0.0, an aspect ratio of
+ // (nominal_width / nominal_height) is assumed
+ if (properties.aspect_ratio <= 0.0)
+ pixelAspectRatio = 1.0f;
+ else
+ pixelAspectRatio = properties.aspect_ratio * nominalHeight / nominalWidth;
+
+ return new RETRO::VideoStreamProperties{
+ pixelFormat,
+ nominalWidth,
+ nominalHeight,
+ maxWidth,
+ maxHeight,
+ pixelAspectRatio
+ };
+}
diff --git a/xbmc/games/addons/streams/GameClientStreamVideo.h b/xbmc/games/addons/streams/GameClientStreamVideo.h
new file mode 100644
index 0000000000..a5aa4b7e1a
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreamVideo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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 "IGameClientStream.h"
+
+struct game_stream_video_properties;
+
+namespace KODI
+{
+namespace RETRO
+{
+ class IRetroPlayerStream;
+ struct VideoStreamProperties;
+}
+
+namespace GAME
+{
+
+class CGameClientStreamVideo : public IGameClientStream
+{
+public:
+ CGameClientStreamVideo() = default;
+ ~CGameClientStreamVideo() override = default;
+
+ // Implementation of IGameClientStream
+ bool OpenStream(RETRO::IRetroPlayerStream* stream,
+ const game_stream_properties& properties) override;
+ void CloseStream() override;
+ void AddData(const game_stream_packet& packet) override;
+
+private:
+ // Utility functions
+ static RETRO::VideoStreamProperties* TranslateProperties(const game_stream_video_properties &properties);
+
+ // Stream parameters
+ RETRO::IRetroPlayerStream* m_stream;
+};
+
+} // namespace GAME
+} // namespace KODI
diff --git a/xbmc/games/addons/streams/GameClientStreams.cpp b/xbmc/games/addons/streams/GameClientStreams.cpp
new file mode 100644
index 0000000000..915e579b8b
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreams.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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 "GameClientStreams.h"
+#include "GameClientStreamAudio.h"
+#include "GameClientStreamVideo.h"
+#include "cores/RetroPlayer/streams/IRetroPlayerStream.h"
+#include "cores/RetroPlayer/streams/IStreamManager.h"
+#include "cores/RetroPlayer/streams/RetroPlayerStreamTypes.h"
+#include "games/addons/GameClient.h"
+#include "games/addons/GameClientTranslator.h"
+#include "utils/log.h"
+
+#include <memory>
+
+using namespace KODI;
+using namespace GAME;
+
+CGameClientStreams::CGameClientStreams(CGameClient &gameClient) :
+ m_gameClient(gameClient)
+{
+}
+
+void CGameClientStreams::Initialize(RETRO::IStreamManager& streamManager)
+{
+ m_streamManager = &streamManager;
+}
+
+void CGameClientStreams::Deinitialize()
+{
+ m_streamManager = nullptr;
+}
+
+IGameClientStream *CGameClientStreams::OpenStream(const game_stream_properties &properties)
+{
+ if (m_streamManager == nullptr)
+ return nullptr;
+
+ RETRO::StreamType retroStreamType;
+ if (!CGameClientTranslator::TranslateStreamType(properties.type, retroStreamType))
+ {
+ CLog::Log(LOGERROR, "GAME: Invalid stream type: %d", static_cast<int>(properties.type));
+ return nullptr;
+ }
+
+ std::unique_ptr<IGameClientStream> gameStream = CreateStream(properties.type);
+ if (!gameStream)
+ {
+ CLog::Log(LOGERROR, "GAME: No stream implementation for type: %d", static_cast<int>(properties.type));
+ return nullptr;
+ }
+
+ RETRO::StreamPtr retroStream = m_streamManager->CreateStream(retroStreamType);
+ if (!retroStream)
+ {
+ CLog::Log(LOGERROR, "GAME: Invalid RetroPlayer stream type: %$d", static_cast<int>(retroStreamType));
+ return nullptr;
+ }
+
+ if (!gameStream->OpenStream(retroStream.get(), properties))
+ {
+ CLog::Log(LOGERROR, "GAME: Failed to open audio stream");
+ return nullptr;
+ }
+
+ m_streams[gameStream.get()] = std::move(retroStream);
+
+ return gameStream.release();
+}
+
+void CGameClientStreams::CloseStream(IGameClientStream *stream)
+{
+ if (stream != nullptr)
+ {
+ std::unique_ptr<IGameClientStream> streamHolder(stream);
+ m_streamManager->CloseStream(std::move(m_streams[stream]));
+ m_streams.erase(stream);
+ }
+}
+
+std::unique_ptr<IGameClientStream> CGameClientStreams::CreateStream(GAME_STREAM_TYPE streamType) const
+{
+ std::unique_ptr<IGameClientStream> gameStream;
+
+ switch (streamType)
+ {
+ case GAME_STREAM_AUDIO:
+ {
+ gameStream.reset(new CGameClientStreamAudio(m_gameClient.GetSampleRate()));
+ break;
+ }
+ case GAME_STREAM_VIDEO:
+ {
+ gameStream.reset(new CGameClientStreamVideo);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return gameStream;
+}
diff --git a/xbmc/games/addons/streams/GameClientStreams.h b/xbmc/games/addons/streams/GameClientStreams.h
new file mode 100644
index 0000000000..b0a8f819f1
--- /dev/null
+++ b/xbmc/games/addons/streams/GameClientStreams.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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 "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
+#include "cores/RetroPlayer/streams/RetroPlayerStreamTypes.h"
+
+#include <map>
+
+namespace KODI
+{
+namespace RETRO
+{
+ class IStreamManager;
+}
+
+namespace GAME
+{
+
+class CGameClient;
+class IGameClientStream;
+
+class CGameClientStreams
+{
+public:
+ CGameClientStreams(CGameClient &gameClient);
+
+ void Initialize(RETRO::IStreamManager& streamManager);
+ void Deinitialize();
+
+ IGameClientStream* OpenStream(const game_stream_properties &properties);
+ void CloseStream(IGameClientStream* stream);
+
+private:
+ // Utility functions
+ std::unique_ptr<IGameClientStream> CreateStream(GAME_STREAM_TYPE streamType) const;
+
+ // Construction parameters
+ CGameClient& m_gameClient;
+
+ // Initialization parameters
+ RETRO::IStreamManager* m_streamManager = nullptr;
+
+ // Stream parameters
+ std::map<IGameClientStream*, RETRO::StreamPtr> m_streams;
+};
+
+} // namespace GAME
+} // namespace KODI
diff --git a/xbmc/games/addons/streams/IGameClientStream.h b/xbmc/games/addons/streams/IGameClientStream.h
new file mode 100644
index 0000000000..1f5bc6771c
--- /dev/null
+++ b/xbmc/games/addons/streams/IGameClientStream.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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
+
+struct game_stream_buffer;
+struct game_stream_packet;
+struct game_stream_properties;
+
+namespace KODI
+{
+namespace RETRO
+{
+ class IRetroPlayerStream;
+}
+
+namespace GAME
+{
+
+class IGameClientStream
+{
+public:
+ virtual ~IGameClientStream() = default;
+
+ /*!
+ * \brief Open the stream
+ *
+ * \param stream The RetroPlayer resource to take ownership of
+ *
+ * \return True if the stream was opened, false otherwise
+ */
+ virtual bool OpenStream(RETRO::IRetroPlayerStream* stream,
+ const game_stream_properties& properties) = 0;
+
+ /*!
+ * \brief Release the RetroPlayer stream resource
+ */
+ virtual void CloseStream() = 0;
+
+ /*!
+ * \brief Get a buffer for zero-copy stream data
+ *
+ * \param width The framebuffer width, or 0 for no width specified
+ * \param height The framebuffer height, or 0 for no height specified
+ * \param[out] buffer The buffer, or unmodified if false is returned
+ *
+ * If this returns true, buffer must be freed using ReleaseBuffer().
+ *
+ * \return True if buffer was set, false otherwise
+ */
+ virtual bool GetBuffer(unsigned int width, unsigned int height, game_stream_buffer& buffer) { return false; }
+
+ /*!
+ * \brief Free an allocated buffer
+ *
+ * \param buffer The buffer returned from GetStreamBuffer()
+ */
+ virtual void ReleaseBuffer(game_stream_buffer& buffer) { }
+
+ /*!
+ * \brief Add a data packet to a stream
+ *
+ * \param packet The data packet
+ */
+ virtual void AddData(const game_stream_packet& packet) = 0;
+};
+
+} // namespace GAME
+} // namespace KODI