mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-10 13:44:17 +00:00
Add connection status overlay for DXVA2 and SDL
This commit is contained in:
parent
9a6f5ba1a8
commit
c75b9c9221
9 changed files with 186 additions and 95 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -71,5 +71,6 @@ private:
|
|||
DXVA2_VideoDesc m_Desc;
|
||||
REFERENCE_TIME m_FrameIndex;
|
||||
LPD3DXFONT m_DebugOverlayFont;
|
||||
LPD3DXFONT m_StatusOverlayFont;
|
||||
bool m_BlockingPresent;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace Overlay {
|
|||
|
||||
enum OverlayType {
|
||||
OverlayDebug,
|
||||
OverlayMinorNotification,
|
||||
OverlayMajorNotification,
|
||||
OverlayStatusUpdate,
|
||||
OverlayNotification,
|
||||
OverlayMax
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 6eb17a8e73349f24309d86f81812dc8869bd54c1
|
||||
Subproject commit c9745855fd3d0eb2f5c4c4546a30c849b7453a7c
|
Loading…
Reference in a new issue