diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/games/addons/streams | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/games/addons/streams')
-rw-r--r-- | xbmc/games/addons/streams/CMakeLists.txt | 14 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamAudio.cpp | 105 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamAudio.h | 52 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamSwFramebuffer.cpp | 40 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h | 29 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamVideo.cpp | 108 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreamVideo.h | 49 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreams.cpp | 118 | ||||
-rw-r--r-- | xbmc/games/addons/streams/GameClientStreams.h | 55 | ||||
-rw-r--r-- | xbmc/games/addons/streams/IGameClientStream.h | 77 |
10 files changed, 647 insertions, 0 deletions
diff --git a/xbmc/games/addons/streams/CMakeLists.txt b/xbmc/games/addons/streams/CMakeLists.txt new file mode 100644 index 0000000..f8de0e6 --- /dev/null +++ b/xbmc/games/addons/streams/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SOURCES GameClientStreamAudio.cpp + GameClientStreams.cpp + GameClientStreamSwFramebuffer.cpp + GameClientStreamVideo.cpp +) + +set(HEADERS GameClientStreamAudio.h + GameClientStreams.h + GameClientStreamSwFramebuffer.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 0000000..a38f054 --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamAudio.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#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(static_cast<const RETRO::StreamProperties&>(*audioProperties))) + m_stream = stream; + } + + return m_stream != nullptr; +} + +void CGameClientStreamAudio::CloseStream() +{ + if (m_stream != nullptr) + { + 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(static_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: {}", 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: {}", 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 0000000..d48b0a6 --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamAudio.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "IGameClientStream.h" +#include "addons/kodi-dev-kit/include/kodi/addon-instance/Game.h" + +#include <vector> + +namespace KODI +{ +namespace RETRO +{ +class IRetroPlayerStream; +struct AudioStreamProperties; +} // namespace RETRO + +namespace GAME +{ + +class CGameClientStreamAudio : public IGameClientStream +{ +public: + CGameClientStreamAudio(double sampleRate); + ~CGameClientStreamAudio() override { CloseStream(); } + + // 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 + const double m_sampleRate; + + // Stream parameters + RETRO::IRetroPlayerStream* m_stream = nullptr; +}; + +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.cpp b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.cpp new file mode 100644 index 0000000..4d1e8af --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GameClientStreamSwFramebuffer.h" + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/Game.h" +#include "cores/RetroPlayer/streams/RetroPlayerVideo.h" +#include "games/addons/GameClientTranslator.h" + +using namespace KODI; +using namespace GAME; + +bool CGameClientStreamSwFramebuffer::GetBuffer(unsigned int width, + unsigned int height, + game_stream_buffer& buffer) +{ + if (m_stream != nullptr) + { + RETRO::VideoStreamBuffer streamBuffer; + if (m_stream->GetStreamBuffer(width, height, static_cast<RETRO::StreamBuffer&>(streamBuffer))) + { + buffer.type = GAME_STREAM_SW_FRAMEBUFFER; + + game_stream_sw_framebuffer_buffer& framebuffer = buffer.sw_framebuffer; + + framebuffer.format = CGameClientTranslator::TranslatePixelFormat(streamBuffer.pixfmt); + framebuffer.data = streamBuffer.data; + framebuffer.size = streamBuffer.size; + + return true; + } + } + + return false; +} diff --git a/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h new file mode 100644 index 0000000..2b17dac --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "GameClientStreamVideo.h" + +namespace KODI +{ +namespace GAME +{ + +class CGameClientStreamSwFramebuffer : public CGameClientStreamVideo +{ +public: + CGameClientStreamSwFramebuffer() = default; + ~CGameClientStreamSwFramebuffer() override = default; + + // Implementation of IGameClientStream via CGameClientStreamVideo + bool GetBuffer(unsigned int width, unsigned int height, game_stream_buffer& buffer) override; +}; + +} // 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 0000000..d110b28 --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamVideo.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#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(static_cast<const RETRO::StreamProperties&>(*videoProperties))) + m_stream = stream; + } + + return m_stream != nullptr; +} + +void CGameClientStreamVideo::CloseStream() +{ + if (m_stream != nullptr) + { + m_stream->CloseStream(); + m_stream = nullptr; + } +} + +void CGameClientStreamVideo::AddData(const game_stream_packet& packet) +{ + if (packet.type != GAME_STREAM_VIDEO && packet.type != GAME_STREAM_SW_FRAMEBUFFER) + 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(static_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: {}", 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: {}x{}", 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: {}x{}", maxWidth, maxHeight); + return nullptr; + } + + if (nominalWidth > maxWidth || nominalHeight > maxHeight) + CLog::Log(LOGERROR, "GAME: Nominal dimensions ({}x{}) bigger than max dimensions ({}x{})", + 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.0f) + 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 0000000..4c90c2c --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreamVideo.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "IGameClientStream.h" + +struct game_stream_video_properties; + +namespace KODI +{ +namespace RETRO +{ +class IRetroPlayerStream; +struct VideoStreamProperties; +} // namespace RETRO + +namespace GAME +{ + +class CGameClientStreamVideo : public IGameClientStream +{ +public: + CGameClientStreamVideo() = default; + ~CGameClientStreamVideo() override { CloseStream(); } + + // 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; + +protected: + // Stream parameters + RETRO::IRetroPlayerStream* m_stream = nullptr; + +private: + // Utility functions + static RETRO::VideoStreamProperties* TranslateProperties( + const game_stream_video_properties& properties); +}; + +} // 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 0000000..7d005ea --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreams.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GameClientStreams.h" + +#include "GameClientStreamAudio.h" +#include "GameClientStreamSwFramebuffer.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: {}", 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: {}", + static_cast<int>(properties.type)); + return nullptr; + } + + RETRO::StreamPtr retroStream = m_streamManager->CreateStream(retroStreamType); + if (!retroStream) + { + CLog::Log(LOGERROR, "GAME: Invalid RetroPlayer stream type: {}", + 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); + streamHolder->CloseStream(); + + 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; + } + case GAME_STREAM_SW_FRAMEBUFFER: + { + gameStream.reset(new CGameClientStreamSwFramebuffer); + 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 0000000..6f3f639 --- /dev/null +++ b/xbmc/games/addons/streams/GameClientStreams.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/Game.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 0000000..c2374bc --- /dev/null +++ b/xbmc/games/addons/streams/IGameClientStream.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#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 GetBuffer() + */ + 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 |