diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 0cd21445..b1037653 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -39,7 +39,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = { nullptr, nullptr, Session::clLogMessage, - Session::clRumble + Session::clRumble, + Session::clConnectionStatusUpdate }; AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = { @@ -114,6 +115,21 @@ void Session::clRumble(unsigned short controllerNumber, unsigned short lowFreqMo SDL_AtomicUnlock(&s_ActiveSession->m_InputHandlerLock); } +void Session::clConnectionStatusUpdate(int connectionStatus) +{ + switch (connectionStatus) + { + case CONN_STATUS_POOR: + strcpy(s_ActiveSession->m_OverlayManager.getOverlayText(Overlay::OverlayStatusUpdate), "Poor network connection"); + s_ActiveSession->m_OverlayManager.setOverlayTextUpdated(Overlay::OverlayStatusUpdate); + s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, true); + break; + case CONN_STATUS_OKAY: + s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, false); + break; + } +} + #define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing) bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, diff --git a/app/streaming/session.h b/app/streaming/session.h index d710f696..99eaa9ab 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -101,6 +101,9 @@ private: static void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor); + static + void clConnectionStatusUpdate(int connectionStatus); + static int arInit(int audioConfiguration, const POPUS_MULTISTREAM_CONFIGURATION opusConfig, diff --git a/app/streaming/video/ffmpeg-renderers/dxva2.cpp b/app/streaming/video/ffmpeg-renderers/dxva2.cpp index 8d75ddfc..74233a9f 100644 --- a/app/streaming/video/ffmpeg-renderers/dxva2.cpp +++ b/app/streaming/video/ffmpeg-renderers/dxva2.cpp @@ -28,6 +28,7 @@ DXVA2Renderer::DXVA2Renderer() : m_Processor(nullptr), m_FrameIndex(0), m_DebugOverlayFont(nullptr), + m_StatusOverlayFont(nullptr), m_BlockingPresent(false) { RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces)); @@ -48,6 +49,7 @@ DXVA2Renderer::~DXVA2Renderer() SAFE_COM_RELEASE(m_ProcService); SAFE_COM_RELEASE(m_Processor); SAFE_COM_RELEASE(m_DebugOverlayFont); + SAFE_COM_RELEASE(m_StatusOverlayFont); for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) { SAFE_COM_RELEASE(m_DecSurfaces[i]); @@ -707,30 +709,61 @@ IFFmpegRenderer::FramePacingConstraint DXVA2Renderer::getFramePacingConstraint() return PACING_ANY; } -void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType) +void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type) { HRESULT hr; - // Initialize the overlay font if it's not already created - if (m_DebugOverlayFont == nullptr) { - hr = D3DXCreateFontA(m_Device, - Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug), - 0, - FW_HEAVY, - 1, - false, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - DEFAULT_QUALITY, - DEFAULT_PITCH | FF_DONTCARE, - "", - &m_DebugOverlayFont); - if (FAILED(hr)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "D3DXCreateFontA() failed: %x", - hr); - m_DebugOverlayFont = nullptr; + switch (type) + { + case Overlay::OverlayDebug: + if (m_DebugOverlayFont == nullptr) { + hr = D3DXCreateFontA(m_Device, + Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug), + 0, + FW_HEAVY, + 1, + false, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, + "", + &m_DebugOverlayFont); + if (FAILED(hr)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "D3DXCreateFontA() failed: %x", + hr); + m_DebugOverlayFont = nullptr; + } } + break; + + case Overlay::OverlayStatusUpdate: + if (m_StatusOverlayFont == nullptr) { + hr = D3DXCreateFontA(m_Device, + Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayNotification), + 0, + FW_HEAVY, + 1, + false, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, + "", + &m_StatusOverlayFont); + if (FAILED(hr)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "D3DXCreateFontA() failed: %x", + hr); + m_StatusOverlayFont = nullptr; + } + } + break; + + default: + SDL_assert(false); + break; } } @@ -929,6 +962,18 @@ void DXVA2Renderer::renderFrame(AVFrame *frame) } } + if (m_StatusOverlayFont != nullptr) { + if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayStatusUpdate)) { + SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayStatusUpdate); + m_StatusOverlayFont->DrawTextA(nullptr, + Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayStatusUpdate), + -1, + &sample.DstRect, + DT_RIGHT | DT_NOCLIP, + D3DCOLOR_ARGB(color.a, color.r, color.g, color.b)); + } + } + hr = m_Device->EndScene(); if (FAILED(hr)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, diff --git a/app/streaming/video/ffmpeg-renderers/dxva2.h b/app/streaming/video/ffmpeg-renderers/dxva2.h index be9ec628..810557a1 100644 --- a/app/streaming/video/ffmpeg-renderers/dxva2.h +++ b/app/streaming/video/ffmpeg-renderers/dxva2.h @@ -71,5 +71,6 @@ private: DXVA2_VideoDesc m_Desc; REFERENCE_TIME m_FrameIndex; LPD3DXFONT m_DebugOverlayFont; + LPD3DXFONT m_StatusOverlayFont; bool m_BlockingPresent; }; diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp index 298a2b75..458f1104 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp @@ -9,10 +9,7 @@ SdlRenderer::SdlRenderer() : m_Renderer(nullptr), - m_Texture(nullptr), - m_DebugOverlayFont(nullptr), - m_DebugOverlaySurface(nullptr), - m_DebugOverlayTexture(nullptr) + m_Texture(nullptr) { SDL_assert(TTF_WasInit() == 0); if (TTF_Init() != 0) { @@ -21,23 +18,33 @@ SdlRenderer::SdlRenderer() TTF_GetError()); return; } + + SDL_zero(m_OverlayFonts); + SDL_zero(m_OverlaySurfaces); + SDL_zero(m_OverlayTextures); } SdlRenderer::~SdlRenderer() { - if (m_DebugOverlayFont != nullptr) { - TTF_CloseFont(m_DebugOverlayFont); + 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); - if (m_DebugOverlayTexture != nullptr) { - SDL_DestroyTexture(m_DebugOverlayTexture); + for (int i = 0; i < Overlay::OverlayMax; i++) { + if (m_OverlayTextures[i] != nullptr) { + SDL_DestroyTexture(m_OverlayTextures[i]); + } } - if (m_DebugOverlaySurface != nullptr) { - SDL_FreeSurface(m_DebugOverlaySurface); + for (int i = 0; i < Overlay::OverlayMax; i++) { + if (m_OverlaySurfaces[i] != nullptr) { + SDL_FreeSurface(m_OverlaySurfaces[i]); + } } if (m_Texture != nullptr) { @@ -79,45 +86,41 @@ IFFmpegRenderer::FramePacingConstraint SdlRenderer::getFramePacingConstraint() void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type) { - if (type == Overlay::OverlayDebug) { - if (m_DebugOverlayFont == nullptr) { - QByteArray fontPath = QDir::toNativeSeparators(Path::getDataFilePath("ModeSeven.ttf")).toUtf8(); - if (fontPath.isEmpty()) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Unable to locate SDL overlay font"); - return; - } - - m_DebugOverlayFont = TTF_OpenFont(fontPath.data(), - Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug)); - if (m_DebugOverlayFont == nullptr) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "TTF_OpenFont() failed: %s", - TTF_GetError()); - - // Can't proceed without a font - return; - } + // Construct the required font to render the overlay + if (m_OverlayFonts[type] == nullptr) { + QByteArray fontPath = QDir::toNativeSeparators(Path::getDataFilePath("ModeSeven.ttf")).toUtf8(); + if (fontPath.isEmpty()) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Unable to locate SDL overlay font"); + return; } - SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface); + m_OverlayFonts[type] = TTF_OpenFont(fontPath.data(), + Session::get()->getOverlayManager().getOverlayFontSize(type)); + if (m_OverlayFonts[type] == nullptr) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "TTF_OpenFont() failed: %s", + TTF_GetError()); - // 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); + // Can't proceed without a font + return; } } - else { - SDL_assert(false); + + 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); } } @@ -180,6 +183,45 @@ bool SdlRenderer::initialize(SDL_Window* window, return true; } +void SdlRenderer::renderOverlay(Overlay::OverlayType type) +{ + if (Session::get()->getOverlayManager().isOverlayEnabled(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)) { + if (m_OverlayTextures[type] != nullptr) { + SDL_DestroyTexture(m_OverlayTextures[type]); + } + + if (type == Overlay::OverlayStatusUpdate) { + // Top right + int unused; + SDL_RenderGetLogicalSize(m_Renderer, &m_OverlayRects[type].x, &unused); + m_OverlayRects[type].x -= surface->w; + m_OverlayRects[type].y = 0; + } + else if (type == Overlay::OverlayDebug) { + // Top left + m_OverlayRects[type].x = 0; + m_OverlayRects[type].y = 0; + } + + m_OverlayRects[type].w = surface->w; + m_OverlayRects[type].h = surface->h; + + m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface); + SDL_FreeSurface(surface); + } + + // If we have an overlay texture, render it too + if (m_OverlayTextures[type] != nullptr) { + SDL_RenderCopy(m_Renderer, m_OverlayTextures[type], nullptr, &m_OverlayRects[type]); + } + } +} + void SdlRenderer::renderFrame(AVFrame* frame) { SDL_UpdateYUVTexture(m_Texture, nullptr, @@ -196,29 +238,8 @@ void SdlRenderer::renderFrame(AVFrame* frame) 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); - } + for (int i = 0; i < Overlay::OverlayMax; i++) { + renderOverlay((Overlay::OverlayType)i); } SDL_RenderPresent(m_Renderer); diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.h b/app/streaming/video/ffmpeg-renderers/sdlvid.h index 11667e2e..4818f7d3 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.h +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.h @@ -22,11 +22,13 @@ public: virtual void notifyOverlayUpdated(Overlay::OverlayType) override; private: + void renderOverlay(Overlay::OverlayType type); + SDL_Renderer* m_Renderer; SDL_Texture* m_Texture; - TTF_Font* m_DebugOverlayFont; - SDL_Surface* m_DebugOverlaySurface; - SDL_Texture* m_DebugOverlayTexture; - SDL_Rect m_DebugOverlayRect; + 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 d24d1f6a..9e29d770 100644 --- a/app/streaming/video/overlaymanager.cpp +++ b/app/streaming/video/overlaymanager.cpp @@ -9,6 +9,9 @@ OverlayManager::OverlayManager() : m_Overlays[OverlayType::OverlayDebug].color = {0xD0, 0xD0, 0x00, 0xFF}; m_Overlays[OverlayType::OverlayDebug].fontSize = 20; + + m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF}; + m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36; } bool OverlayManager::isOverlayEnabled(OverlayType type) diff --git a/app/streaming/video/overlaymanager.h b/app/streaming/video/overlaymanager.h index 808491be..4758a414 100644 --- a/app/streaming/video/overlaymanager.h +++ b/app/streaming/video/overlaymanager.h @@ -8,8 +8,8 @@ namespace Overlay { enum OverlayType { OverlayDebug, - OverlayMinorNotification, - OverlayMajorNotification, + OverlayStatusUpdate, + OverlayNotification, OverlayMax }; diff --git a/moonlight-common-c/moonlight-common-c b/moonlight-common-c/moonlight-common-c index 6eb17a8e..c9745855 160000 --- a/moonlight-common-c/moonlight-common-c +++ b/moonlight-common-c/moonlight-common-c @@ -1 +1 @@ -Subproject commit 6eb17a8e73349f24309d86f81812dc8869bd54c1 +Subproject commit c9745855fd3d0eb2f5c4c4546a30c849b7453a7c