diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 970e0e42..305e6a3e 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -1802,24 +1802,6 @@ void Session::execInternal() FillRect(info.info.win.hdc, &clientRect, blackBrush); DeleteObject(blackBrush); } -#else - if (strcmp(SDL_GetCurrentVideoDriver(), "KMSDRM") == 0) { - // Create a dummy renderer to force SDL to complete the modesetting - // operation that the KMSDRM backend keeps pending until the next - // time we swap buffers. - SDL_Renderer* renderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_SOFTWARE); - if (renderer != nullptr) { - SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - SDL_DestroyRenderer(renderer); - } - else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "SDL_CreateRenderer() for KMSDRM modesetting failed: %s", - SDL_GetError()); - } - } #endif } diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index a3281db5..59016ad9 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -47,6 +47,7 @@ extern "C" { #include #include "streaming/streamutils.h" +#include "streaming/session.h" #include @@ -178,6 +179,43 @@ bool DrmRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary** return true; } +void DrmRenderer::prepareToRender() +{ + // Create a dummy renderer to force SDL to complete the modesetting + // operation that the KMSDRM backend keeps pending until the next + // time we swap buffers. We have to do this before we enumerate + // CRTC modes below. + SDL_Renderer* renderer = SDL_CreateRenderer(params->window, -1, SDL_RENDERER_SOFTWARE); + if (renderer != nullptr) { + // SDL_CreateRenderer() can end up having to recreate our window (SDL_RecreateWindow()) + // to ensure it's compatible with the renderer's OpenGL context. If that happens, we + // can get spurious SDL_WINDOWEVENT events that will cause us to (again) recreate our + // renderer. This can lead to an infinite to renderer recreation, so discard all + // SDL_WINDOWEVENT events after SDL_CreateRenderer(). + Session* session = Session::get(); + if (session != nullptr) { + // If we get here during a session, we need to synchronize with the event loop + // to ensure we don't drop any important events. + session->flushWindowEvents(); + } + else { + // If we get here prior to the start of a session, just pump and flush ourselves. + SDL_PumpEvents(); + SDL_FlushEvent(SDL_WINDOWEVENT); + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + SDL_DestroyRenderer(renderer); + } + else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_CreateRenderer() failed: %s", + SDL_GetError()); + } +} + bool DrmRenderer::getPropertyByName(drmModeObjectPropertiesPtr props, const char* name, uint64_t *value) { for (uint32_t j = 0; j < props->count_props; j++) { drmModePropertyPtr prop = drmModeGetProperty(m_DrmFd, props->props[j]); diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index 57d9ec40..7cb4852d 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -52,6 +52,7 @@ public: virtual ~DrmRenderer() override; virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; + virtual void prepareToRender() override; virtual void renderFrame(AVFrame* frame) override; virtual enum AVPixelFormat getPreferredPixelFormat(int videoFormat) override; virtual bool isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) override; diff --git a/app/streaming/video/ffmpeg-renderers/eglvid.cpp b/app/streaming/video/ffmpeg-renderers/eglvid.cpp index 91f9f523..9e9fae8b 100644 --- a/app/streaming/video/ffmpeg-renderers/eglvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/eglvid.cpp @@ -691,13 +691,6 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params) SDL_GL_SetSwapInterval(0); } - if (!params->testOnly) { - // Draw a black frame until the video stream starts rendering - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - SDL_GL_SwapWindow(params->window); - } - glGenTextures(EGL_MAX_PLANES, m_Textures); for (size_t i = 0; i < EGL_MAX_PLANES; ++i) { glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_Textures[i]); @@ -877,6 +870,18 @@ void EGLRenderer::waitToRender() } } +void EGLRenderer::prepareToRender() +{ + SDL_GL_MakeCurrent(m_Window, m_Context); + { + // Draw a black frame until the video stream starts rendering + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + SDL_GL_SwapWindow(m_Window); + } + SDL_GL_MakeCurrent(m_Window, nullptr); +} + void EGLRenderer::renderFrame(AVFrame* frame) { EGLImage imgs[EGL_MAX_PLANES]; diff --git a/app/streaming/video/ffmpeg-renderers/eglvid.h b/app/streaming/video/ffmpeg-renderers/eglvid.h index ec7b994f..9dfbb4cd 100644 --- a/app/streaming/video/ffmpeg-renderers/eglvid.h +++ b/app/streaming/video/ffmpeg-renderers/eglvid.h @@ -14,6 +14,7 @@ public: virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; virtual void cleanupRenderContext() override; virtual void waitToRender() override; + virtual void prepareToRender() override; virtual void renderFrame(AVFrame* frame) override; virtual bool testRenderFrame(AVFrame* frame) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override; diff --git a/app/streaming/video/ffmpeg-renderers/mmal.cpp b/app/streaming/video/ffmpeg-renderers/mmal.cpp index bd733fbe..228d8f47 100644 --- a/app/streaming/video/ffmpeg-renderers/mmal.cpp +++ b/app/streaming/video/ffmpeg-renderers/mmal.cpp @@ -59,6 +59,40 @@ bool MmalRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary** return true; } +void MmalRenderer::prepareToRender() +{ + // Create a renderer and draw a black background for the area not covered by the MMAL overlay. + // On the KMSDRM backend, this triggers the modeset that puts the CRTC into the mode we selected. + m_BackgroundRenderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_SOFTWARE); + if (m_BackgroundRenderer == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_CreateRenderer() failed: %s", + SDL_GetError()); + return; + } + + // SDL_CreateRenderer() can end up having to recreate our window (SDL_RecreateWindow()) + // to ensure it's compatible with the renderer's OpenGL context. If that happens, we + // can get spurious SDL_WINDOWEVENT events that will cause us to (again) recreate our + // renderer. This can lead to an infinite to renderer recreation, so discard all + // SDL_WINDOWEVENT events after SDL_CreateRenderer(). + Session* session = Session::get(); + if (session != nullptr) { + // If we get here during a session, we need to synchronize with the event loop + // to ensure we don't drop any important events. + session->flushWindowEvents(); + } + else { + // If we get here prior to the start of a session, just pump and flush ourselves. + SDL_PumpEvents(); + SDL_FlushEvent(SDL_WINDOWEVENT); + } + + SDL_SetRenderDrawColor(m_BackgroundRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(m_BackgroundRenderer); + SDL_RenderPresent(m_BackgroundRenderer); +} + void MmalRenderer::updateDisplayRegion() { MMAL_STATUS_T status; @@ -124,9 +158,6 @@ bool MmalRenderer::initialize(PDECODER_PARAMETERS params) m_VideoWidth = params->width; m_VideoHeight = params->height; - // Clear the background if possible - setupBackground(params); - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &m_Renderer); if (status != MMAL_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, @@ -217,42 +248,6 @@ int MmalRenderer::getDecoderColorspace() return COLORSPACE_REC_709; } -void MmalRenderer::setupBackground(PDECODER_PARAMETERS params) -{ - if (!params->testOnly) { - // Create a renderer and draw a black background for the area not covered by the MMAL overlay. - // On the KMSDRM backend, this triggers the modeset that puts the CRTC into the mode we selected. - m_BackgroundRenderer = SDL_CreateRenderer(params->window, -1, SDL_RENDERER_SOFTWARE); - if (m_BackgroundRenderer == nullptr) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "SDL_CreateRenderer() failed: %s", - SDL_GetError()); - return; - } - - // SDL_CreateRenderer() can end up having to recreate our window (SDL_RecreateWindow()) - // to ensure it's compatible with the renderer's OpenGL context. If that happens, we - // can get spurious SDL_WINDOWEVENT events that will cause us to (again) recreate our - // renderer. This can lead to an infinite to renderer recreation, so discard all - // SDL_WINDOWEVENT events after SDL_CreateRenderer(). - Session* session = Session::get(); - if (session != nullptr) { - // If we get here during a session, we need to synchronize with the event loop - // to ensure we don't drop any important events. - session->flushWindowEvents(); - } - else { - // If we get here prior to the start of a session, just pump and flush ourselves. - SDL_PumpEvents(); - SDL_FlushEvent(SDL_WINDOWEVENT); - } - - SDL_SetRenderDrawColor(m_BackgroundRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(m_BackgroundRenderer); - SDL_RenderPresent(m_BackgroundRenderer); - } -} - void MmalRenderer::InputPortCallback(MMAL_PORT_T*, MMAL_BUFFER_HEADER_T* buffer) { mmal_buffer_header_release(buffer); diff --git a/app/streaming/video/ffmpeg-renderers/mmal.h b/app/streaming/video/ffmpeg-renderers/mmal.h index 560a8b36..21cf9907 100644 --- a/app/streaming/video/ffmpeg-renderers/mmal.h +++ b/app/streaming/video/ffmpeg-renderers/mmal.h @@ -13,6 +13,7 @@ public: virtual ~MmalRenderer() override; virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; + virtual void prepareToRender() override; virtual void renderFrame(AVFrame* frame) override; virtual enum AVPixelFormat getPreferredPixelFormat(int videoFormat) override; virtual bool needsTestFrame() override; @@ -23,8 +24,6 @@ private: static void InputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer); bool getDtDeviceStatus(QString name, bool ifUnknown); bool isMmalOverlaySupported(); - - void setupBackground(PDECODER_PARAMETERS params); void updateDisplayRegion(); MMAL_COMPONENT_T* m_Renderer; diff --git a/app/streaming/video/ffmpeg-renderers/renderer.h b/app/streaming/video/ffmpeg-renderers/renderer.h index 1b2b3552..91cd5a70 100644 --- a/app/streaming/video/ffmpeg-renderers/renderer.h +++ b/app/streaming/video/ffmpeg-renderers/renderer.h @@ -256,6 +256,12 @@ public: return false; } + virtual void prepareToRender() { + // Allow renderers to perform any final preparations for + // rendering after they have been selected to render. Such + // preparations might include clearing the window. + } + // Allow renderers to expose their type enum class RendererType { Unknown, diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp index 66c5c9e5..80f649b4 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp @@ -54,6 +54,14 @@ bool SdlRenderer::prepareDecoderContext(AVCodecContext*, AVDictionary**) return true; } +void SdlRenderer::prepareToRender() +{ + // Draw a black frame until the video stream starts rendering + SDL_SetRenderDrawColor(m_Renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(m_Renderer); + SDL_RenderPresent(m_Renderer); +} + bool SdlRenderer::isRenderThreadSupported() { SDL_RendererInfo info; @@ -165,13 +173,6 @@ bool SdlRenderer::initialize(PDECODER_PARAMETERS params) SDL_FlushEvent(SDL_WINDOWEVENT); } - if (!params->testOnly) { - // Draw a black frame until the video stream starts rendering - SDL_SetRenderDrawColor(m_Renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(m_Renderer); - SDL_RenderPresent(m_Renderer); - } - #ifdef Q_OS_WIN32 // For some reason, using Direct3D9Ex breaks this with multi-monitor setups. // When focus is lost, the window is minimized then immediately restored without diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.h b/app/streaming/video/ffmpeg-renderers/sdlvid.h index 9a450bf2..a47b5ce0 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.h +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.h @@ -13,6 +13,7 @@ public: virtual ~SdlRenderer() override; virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; + virtual void prepareToRender() override; virtual void renderFrame(AVFrame* frame) override; virtual bool isRenderThreadSupported() override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index f2e1846e..ddfeaa9f 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -600,6 +600,9 @@ bool FFmpegVideoDecoder::completeInitialization(const AVCodec* decoder, enum AVP // Tell overlay manager to use this frontend renderer Session::get()->getOverlayManager().setOverlayRenderer(m_FrontendRenderer); + // Allow the renderer to perform final preparations for rendering + m_FrontendRenderer->prepareToRender(); + // Only create the decoder thread when instantiating the decoder for real. It will use APIs from // moonlight-common-c that can only be legally called with an established connection. m_DecoderThread = SDL_CreateThread(FFmpegVideoDecoder::decoderThreadProcThunk, "FFDecoder", (void*)this);