diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index ea204d5a..da558fc9 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -101,6 +101,11 @@ bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, int Session::drSetup(int videoFormat, int width, int height, int frameRate, void *, int) { + s_ActiveSession->m_ActiveVideoFormat = videoFormat; + s_ActiveSession->m_ActiveVideoWidth = width; + s_ActiveSession->m_ActiveVideoHeight = height; + s_ActiveSession->m_ActiveVideoFrameRate = frameRate; + if (!chooseDecoder(s_ActiveSession->m_Preferences.videoDecoderSelection, s_ActiveSession->m_Window, videoFormat, width, height, frameRate, @@ -117,7 +122,16 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du) // from underneath the session when we initiate destruction. // We need to destroy the decoder on the main thread to satisfy // some API constraints (like DXVA2). + SDL_AtomicLock(&s_ActiveSession->m_DecoderLock); + + if (s_ActiveSession->m_NeedsIdr) { + // If we reset our decoder, we'll need to request an IDR frame + s_ActiveSession->m_NeedsIdr = false; + SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock); + return DR_NEED_IDR; + } + IVideoDecoder* decoder = s_ActiveSession->m_VideoDecoder; if (decoder != nullptr) { int ret = decoder->submitDecodeUnit(du); @@ -160,7 +174,8 @@ Session::Session(NvComputer* computer, NvApp& app) m_App(app), m_Window(nullptr), m_VideoDecoder(nullptr), - m_DecoderLock(0) + m_DecoderLock(0), + m_NeedsIdr(false) { LiInitializeVideoCallbacks(&m_VideoCallbacks); m_VideoCallbacks.setup = drSetup; @@ -510,6 +525,32 @@ void Session::exec() m_VideoDecoder->renderFrame(&event.user); break; } + + case SDL_RENDER_DEVICE_RESET: + case SDL_RENDER_TARGETS_RESET: + SDL_AtomicLock(&m_DecoderLock); + + // Destroy the old decoder + delete m_VideoDecoder; + + // Chose a new decoder (hopefully the same one, but possibly + // not if a GPU was removed or something). + if (!chooseDecoder(m_Preferences.videoDecoderSelection, + m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth, + m_ActiveVideoHeight, m_ActiveVideoFrameRate, + s_ActiveSession->m_VideoDecoder)) { + SDL_AtomicUnlock(&m_DecoderLock); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Failed to recreate decoder after reset"); + goto DispatchDeferredCleanup; + } + + // Request an IDR frame to complete the reset + m_NeedsIdr = true; + + SDL_AtomicUnlock(&m_DecoderLock); + break; + case SDL_KEYUP: case SDL_KEYDOWN: inputHandler.handleKeyEvent(&event.key); diff --git a/app/streaming/session.hpp b/app/streaming/session.hpp index a6cd0bf9..4bfa49f3 100644 --- a/app/streaming/session.hpp +++ b/app/streaming/session.hpp @@ -94,6 +94,12 @@ private: SDL_Window* m_Window; IVideoDecoder* m_VideoDecoder; SDL_SpinLock m_DecoderLock; + bool m_NeedsIdr; + + int m_ActiveVideoFormat; + int m_ActiveVideoWidth; + int m_ActiveVideoHeight; + int m_ActiveVideoFrameRate; static SDL_AudioDeviceID s_AudioDevice; static OpusMSDecoder* s_OpusDecoder; diff --git a/app/streaming/video/ffmpeg-renderers/dxva2.cpp b/app/streaming/video/ffmpeg-renderers/dxva2.cpp index 9acd7f72..5a5b8349 100644 --- a/app/streaming/video/ffmpeg-renderers/dxva2.cpp +++ b/app/streaming/video/ffmpeg-renderers/dxva2.cpp @@ -409,28 +409,6 @@ void DXVA2Renderer::renderFrame(AVFrame* frame) IDirect3DSurface9* surface = reinterpret_cast(frame->data[3]); HRESULT hr; - hr = m_Device->TestCooperativeLevel(); - switch (hr) { - case D3D_OK: - break; - case D3DERR_DEVICELOST: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "D3D device lost!"); - av_frame_free(&frame); - return; - case D3DERR_DEVICENOTRESET: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "D3D device not reset!"); - av_frame_free(&frame); - return; - default: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Unknown D3D error: %x", - hr); - av_frame_free(&frame); - return; - } - DXVA2_VideoSample sample = {}; sample.Start = m_FrameIndex; sample.End = m_FrameIndex + 1; @@ -473,25 +451,33 @@ void DXVA2Renderer::renderFrame(AVFrame* frame) bltParams.Alpha = DXVA2_Fixed32OpaqueAlpha(); - hr = m_Device->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0.0f, 0); - if (FAILED(hr)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Clear() failed: %x", - hr); + if (SDL_RenderClear(m_SdlRenderer) != 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "SDL_RenderClear() failed: %s", + SDL_GetError()); av_frame_free(&frame); + + // We're going to cheat a little bit here. It seems SDL's + // renderer may flake out in scenarios like moving the window + // between monitors, so generate a synthetic reset event for + // the main loop to consume. + SDL_Event event; + event.type = SDL_RENDER_TARGETS_RESET; + SDL_PushEvent(&event); + return; } hr = m_Processor->VideoProcessBlt(m_RenderTarget, &bltParams, &sample, 1, nullptr); + av_frame_free(&frame); + if (FAILED(hr)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "VideoProcessBlt() failed: %x", hr); - av_frame_free(&frame); - return; } - m_Device->Present(nullptr, nullptr, nullptr, nullptr); - - av_frame_free(&frame); + // We must try to present to trigger SDL's logic to recover the render target, + // even if VideoProcessBlt() fails. + SDL_RenderPresent(m_SdlRenderer); } diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index f24219c6..a6444950 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -128,14 +128,9 @@ FFmpegVideoDecoder::~FFmpegVideoDecoder() } } - avcodec_close(m_VideoDecoderCtx); - av_free(m_VideoDecoderCtx); - m_VideoDecoderCtx = nullptr; - - m_HwDecodeCfg = nullptr; + avcodec_free_context(&m_VideoDecoderCtx); delete m_Renderer; - m_Renderer = nullptr; } bool FFmpegVideoDecoder::initialize(