diff --git a/app/ModeSeven.ttf b/app/ModeSeven.ttf new file mode 100644 index 00000000..87c9280f Binary files /dev/null and b/app/ModeSeven.ttf differ diff --git a/app/app.pro b/app/app.pro index 0b5b0a09..cabe8424 100644 --- a/app/app.pro +++ b/app/app.pro @@ -91,7 +91,7 @@ unix:!macx { } } win32 { - LIBS += -llibssl -llibcrypto -lSDL2 -lavcodec -lavutil -lopus -ld3dx9 + LIBS += -llibssl -llibcrypto -lSDL2 -lSDL2_ttf -lavcodec -lavutil -lopus -ld3dx9 CONFIG += ffmpeg soundio } macx { @@ -307,7 +307,7 @@ unix:!macx: { appstream.files = deploy/linux/com.moonlight_stream.Moonlight.appdata.xml appstream.path = $$PREFIX/$$DATADIR/metainfo/ - appdata.files = SDL_GameControllerDB/gamecontrollerdb.txt + appdata.files = SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf appdata.path = "$$PREFIX/$$DATADIR/Moonlight Game Streaming Project/Moonlight/" INSTALLS += target appdata desktop icons appstream @@ -328,7 +328,7 @@ macx { QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist - APP_BUNDLE_RESOURCES.files = moonlight.icns SDL_GameControllerDB/gamecontrollerdb.txt + APP_BUNDLE_RESOURCES.files = moonlight.icns SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf APP_BUNDLE_RESOURCES.path = Contents/Resources APP_BUNDLE_FRAMEWORKS.files = $$files(../libs/mac/Frameworks/*.framework, true) diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp index 97fd26e6..83cb8d8b 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp @@ -1,16 +1,49 @@ #include "sdlvid.h" +#include "streaming/session.h" + #include SdlRenderer::SdlRenderer() : m_Renderer(nullptr), - m_Texture(nullptr) + m_Texture(nullptr), + m_DebugOverlayFont(nullptr), + m_DebugOverlaySurface(nullptr), + m_DebugOverlayTexture(nullptr) { + SDL_assert(TTF_WasInit() == 0); + if (TTF_Init() != 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "TTF_Init() failed: %s", + TTF_GetError()); + return; + } + m_DebugOverlayFont = TTF_OpenFont("ModeSeven.ttf", 20); + if (m_DebugOverlayFont == nullptr) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "TTF_OpenFont() failed: %s", + TTF_GetError()); + } } SdlRenderer::~SdlRenderer() { + if (m_DebugOverlayFont != nullptr) { + TTF_CloseFont(m_DebugOverlayFont); + } + + TTF_Quit(); + SDL_assert(TTF_WasInit() == 0); + + if (m_DebugOverlayTexture != nullptr) { + SDL_DestroyTexture(m_DebugOverlayTexture); + } + + if (m_DebugOverlaySurface != nullptr) { + SDL_FreeSurface(m_DebugOverlaySurface); + } + if (m_Texture != nullptr) { SDL_DestroyTexture(m_Texture); } @@ -48,6 +81,35 @@ IFFmpegRenderer::FramePacingConstraint SdlRenderer::getFramePacingConstraint() return PACING_ANY; } +void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type) +{ + if (type == Overlay::OverlayDebug) { + if (m_DebugOverlayFont == nullptr) { + // Can't proceed without a font + return; + } + + SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface); + + // Free the old surface + if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, 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_DebugOverlayFont, + Session::get()->getOverlayManager().getOverlayText(type), + Session::get()->getOverlayManager().getOverlayColor(type), + 1000); + SDL_AtomicSetPtr((void**)&m_DebugOverlaySurface, surface); + } + } + else { + SDL_assert(false); + } +} + bool SdlRenderer::initialize(SDL_Window* window, int, int width, @@ -109,6 +171,35 @@ void SdlRenderer::renderFrameAtVsync(AVFrame* frame) frame->linesize[2]); SDL_RenderClear(m_Renderer); + + // Draw the video content itself SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr); + + // Draw the overlays + if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)) { + // 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_DebugOverlaySurface); + if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, surface, nullptr)) { + if (m_DebugOverlayTexture != nullptr) { + SDL_DestroyTexture(m_DebugOverlayTexture); + } + + m_DebugOverlayRect.x = 0; + m_DebugOverlayRect.y = 0; + m_DebugOverlayRect.w = surface->w; + m_DebugOverlayRect.h = surface->h; + + m_DebugOverlayTexture = SDL_CreateTextureFromSurface(m_Renderer, surface); + SDL_FreeSurface(surface); + } + + // If we have a debug overlay texture, render it too + if (m_DebugOverlayTexture != nullptr) { + SDL_RenderCopy(m_Renderer, m_DebugOverlayTexture, nullptr, &m_DebugOverlayRect); + } + } + SDL_RenderPresent(m_Renderer); } diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.h b/app/streaming/video/ffmpeg-renderers/sdlvid.h index 40d58ac4..2275ef1a 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.h +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.h @@ -2,24 +2,31 @@ #include "renderer.h" +#include + class SdlRenderer : public IFFmpegRenderer { public: SdlRenderer(); - virtual ~SdlRenderer(); + virtual ~SdlRenderer() override; virtual bool initialize(SDL_Window* window, int videoFormat, int width, int height, int maxFps, - bool enableVsync); - virtual bool prepareDecoderContext(AVCodecContext* context); - virtual void renderFrameAtVsync(AVFrame* frame); - virtual bool needsTestFrame(); - virtual int getDecoderCapabilities(); - virtual FramePacingConstraint getFramePacingConstraint(); + bool enableVsync) override; + virtual bool prepareDecoderContext(AVCodecContext* context) override; + virtual void renderFrameAtVsync(AVFrame* frame) override; + virtual bool needsTestFrame() override; + virtual int getDecoderCapabilities() override; + virtual FramePacingConstraint getFramePacingConstraint() override; + virtual void notifyOverlayUpdated(Overlay::OverlayType) override; private: SDL_Renderer* m_Renderer; SDL_Texture* m_Texture; + TTF_Font* m_DebugOverlayFont; + SDL_Surface* m_DebugOverlaySurface; + SDL_Texture* m_DebugOverlayTexture; + SDL_Rect m_DebugOverlayRect; }; diff --git a/scripts/generate-installers.bat b/scripts/generate-installers.bat index a14793f9..a6ef5205 100644 --- a/scripts/generate-installers.bat +++ b/scripts/generate-installers.bat @@ -123,6 +123,10 @@ echo Copying GC mapping list copy %SOURCE_ROOT%\app\SDL_GameControllerDB\gamecontrollerdb.txt %DEPLOY_FOLDER% if !ERRORLEVEL! NEQ 0 goto Error +echo Copying SDL overlay font +copy %SOURCE_ROOT%\app\ModeSeven.ttf %DEPLOY_FOLDER% +if !ERRORLEVEL! NEQ 0 goto Error + echo Deploying Qt dependencies windeployqt.exe --dir %DEPLOY_FOLDER% --%BUILD_CONFIG% --qmldir %SOURCE_ROOT%\app\gui --no-opengl-sw --no-compiler-runtime %BUILD_FOLDER%\app\%BUILD_CONFIG%\Moonlight.exe if !ERRORLEVEL! NEQ 0 goto Error