Refactor SDL overlay drawing into OverlayManager for sharing with other renderers

This commit is contained in:
Cameron Gutman 2020-12-13 15:51:02 -06:00
parent 19d5358b97
commit d58837421f
4 changed files with 102 additions and 90 deletions

View File

@ -2,54 +2,25 @@
#include "streaming/session.h" #include "streaming/session.h"
#include "streaming/streamutils.h" #include "streaming/streamutils.h"
#include "path.h"
#include <QDir>
#include <Limelight.h> #include <Limelight.h>
SdlRenderer::SdlRenderer() SdlRenderer::SdlRenderer()
: m_Renderer(nullptr), : m_Renderer(nullptr),
m_Texture(nullptr), m_Texture(nullptr),
m_SwPixelFormat(AV_PIX_FMT_NONE), m_SwPixelFormat(AV_PIX_FMT_NONE)
m_FontData(Path::readDataFile("ModeSeven.ttf"))
{ {
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); SDL_zero(m_OverlayTextures);
} }
SdlRenderer::~SdlRenderer() 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++) { for (int i = 0; i < Overlay::OverlayMax; i++) {
if (m_OverlayTextures[i] != nullptr) { if (m_OverlayTextures[i] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[i]); 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) { if (m_Texture != nullptr) {
SDL_DestroyTexture(m_Texture); SDL_DestroyTexture(m_Texture);
} }
@ -69,47 +40,6 @@ bool SdlRenderer::prepareDecoderContext(AVCodecContext*, AVDictionary**)
return true; 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() bool SdlRenderer::isRenderThreadSupported()
{ {
SDL_RendererInfo info; 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. // 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 // NB: We have to do this conversion at render-time because we can only interact
// with the renderer on a single thread. // with the renderer on a single thread.
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]); SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], surface, nullptr)) { if (newSurface != nullptr) {
if (m_OverlayTextures[type] != nullptr) { if (m_OverlayTextures[type] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[type]); SDL_DestroyTexture(m_OverlayTextures[type]);
} }
@ -232,7 +162,7 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
SDL_Rect viewportRect; SDL_Rect viewportRect;
SDL_RenderGetViewport(m_Renderer, &viewportRect); SDL_RenderGetViewport(m_Renderer, &viewportRect);
m_OverlayRects[type].x = 0; 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) { else if (type == Overlay::OverlayDebug) {
// Top left // Top left
@ -240,11 +170,11 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
m_OverlayRects[type].y = 0; m_OverlayRects[type].y = 0;
} }
m_OverlayRects[type].w = surface->w; m_OverlayRects[type].w = newSurface->w;
m_OverlayRects[type].h = surface->h; m_OverlayRects[type].h = newSurface->h;
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface); m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, newSurface);
SDL_FreeSurface(surface); SDL_FreeSurface(newSurface);
} }
// If we have an overlay texture, render it too // If we have an overlay texture, render it too

View File

@ -2,8 +2,6 @@
#include "renderer.h" #include "renderer.h"
#include <SDL_ttf.h>
class SdlRenderer : public IFFmpegRenderer { class SdlRenderer : public IFFmpegRenderer {
public: public:
SdlRenderer(); SdlRenderer();
@ -11,7 +9,6 @@ public:
virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
virtual void renderFrame(AVFrame* frame) override; virtual void renderFrame(AVFrame* frame) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool isRenderThreadSupported() override; virtual bool isRenderThreadSupported() override;
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
@ -21,9 +18,6 @@ private:
SDL_Renderer* m_Renderer; SDL_Renderer* m_Renderer;
SDL_Texture* m_Texture; SDL_Texture* m_Texture;
int m_SwPixelFormat; 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_Texture* m_OverlayTextures[Overlay::OverlayMax];
SDL_Rect m_OverlayRects[Overlay::OverlayMax]; SDL_Rect m_OverlayRects[Overlay::OverlayMax];
}; };

View File

@ -1,9 +1,11 @@
#include "overlaymanager.h" #include "overlaymanager.h"
#include "path.h"
using namespace Overlay; using namespace Overlay;
OverlayManager::OverlayManager() : OverlayManager::OverlayManager() :
m_Renderer(nullptr) m_Renderer(nullptr),
m_FontData(Path::readDataFile("ModeSeven.ttf"))
{ {
memset(m_Overlays, 0, sizeof(m_Overlays)); 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].color = {0xCC, 0x00, 0x00, 0xFF};
m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36; 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) bool OverlayManager::isOverlayEnabled(OverlayType type)
@ -29,12 +54,19 @@ int OverlayManager::getOverlayFontSize(OverlayType type)
return m_Overlays[type].fontSize; 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) void OverlayManager::setOverlayTextUpdated(OverlayType type)
{ {
// Only update the overlay state if it's enabled. If it's not enabled, // Only update the overlay state if it's enabled. If it's not enabled,
// the renderer has already been notified by setOverlayState(). // the renderer has already been notified by setOverlayState().
if (m_Overlays[type].enabled && m_Renderer != nullptr) { if (m_Overlays[type].enabled) {
m_Renderer->notifyOverlayUpdated(type); notifyOverlayUpdated(type);
} }
} }
@ -50,9 +82,7 @@ void OverlayManager::setOverlayState(OverlayType type, bool enabled)
m_Overlays[type].text[0] = 0; m_Overlays[type].text[0] = 0;
} }
if (m_Renderer != nullptr) { notifyOverlayUpdated(type);
m_Renderer->notifyOverlayUpdated(type);
}
} }
} }
@ -65,3 +95,51 @@ void OverlayManager::setOverlayRenderer(IOverlayRenderer* renderer)
{ {
m_Renderer = 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);
}

View File

@ -3,6 +3,7 @@
#include <QString> #include <QString>
#include <SDL.h> #include <SDL.h>
#include <SDL_ttf.h>
namespace Overlay { namespace Overlay {
@ -24,6 +25,7 @@ class OverlayManager
{ {
public: public:
OverlayManager(); OverlayManager();
~OverlayManager();
bool isOverlayEnabled(OverlayType type); bool isOverlayEnabled(OverlayType type);
char* getOverlayText(OverlayType type); char* getOverlayText(OverlayType type);
@ -31,16 +33,24 @@ public:
void setOverlayState(OverlayType type, bool enabled); void setOverlayState(OverlayType type, bool enabled);
SDL_Color getOverlayColor(OverlayType type); SDL_Color getOverlayColor(OverlayType type);
int getOverlayFontSize(OverlayType type); int getOverlayFontSize(OverlayType type);
SDL_Surface* getUpdatedOverlaySurface(OverlayType type);
void setOverlayRenderer(IOverlayRenderer* renderer); void setOverlayRenderer(IOverlayRenderer* renderer);
private:
void notifyOverlayUpdated(OverlayType type);
struct { struct {
bool enabled; bool enabled;
int fontSize; int fontSize;
SDL_Color color; SDL_Color color;
char text[512]; char text[512];
TTF_Font* font;
SDL_Surface* surface;
} m_Overlays[OverlayMax]; } m_Overlays[OverlayMax];
IOverlayRenderer* m_Renderer; IOverlayRenderer* m_Renderer;
QByteArray m_FontData;
}; };
} }