diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp index ed23b711..294dcca5 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp @@ -2,54 +2,25 @@ #include "streaming/session.h" #include "streaming/streamutils.h" -#include "path.h" - -#include #include SdlRenderer::SdlRenderer() : m_Renderer(nullptr), m_Texture(nullptr), - m_SwPixelFormat(AV_PIX_FMT_NONE), - m_FontData(Path::readDataFile("ModeSeven.ttf")) + m_SwPixelFormat(AV_PIX_FMT_NONE) { - SDL_assert(TTF_WasInit() == 0); - if (TTF_Init() != 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "TTF_Init() failed: %s", - TTF_GetError()); - return; - } - - SDL_zero(m_OverlayFonts); - SDL_zero(m_OverlaySurfaces); SDL_zero(m_OverlayTextures); } SdlRenderer::~SdlRenderer() { - for (int i = 0; i < Overlay::OverlayMax; i++) { - if (m_OverlayFonts[i] != nullptr) { - TTF_CloseFont(m_OverlayFonts[i]); - } - } - - TTF_Quit(); - SDL_assert(TTF_WasInit() == 0); - for (int i = 0; i < Overlay::OverlayMax; i++) { if (m_OverlayTextures[i] != nullptr) { SDL_DestroyTexture(m_OverlayTextures[i]); } } - for (int i = 0; i < Overlay::OverlayMax; i++) { - if (m_OverlaySurfaces[i] != nullptr) { - SDL_FreeSurface(m_OverlaySurfaces[i]); - } - } - if (m_Texture != nullptr) { SDL_DestroyTexture(m_Texture); } @@ -69,47 +40,6 @@ bool SdlRenderer::prepareDecoderContext(AVCodecContext*, AVDictionary**) return true; } -void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type) -{ - // Construct the required font to render the overlay - if (m_OverlayFonts[type] == nullptr) { - if (m_FontData.isEmpty()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "SDL overlay font failed to load"); - return; - } - - // m_FontData must stay around until the font is closed - m_OverlayFonts[type] = TTF_OpenFontRW(SDL_RWFromConstMem(m_FontData.constData(), m_FontData.size()), - 1, - Session::get()->getOverlayManager().getOverlayFontSize(type)); - if (m_OverlayFonts[type] == nullptr) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "TTF_OpenFont() failed: %s", - TTF_GetError()); - - // Can't proceed without a font - return; - } - } - - SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]); - - // Free the old surface - if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], oldSurface, nullptr)) { - SDL_FreeSurface(oldSurface); - } - - if (Session::get()->getOverlayManager().isOverlayEnabled(type)) { - // The _Wrapped variant is required for line breaks to work - SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_OverlayFonts[type], - Session::get()->getOverlayManager().getOverlayText(type), - Session::get()->getOverlayManager().getOverlayColor(type), - 1000); - SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], surface); - } -} - bool SdlRenderer::isRenderThreadSupported() { SDL_RendererInfo info; @@ -221,8 +151,8 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type) // If a new surface has been created for updated overlay data, convert it into a texture. // NB: We have to do this conversion at render-time because we can only interact // with the renderer on a single thread. - SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]); - if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], surface, nullptr)) { + SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type); + if (newSurface != nullptr) { if (m_OverlayTextures[type] != nullptr) { SDL_DestroyTexture(m_OverlayTextures[type]); } @@ -232,7 +162,7 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type) SDL_Rect viewportRect; SDL_RenderGetViewport(m_Renderer, &viewportRect); m_OverlayRects[type].x = 0; - m_OverlayRects[type].y = viewportRect.h - surface->h; + m_OverlayRects[type].y = viewportRect.h - newSurface->h; } else if (type == Overlay::OverlayDebug) { // Top left @@ -240,11 +170,11 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type) m_OverlayRects[type].y = 0; } - m_OverlayRects[type].w = surface->w; - m_OverlayRects[type].h = surface->h; + m_OverlayRects[type].w = newSurface->w; + m_OverlayRects[type].h = newSurface->h; - m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface); - SDL_FreeSurface(surface); + m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, newSurface); + SDL_FreeSurface(newSurface); } // If we have an overlay texture, render it too diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.h b/app/streaming/video/ffmpeg-renderers/sdlvid.h index 35a88d3b..aadeaf2d 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.h +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.h @@ -2,8 +2,6 @@ #include "renderer.h" -#include - class SdlRenderer : public IFFmpegRenderer { public: SdlRenderer(); @@ -11,7 +9,6 @@ public: virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; virtual void renderFrame(AVFrame* frame) override; - virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual bool isRenderThreadSupported() override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; @@ -21,9 +18,6 @@ private: SDL_Renderer* m_Renderer; SDL_Texture* m_Texture; int m_SwPixelFormat; - QByteArray m_FontData; - TTF_Font* m_OverlayFonts[Overlay::OverlayMax]; - SDL_Surface* m_OverlaySurfaces[Overlay::OverlayMax]; SDL_Texture* m_OverlayTextures[Overlay::OverlayMax]; SDL_Rect m_OverlayRects[Overlay::OverlayMax]; }; diff --git a/app/streaming/video/overlaymanager.cpp b/app/streaming/video/overlaymanager.cpp index 9e29d770..810689ec 100644 --- a/app/streaming/video/overlaymanager.cpp +++ b/app/streaming/video/overlaymanager.cpp @@ -1,9 +1,11 @@ #include "overlaymanager.h" +#include "path.h" using namespace Overlay; OverlayManager::OverlayManager() : - m_Renderer(nullptr) + m_Renderer(nullptr), + m_FontData(Path::readDataFile("ModeSeven.ttf")) { memset(m_Overlays, 0, sizeof(m_Overlays)); @@ -12,6 +14,29 @@ OverlayManager::OverlayManager() : m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF}; m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36; + + SDL_assert(TTF_WasInit() == 0); + if (TTF_Init() != 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "TTF_Init() failed: %s", + TTF_GetError()); + return; + } +} + +OverlayManager::~OverlayManager() +{ + for (int i = 0; i < OverlayType::OverlayMax; i++) { + if (m_Overlays[i].surface != nullptr) { + SDL_FreeSurface(m_Overlays[i].surface); + } + if (m_Overlays[i].font != nullptr) { + TTF_CloseFont(m_Overlays[i].font); + } + } + + TTF_Quit(); + SDL_assert(TTF_WasInit() == 0); } bool OverlayManager::isOverlayEnabled(OverlayType type) @@ -29,12 +54,19 @@ int OverlayManager::getOverlayFontSize(OverlayType type) return m_Overlays[type].fontSize; } +SDL_Surface* OverlayManager::getUpdatedOverlaySurface(OverlayType type) +{ + // If a new surface is available, return it. If not, return nullptr. + // Caller must free the surface on success. + return (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, nullptr); +} + void OverlayManager::setOverlayTextUpdated(OverlayType type) { // Only update the overlay state if it's enabled. If it's not enabled, // the renderer has already been notified by setOverlayState(). - if (m_Overlays[type].enabled && m_Renderer != nullptr) { - m_Renderer->notifyOverlayUpdated(type); + if (m_Overlays[type].enabled) { + notifyOverlayUpdated(type); } } @@ -50,9 +82,7 @@ void OverlayManager::setOverlayState(OverlayType type, bool enabled) m_Overlays[type].text[0] = 0; } - if (m_Renderer != nullptr) { - m_Renderer->notifyOverlayUpdated(type); - } + notifyOverlayUpdated(type); } } @@ -65,3 +95,51 @@ void OverlayManager::setOverlayRenderer(IOverlayRenderer* renderer) { m_Renderer = renderer; } + +void OverlayManager::notifyOverlayUpdated(OverlayType type) +{ + if (m_Renderer == nullptr) { + return; + } + + // Construct the required font to render the overlay + if (m_Overlays[type].font == nullptr) { + if (m_FontData.isEmpty()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL overlay font failed to load"); + return; + } + + // m_FontData must stay around until the font is closed + m_Overlays[type].font = TTF_OpenFontRW(SDL_RWFromConstMem(m_FontData.constData(), m_FontData.size()), + 1, + m_Overlays[type].fontSize); + if (m_Overlays[type].font == nullptr) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "TTF_OpenFont() failed: %s", + TTF_GetError()); + + // Can't proceed without a font + return; + } + } + + SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, nullptr); + + // Free the old surface + if (oldSurface != nullptr) { + SDL_FreeSurface(oldSurface); + } + + if (m_Overlays[type].enabled) { + // The _Wrapped variant is required for line breaks to work + SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_Overlays[type].font, + m_Overlays[type].text, + m_Overlays[type].color, + 1000); + SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, surface); + } + + // Notify the renderer + m_Renderer->notifyOverlayUpdated(type); +} diff --git a/app/streaming/video/overlaymanager.h b/app/streaming/video/overlaymanager.h index c55125c3..3c7805ab 100644 --- a/app/streaming/video/overlaymanager.h +++ b/app/streaming/video/overlaymanager.h @@ -3,6 +3,7 @@ #include #include +#include namespace Overlay { @@ -24,6 +25,7 @@ class OverlayManager { public: OverlayManager(); + ~OverlayManager(); bool isOverlayEnabled(OverlayType type); char* getOverlayText(OverlayType type); @@ -31,16 +33,24 @@ public: void setOverlayState(OverlayType type, bool enabled); SDL_Color getOverlayColor(OverlayType type); int getOverlayFontSize(OverlayType type); + SDL_Surface* getUpdatedOverlaySurface(OverlayType type); void setOverlayRenderer(IOverlayRenderer* renderer); +private: + void notifyOverlayUpdated(OverlayType type); + struct { bool enabled; int fontSize; SDL_Color color; char text[512]; + + TTF_Font* font; + SDL_Surface* surface; } m_Overlays[OverlayMax]; IOverlayRenderer* m_Renderer; + QByteArray m_FontData; }; }