summaryrefslogtreecommitdiffstats
path: root/xbmc/cores/RetroPlayer/streams
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/cores/RetroPlayer/streams')
-rw-r--r--xbmc/cores/RetroPlayer/streams/CMakeLists.txt15
-rw-r--r--xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h67
-rw-r--r--xbmc/cores/RetroPlayer/streams/IStreamManager.h41
-rw-r--r--xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp65
-rw-r--r--xbmc/cores/RetroPlayer/streams/RPStreamManager.h42
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp142
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h67
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp19
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h86
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp116
-rw-r--r--xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h112
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.cpp55
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.h48
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/CMakeLists.txt12
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.cpp99
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.h67
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/IMemoryStream.h144
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.cpp104
-rw-r--r--xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.h69
19 files changed, 1370 insertions, 0 deletions
diff --git a/xbmc/cores/RetroPlayer/streams/CMakeLists.txt b/xbmc/cores/RetroPlayer/streams/CMakeLists.txt
new file mode 100644
index 0000000..b5acbc9
--- /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 0000000..dddf3ff
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/IRetroPlayerStream.h
@@ -0,0 +1,67 @@
+/*
+ * 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 "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;
+};
+
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/IStreamManager.h b/xbmc/cores/RetroPlayer/streams/IStreamManager.h
new file mode 100644
index 0000000..b490642
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/IStreamManager.h
@@ -0,0 +1,41 @@
+/*
+ * 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 "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;
+};
+
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp b/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp
new file mode 100644
index 0000000..2682d4d
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "RPStreamManager.h"
+
+#include "IRetroPlayerStream.h"
+#include "RetroPlayerAudio.h"
+#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:
+ case StreamType::SW_BUFFER:
+ {
+ return StreamPtr(new CRetroPlayerVideo(m_renderManager, m_processInfo));
+ }
+ 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 0000000..3615329
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RPStreamManager.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "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;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp
new file mode 100644
index 0000000..d4e5a05
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012-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 "RetroPlayerAudio.h"
+
+#include "ServiceBroker.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 "utils/log.h"
+
+#include <cmath>
+
+using namespace KODI;
+using namespace RETRO;
+
+const double MAX_DELAY = 0.3; // seconds
+
+CRetroPlayerAudio::CRetroPlayerAudio(CRPProcessInfo& processInfo)
+ : m_processInfo(processInfo), m_pAudioStream(nullptr), m_bAudioEnabled(true)
+{
+ CLog::Log(LOGDEBUG, "RetroPlayer[AUDIO]: Initializing audio");
+}
+
+CRetroPlayerAudio::~CRetroPlayerAudio()
+{
+ CLog::Log(LOGDEBUG, "RetroPlayer[AUDIO]: Deinitializing audio");
+
+ CloseStream();
+}
+
+bool CRetroPlayerAudio::OpenStream(const StreamProperties& properties)
+{
+ const AudioStreamProperties& audioProperties =
+ static_cast<const AudioStreamProperties&>(properties);
+
+ const AEDataFormat pcmFormat = CAudioTranslator::TranslatePCMFormat(audioProperties.format);
+ if (pcmFormat == AE_FMT_INVALID)
+ {
+ CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Unknown PCM format: {}",
+ 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();
+
+ IAE* audioEngine = CServiceBroker::GetActiveAE();
+ if (audioEngine == nullptr)
+ return false;
+
+ CLog::Log(
+ LOGINFO,
+ "RetroPlayer[AUDIO]: Creating audio stream, format = {}, sample rate = {}, channels = {}",
+ CAEUtil::DataFormatToStr(pcmFormat), iSampleRate, channelLayout.Count());
+
+ AEAudioFormat audioFormat;
+ audioFormat.m_dataFormat = pcmFormat;
+ audioFormat.m_sampleRate = iSampleRate;
+ audioFormat.m_channelLayout = channelLayout;
+ m_pAudioStream = audioEngine->MakeStream(audioFormat);
+
+ if (m_pAudioStream == nullptr)
+ {
+ CLog::Log(LOGERROR, "RetroPlayer[AUDIO]: Failed to create audio stream");
+ return false;
+ }
+
+ m_processInfo.SetAudioChannels(audioFormat.m_channelLayout);
+ m_processInfo.SetAudioSampleRate(audioFormat.m_sampleRate);
+ m_processInfo.SetAudioBitsPerSample(CAEUtil::DataFormatToUsedBits(audioFormat.m_dataFormat));
+
+ return true;
+}
+
+void CRetroPlayerAudio::AddStreamData(const StreamPacket& packet)
+{
+ const AudioStreamPacket& audioPacket = static_cast<const AudioStreamPacket&>(packet);
+
+ if (m_bAudioEnabled)
+ {
+ if (m_pAudioStream)
+ {
+ const double delaySecs = m_pAudioStream->GetDelay();
+
+ const size_t frameSize = m_pAudioStream->GetChannelCount() *
+ (CAEUtil::DataFormatToBits(m_pAudioStream->GetDataFormat()) >> 3);
+
+ const unsigned int frameCount = static_cast<unsigned int>(audioPacket.size / frameSize);
+
+ if (delaySecs > MAX_DELAY)
+ {
+ m_pAudioStream->Flush();
+ CLog::Log(LOGDEBUG, "RetroPlayer[AUDIO]: Audio delay ({:0.2f} ms) is too high - flushing",
+ delaySecs * 1000);
+ }
+
+ m_pAudioStream->AddData(&audioPacket.data, 0, frameCount, nullptr);
+ }
+ }
+}
+
+void CRetroPlayerAudio::CloseStream()
+{
+ if (m_pAudioStream)
+ {
+ CLog::Log(LOGDEBUG, "RetroPlayer[AUDIO]: Closing audio stream");
+
+ m_pAudioStream.reset();
+ }
+}
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h
new file mode 100644
index 0000000..184fd54
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerAudio.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012-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 "IRetroPlayerStream.h"
+#include "cores/AudioEngine/Interfaces/AE.h"
+
+#include <memory>
+
+class IAEStream;
+
+namespace KODI
+{
+namespace RETRO
+{
+class CRPProcessInfo;
+
+struct AudioStreamProperties : public StreamProperties
+{
+ AudioStreamProperties(PCMFormat format, double sampleRate, AudioChannelMap channelMap)
+ : format(format), sampleRate(sampleRate), channelMap(channelMap)
+ {
+ }
+
+ PCMFormat format;
+ double sampleRate;
+ AudioChannelMap channelMap;
+};
+
+struct AudioStreamPacket : public StreamPacket
+{
+ AudioStreamPacket(const uint8_t* data, size_t size) : data(data), size(size) {}
+
+ const uint8_t* data;
+ size_t size;
+};
+
+class CRetroPlayerAudio : public IRetroPlayerStream
+{
+public:
+ explicit CRetroPlayerAudio(CRPProcessInfo& processInfo);
+ ~CRetroPlayerAudio() 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;
+ IAE::StreamPtr m_pAudioStream;
+ bool m_bAudioEnabled;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp
new file mode 100644
index 0000000..7f7d0de
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 "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 0000000..aa5686a
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerStreamTypes.h
@@ -0,0 +1,86 @@
+/*
+ * 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 <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,
+};
+
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp
new file mode 100644
index 0000000..14c66a8
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012-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 "RetroPlayerVideo.h"
+
+#include "cores/RetroPlayer/process/RPProcessInfo.h"
+#include "cores/RetroPlayer/rendering/RPRenderManager.h"
+#include "cores/RetroPlayer/rendering/RenderTranslator.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 =
+ static_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;
+
+ CLog::Log(LOGDEBUG,
+ "RetroPlayer[VIDEO]: Creating video stream - format {}, nominal {}x{}, max {}x{}",
+ 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,
+ pixelAspectRatio))
+ m_bOpen = true;
+
+ return m_bOpen;
+}
+
+bool CRetroPlayerVideo::GetStreamBuffer(unsigned int width,
+ unsigned int height,
+ StreamBuffer& buffer)
+{
+ VideoStreamBuffer& videoBuffer = static_cast<VideoStreamBuffer&>(buffer);
+
+ if (m_bOpen)
+ return m_renderManager.GetVideoBuffer(width, height, videoBuffer);
+
+ return false;
+}
+
+void CRetroPlayerVideo::AddStreamData(const StreamPacket& packet)
+{
+ const VideoStreamPacket& videoPacket = static_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/streams/RetroPlayerVideo.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h
new file mode 100644
index 0000000..8d153ab
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012-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 "IRetroPlayerStream.h"
+#include "cores/RetroPlayer/RetroPlayerTypes.h"
+
+extern "C"
+{
+#include <libavutil/pixfmt.h>
+}
+
+namespace KODI
+{
+namespace RETRO
+{
+class CRPProcessInfo;
+class CRPRenderManager;
+
+struct VideoStreamProperties : public StreamProperties
+{
+ VideoStreamProperties(AVPixelFormat pixfmt,
+ unsigned int nominalWidth,
+ unsigned int nominalHeight,
+ unsigned int maxWidth,
+ unsigned int maxHeight,
+ float pixelAspectRatio)
+ : pixfmt(pixfmt),
+ nominalWidth(nominalWidth),
+ nominalHeight(nominalHeight),
+ maxWidth(maxWidth),
+ maxHeight(maxHeight),
+ pixelAspectRatio(pixelAspectRatio)
+ {
+ }
+
+ AVPixelFormat pixfmt;
+ unsigned int nominalWidth;
+ unsigned int nominalHeight;
+ unsigned int maxWidth;
+ unsigned int maxHeight;
+ float pixelAspectRatio;
+};
+
+struct VideoStreamBuffer : public StreamBuffer
+{
+ VideoStreamBuffer() = default;
+
+ VideoStreamBuffer(
+ AVPixelFormat pixfmt, uint8_t* data, size_t size, DataAccess access, DataAlignment alignment)
+ : pixfmt(pixfmt), data(data), size(size), access(access), alignment(alignment)
+ {
+ }
+
+ AVPixelFormat pixfmt{AV_PIX_FMT_NONE};
+ uint8_t* data{nullptr};
+ size_t size{0};
+ DataAccess access{DataAccess::READ_WRITE};
+ DataAlignment alignment{DataAlignment::DATA_UNALIGNED};
+};
+
+struct VideoStreamPacket : public StreamPacket
+{
+ VideoStreamPacket(unsigned int width,
+ unsigned int height,
+ VideoRotation rotation,
+ const uint8_t* data,
+ size_t size)
+ : width(width), height(height), rotation(rotation), data(data), size(size)
+ {
+ }
+
+ 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 IRetroPlayerStream
+{
+public:
+ CRetroPlayerVideo(CRPRenderManager& m_renderManager, CRPProcessInfo& m_processInfo);
+ ~CRetroPlayerVideo() override;
+
+ // implementation of IRetroPlayerStream
+ bool OpenStream(const StreamProperties& properties) override;
+ bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) override;
+ void AddStreamData(const StreamPacket& packet) override;
+ void CloseStream() override;
+
+private:
+ // Construction parameters
+ CRPRenderManager& m_renderManager;
+ CRPProcessInfo& m_processInfo;
+
+ // Stream properties
+ bool m_bOpen = false;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.cpp b/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.cpp
new file mode 100644
index 0000000..6d52d3f
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016-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 "BasicMemoryStream.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+CBasicMemoryStream::CBasicMemoryStream()
+{
+ Reset();
+}
+
+void CBasicMemoryStream::Init(size_t frameSize, uint64_t maxFrameCount)
+{
+ Reset();
+
+ m_frameSize = frameSize;
+}
+
+void CBasicMemoryStream::Reset()
+{
+ m_frameSize = 0;
+ m_frameBuffer.reset();
+ m_bHasFrame = false;
+}
+
+uint8_t* CBasicMemoryStream::BeginFrame()
+{
+ if (m_frameSize == 0)
+ return nullptr;
+
+ if (!m_frameBuffer)
+ m_frameBuffer.reset(new uint8_t[m_frameSize]);
+
+ m_bHasFrame = false;
+
+ return m_frameBuffer.get();
+}
+
+void CBasicMemoryStream::SubmitFrame()
+{
+ if (m_frameBuffer)
+ m_bHasFrame = true;
+}
+
+const uint8_t* CBasicMemoryStream::CurrentFrame() const
+{
+ return m_bHasFrame ? m_frameBuffer.get() : nullptr;
+}
diff --git a/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.h b/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.h
new file mode 100644
index 0000000..c3cc716
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/BasicMemoryStream.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016-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 "IMemoryStream.h"
+
+#include <memory>
+
+namespace KODI
+{
+namespace RETRO
+{
+class CBasicMemoryStream : public IMemoryStream
+{
+public:
+ CBasicMemoryStream();
+
+ ~CBasicMemoryStream() override = default;
+
+ // implementation of IMemoryStream
+ void Init(size_t frameSize, uint64_t maxFrameCount) override;
+ void Reset() override;
+ size_t FrameSize() const override { return m_frameSize; }
+ uint64_t MaxFrameCount() const override { return 1; }
+ void SetMaxFrameCount(uint64_t maxFrameCount) override {}
+ uint8_t* BeginFrame() override;
+ void SubmitFrame() override;
+ const uint8_t* CurrentFrame() const override;
+ uint64_t FutureFramesAvailable() const override { return 0; }
+ uint64_t AdvanceFrames(uint64_t frameCount) override { return 0; }
+ uint64_t PastFramesAvailable() const override { return 0; }
+ uint64_t RewindFrames(uint64_t frameCount) override { return 0; }
+ uint64_t GetFrameCounter() const override { return 0; }
+ void SetFrameCounter(uint64_t frameCount) override{};
+
+private:
+ size_t m_frameSize;
+ std::unique_ptr<uint8_t[]> m_frameBuffer;
+ bool m_bHasFrame;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/memory/CMakeLists.txt b/xbmc/cores/RetroPlayer/streams/memory/CMakeLists.txt
new file mode 100644
index 0000000..7297bc6
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES BasicMemoryStream.cpp
+ DeltaPairMemoryStream.cpp
+ LinearMemoryStream.cpp
+)
+
+set(HEADERS BasicMemoryStream.h
+ DeltaPairMemoryStream.h
+ IMemoryStream.h
+ LinearMemoryStream.h
+)
+
+core_add_library(retroplayer_memory)
diff --git a/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.cpp b/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.cpp
new file mode 100644
index 0000000..eae65cd
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016-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 "DeltaPairMemoryStream.h"
+
+#include "utils/log.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+void CDeltaPairMemoryStream::Reset()
+{
+ CLinearMemoryStream::Reset();
+
+ m_rewindBuffer.clear();
+}
+
+void CDeltaPairMemoryStream::SubmitFrameInternal()
+{
+ m_rewindBuffer.emplace_back();
+ MemoryFrame& frame = m_rewindBuffer.back();
+
+ // Record frame history
+ frame.frameHistoryCount = m_currentFrameHistory++;
+
+ uint32_t* currentFrame = m_currentFrame.get();
+ uint32_t* nextFrame = m_nextFrame.get();
+
+ for (size_t i = 0; i < m_paddedFrameSize; i++)
+ {
+ uint32_t xor_val = currentFrame[i] ^ nextFrame[i];
+ if (xor_val)
+ {
+ DeltaPair pair = {i, xor_val};
+ frame.buffer.push_back(pair);
+ }
+ }
+
+ // Delta is generated, bring the new frame forward (m_nextFrame is now disposable)
+ std::swap(m_currentFrame, m_nextFrame);
+
+ m_bHasNextFrame = false;
+
+ if (PastFramesAvailable() + 1 > MaxFrameCount())
+ CullPastFrames(1);
+}
+
+uint64_t CDeltaPairMemoryStream::PastFramesAvailable() const
+{
+ return static_cast<uint64_t>(m_rewindBuffer.size());
+}
+
+uint64_t CDeltaPairMemoryStream::RewindFrames(uint64_t frameCount)
+{
+ uint64_t rewound;
+
+ for (rewound = 0; rewound < frameCount; rewound++)
+ {
+ if (m_rewindBuffer.empty())
+ break;
+
+ const MemoryFrame& frame = m_rewindBuffer.back();
+ const DeltaPair* buffer = frame.buffer.data();
+
+ size_t bufferSize = frame.buffer.size();
+
+ // buffer pointer redirection violates data-dependency requirements...
+ // no vectorization for us :(
+ for (size_t i = 0; i < bufferSize; i++)
+ m_currentFrame[buffer[i].pos] ^= buffer[i].delta;
+
+ // Restore frame history
+ m_currentFrameHistory = frame.frameHistoryCount;
+
+ m_rewindBuffer.pop_back();
+ }
+
+ return rewound;
+}
+
+void CDeltaPairMemoryStream::CullPastFrames(uint64_t frameCount)
+{
+ for (uint64_t removedCount = 0; removedCount < frameCount; removedCount++)
+ {
+ if (m_rewindBuffer.empty())
+ {
+ CLog::Log(LOGDEBUG,
+ "CDeltaPairMemoryStream: Tried to cull {} frames too many. Check your math!",
+ frameCount - removedCount);
+ break;
+ }
+ m_rewindBuffer.pop_front();
+ }
+}
diff --git a/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.h b/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.h
new file mode 100644
index 0000000..b2a7cc8
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/DeltaPairMemoryStream.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016-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 "LinearMemoryStream.h"
+
+#include <deque>
+#include <vector>
+
+namespace KODI
+{
+namespace RETRO
+{
+/*!
+ * \brief Implementation of a linear memory stream using XOR deltas
+ */
+class CDeltaPairMemoryStream : public CLinearMemoryStream
+{
+public:
+ CDeltaPairMemoryStream() = default;
+
+ ~CDeltaPairMemoryStream() override = default;
+
+ // implementation of IMemoryStream via CLinearMemoryStream
+ void Reset() override;
+ uint64_t PastFramesAvailable() const override;
+ uint64_t RewindFrames(uint64_t frameCount) override;
+
+protected:
+ // implementation of CLinearMemoryStream
+ void SubmitFrameInternal() override;
+ void CullPastFrames(uint64_t frameCount) override;
+
+ /*!
+ * Rewinding is implemented by applying XOR deltas on the specific parts of
+ * the save state buffer which have changed. In practice, this is very fast
+ * and simple (linear scan) and allows deltas to be compressed down to 1-3%
+ * of original save state size depending on the system. The algorithm runs
+ * on 32 bits at a time for speed.
+ *
+ * Use std::deque here to achieve amortized O(1) on pop/push to front and
+ * back.
+ */
+ struct DeltaPair
+ {
+ size_t pos;
+ uint32_t delta;
+ };
+
+ using DeltaPairVector = std::vector<DeltaPair>;
+
+ struct MemoryFrame
+ {
+ DeltaPairVector buffer;
+ uint64_t frameHistoryCount;
+ };
+
+ std::deque<MemoryFrame> m_rewindBuffer;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/memory/IMemoryStream.h b/xbmc/cores/RetroPlayer/streams/memory/IMemoryStream.h
new file mode 100644
index 0000000..379a6a4
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/IMemoryStream.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016-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 <stddef.h>
+#include <stdint.h>
+
+namespace KODI
+{
+namespace RETRO
+{
+/*!
+ * \brief Stream of serialized states from game clients
+ *
+ * A memory stream is composed of "frames" of memory representing serialized
+ * states of the game client. For each video frame run by the game loop, the
+ * game client's state is serialized into a buffer provided by this interface.
+ *
+ * Implementation of three types of memory streams are provided:
+ *
+ * - Basic memory stream: has only a current frame, and supports neither
+ * rewind nor forward seeking.
+ *
+ * \sa CBasicMemoryStream
+ *
+ * - Linear memory stream: can grow in one direction. It is possible to
+ * rewind, but not fast-forward.
+ *
+ * \sa CLinearMemoryStream
+ *
+ * - Nonlinear memory stream: can have frames both ahead of and behind
+ * the current frame. If a stream is rewound, it is possible to
+ * recover these frames by seeking forward again.
+ *
+ * \sa CNonlinearMemoryStream (TODO)
+ */
+class IMemoryStream
+{
+public:
+ virtual ~IMemoryStream() = default;
+
+ /*!
+ * \brief Initialize memory stream
+ *
+ * \param frameSize The size of the serialized memory state
+ * \param maxFrameCount The maximum number of frames this stream can hold
+ */
+ virtual void Init(size_t frameSize, uint64_t maxFrameCount) = 0;
+
+ /*!
+ * \brief Free any resources used by this stream
+ */
+ virtual void Reset() = 0;
+
+ /*!
+ * \brief Return the frame size passed to Init()
+ */
+ virtual size_t FrameSize() const = 0;
+
+ /*!
+ * \brief Return the current max frame count
+ */
+ virtual uint64_t MaxFrameCount() const = 0;
+
+ /*!
+ * \brief Update the max frame count
+ *
+ * Old frames may be deleted if the max frame count is reduced.
+ */
+ virtual void SetMaxFrameCount(uint64_t maxFrameCount) = 0;
+
+ /*!
+ * \ brief Get a pointer to which FrameSize() bytes can be written
+ *
+ * The buffer exposed by this function is passed to the game client, which
+ * fills it with a serialization of its current state.
+ */
+ virtual uint8_t* BeginFrame() = 0;
+
+ /*!
+ * \brief Indicate that a frame of size FrameSize() has been written to the
+ * location returned from BeginFrame()
+ */
+ virtual void SubmitFrame() = 0;
+
+ /*!
+ * \brief Get a pointer to the current frame
+ *
+ * This function must have no side effects. The pointer is valid until the
+ * stream is modified.
+ *
+ * \return A buffer of size FrameSize(), or nullptr if the stream is empty
+ */
+ virtual const uint8_t* CurrentFrame() const = 0;
+
+ /*!
+ * \brief Return the number of frames ahead of the current frame
+ *
+ * If the stream supports forward seeking, frames that are passed over
+ * during a "rewind" operation can be recovered again.
+ */
+ virtual uint64_t FutureFramesAvailable() const = 0;
+
+ /*!
+ * \brief Seek ahead the specified number of frames
+ *
+ * \return The number of frames advanced
+ */
+ virtual uint64_t AdvanceFrames(uint64_t frameCount) = 0;
+
+ /*!
+ * \brief Return the number of frames behind the current frame
+ */
+ virtual uint64_t PastFramesAvailable() const = 0;
+
+ /*!
+ * \brief Seek backwards the specified number of frames
+ *
+ * \return The number of frames rewound
+ */
+ virtual uint64_t RewindFrames(uint64_t frameCount) = 0;
+
+ /*!
+ * \brief Get the total number of frames played until the current frame
+ *
+ * \return The history of the current frame, or 0 for unknown
+ */
+ virtual uint64_t GetFrameCounter() const = 0;
+
+ /*!
+ * \brief Set the total number of frames played until the current frame
+ *
+ * \param frameCount The history of the current frame
+ */
+ virtual void SetFrameCounter(uint64_t frameCount) = 0;
+};
+} // namespace RETRO
+} // namespace KODI
diff --git a/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.cpp b/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.cpp
new file mode 100644
index 0000000..65a7959
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016-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 "LinearMemoryStream.h"
+
+using namespace KODI;
+using namespace RETRO;
+
+// Pad forward to nearest boundary of bytes
+#define PAD_TO_CEIL(x, bytes) ((((x) + (bytes)-1) / (bytes)) * (bytes))
+
+CLinearMemoryStream::CLinearMemoryStream()
+{
+ Reset();
+}
+
+void CLinearMemoryStream::Init(size_t frameSize, uint64_t maxFrameCount)
+{
+ Reset();
+
+ m_frameSize = frameSize;
+ m_paddedFrameSize = PAD_TO_CEIL(m_frameSize, sizeof(uint32_t));
+ m_maxFrames = maxFrameCount;
+}
+
+void CLinearMemoryStream::Reset()
+{
+ m_frameSize = 0;
+ m_paddedFrameSize = 0;
+ m_maxFrames = 0;
+ m_currentFrame.reset();
+ m_nextFrame.reset();
+ m_bHasCurrentFrame = false;
+ m_bHasNextFrame = false;
+ m_currentFrameHistory = 0;
+}
+
+void CLinearMemoryStream::SetMaxFrameCount(uint64_t maxFrameCount)
+{
+ if (maxFrameCount == 0)
+ {
+ Reset();
+ }
+ else
+ {
+ const uint64_t frameCount = BufferSize();
+ if (maxFrameCount < frameCount)
+ CullPastFrames(frameCount - maxFrameCount);
+ }
+
+ m_maxFrames = maxFrameCount;
+}
+
+uint8_t* CLinearMemoryStream::BeginFrame()
+{
+ if (m_paddedFrameSize == 0)
+ return nullptr;
+
+ if (!m_bHasCurrentFrame)
+ {
+ if (!m_currentFrame)
+ m_currentFrame.reset(new uint32_t[m_paddedFrameSize]);
+ return reinterpret_cast<uint8_t*>(m_currentFrame.get());
+ }
+
+ if (!m_nextFrame)
+ m_nextFrame.reset(new uint32_t[m_paddedFrameSize]);
+ return reinterpret_cast<uint8_t*>(m_nextFrame.get());
+}
+
+const uint8_t* CLinearMemoryStream::CurrentFrame() const
+{
+ if (m_bHasCurrentFrame)
+ return reinterpret_cast<const uint8_t*>(m_currentFrame.get());
+
+ return nullptr;
+}
+
+void CLinearMemoryStream::SubmitFrame()
+{
+ if (!m_bHasCurrentFrame)
+ {
+ m_bHasCurrentFrame = true;
+ }
+ else if (!m_bHasNextFrame)
+ {
+ m_bHasNextFrame = true;
+ }
+
+ if (m_bHasNextFrame)
+ {
+ SubmitFrameInternal();
+ }
+}
+
+uint64_t CLinearMemoryStream::BufferSize() const
+{
+ return PastFramesAvailable() + (m_bHasCurrentFrame ? 1 : 0);
+}
diff --git a/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.h b/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.h
new file mode 100644
index 0000000..f7b9465
--- /dev/null
+++ b/xbmc/cores/RetroPlayer/streams/memory/LinearMemoryStream.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016-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 "IMemoryStream.h"
+
+#include <memory>
+#include <stdint.h>
+
+namespace KODI
+{
+namespace RETRO
+{
+class CLinearMemoryStream : public IMemoryStream
+{
+public:
+ CLinearMemoryStream();
+
+ ~CLinearMemoryStream() override = default;
+
+ // partial implementation of IMemoryStream
+ void Init(size_t frameSize, uint64_t maxFrameCount) override;
+ void Reset() override;
+ size_t FrameSize() const override { return m_frameSize; }
+ uint64_t MaxFrameCount() const override { return m_maxFrames; }
+ void SetMaxFrameCount(uint64_t maxFrameCount) override;
+ uint8_t* BeginFrame() override;
+ void SubmitFrame() override;
+ const uint8_t* CurrentFrame() const override;
+ uint64_t FutureFramesAvailable() const override { return 0; }
+ uint64_t AdvanceFrames(uint64_t frameCount) override { return 0; }
+ uint64_t PastFramesAvailable() const override = 0;
+ uint64_t RewindFrames(uint64_t frameCount) override = 0;
+ uint64_t GetFrameCounter() const override { return m_currentFrameHistory; }
+ void SetFrameCounter(uint64_t frameCount) override { m_currentFrameHistory = frameCount; }
+
+protected:
+ virtual void SubmitFrameInternal() = 0;
+ virtual void CullPastFrames(uint64_t frameCount) = 0;
+
+ // Helper function
+ uint64_t BufferSize() const;
+
+ size_t m_paddedFrameSize;
+ uint64_t m_maxFrames;
+
+ /**
+ * Simple double-buffering. After XORing the two states, the next becomes
+ * the current, and the current becomes a buffer for the next call to
+ * CGameClient::Serialize().
+ */
+ std::unique_ptr<uint32_t[]> m_currentFrame;
+ std::unique_ptr<uint32_t[]> m_nextFrame;
+ bool m_bHasCurrentFrame;
+ bool m_bHasNextFrame;
+
+ uint64_t m_currentFrameHistory;
+
+private:
+ size_t m_frameSize;
+};
+} // namespace RETRO
+} // namespace KODI