From a89cadc5209f0847bcc44881f24c7db83632aadb Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 17 Jul 2018 20:00:16 -0700 Subject: [PATCH] Separate FFmpeg decoder from the Session class (#4) --- app/app.pro | 16 +- app/streaming/session.cpp | 110 ++++++++++- app/streaming/session.hpp | 39 +--- app/streaming/video/decoder.h | 37 ++++ .../ffmpeg-renderers}/dxva2.cpp | 0 .../ffmpeg-renderers}/dxva2.h | 2 +- .../ffmpeg-renderers}/renderer.h | 6 +- .../ffmpeg-renderers}/sdl.cpp | 2 +- .../ffmpeg-renderers}/vt.h | 2 +- .../ffmpeg-renderers}/vt.mm | 4 +- app/streaming/{video.cpp => video/ffmpeg.cpp} | 183 +++++++----------- app/streaming/video/ffmpeg.h | 41 ++++ 12 files changed, 276 insertions(+), 166 deletions(-) create mode 100644 app/streaming/video/decoder.h rename app/streaming/{renderers => video/ffmpeg-renderers}/dxva2.cpp (100%) rename app/streaming/{renderers => video/ffmpeg-renderers}/dxva2.h (97%) rename app/streaming/{renderers => video/ffmpeg-renderers}/renderer.h (88%) rename app/streaming/{renderers => video/ffmpeg-renderers}/sdl.cpp (98%) rename app/streaming/{renderers => video/ffmpeg-renderers}/vt.h (81%) rename app/streaming/{renderers => video/ffmpeg-renderers}/vt.mm (98%) rename app/streaming/{video.cpp => video/ffmpeg.cpp} (54%) create mode 100644 app/streaming/video/ffmpeg.h diff --git a/app/app.pro b/app/app.pro index ca25d548..eb505875 100644 --- a/app/app.pro +++ b/app/app.pro @@ -55,16 +55,16 @@ SOURCES += \ streaming/input.cpp \ streaming/session.cpp \ streaming/audio.cpp \ - streaming/video.cpp \ + streaming/video/ffmpeg.cpp \ gui/computermodel.cpp \ gui/appmodel.cpp \ - streaming/renderers/sdl.cpp + streaming/video/ffmpeg-renderers/sdl.cpp win32 { - SOURCES += streaming/renderers/dxva2.cpp + SOURCES += streaming/video/ffmpeg-renderers/dxva2.cpp } macx { - SOURCES += streaming/renderers/vt.mm + SOURCES += streaming/video/ffmpeg-renderers/vt.mm } HEADERS += \ @@ -79,13 +79,15 @@ HEADERS += \ streaming/session.hpp \ gui/computermodel.h \ gui/appmodel.h \ - streaming/renderers/renderer.h + streaming/video/decoder.h \ + streaming/video/ffmpeg.h \ + streaming/video/ffmpeg-renderers/renderer.h win32 { - HEADERS += streaming/renderers/dxva2.h + HEADERS += streaming/video/ffmpeg-renderers/dxva2.h } macx { - HEADERS += streaming/renderers/vt.h + HEADERS += streaming/video/ffmpeg-renderers/vt.h } RESOURCES += \ diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index cd7208bf..ea204d5a 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -5,6 +5,8 @@ #include #include "utils.h" +#include "video/ffmpeg.h" + #include #include #include @@ -76,16 +78,99 @@ void Session::clLogMessage(const char* format, ...) va_end(ap); } +bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, int videoFormat, int width, int height, + int frameRate, IVideoDecoder*& chosenDecoder) +{ + chosenDecoder = new FFmpegVideoDecoder(); + if (chosenDecoder->initialize(vds, window, videoFormat, width, height, frameRate)) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "FFmpeg-based video decoder chosen"); + return true; + } + else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Unable to load FFmpeg decoder"); + delete chosenDecoder; + chosenDecoder = nullptr; + } + + // If we reach this, we didn't initialize any decoders successfully + return false; +} + +int Session::drSetup(int videoFormat, int width, int height, int frameRate, void *, int) +{ + if (!chooseDecoder(s_ActiveSession->m_Preferences.videoDecoderSelection, + s_ActiveSession->m_Window, + videoFormat, width, height, frameRate, + s_ActiveSession->m_VideoDecoder)) { + return -1; + } + + return 0; +} + +int Session::drSubmitDecodeUnit(PDECODE_UNIT du) +{ + // Use a lock since we'll be yanking this decoder out + // 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); + IVideoDecoder* decoder = s_ActiveSession->m_VideoDecoder; + if (decoder != nullptr) { + int ret = decoder->submitDecodeUnit(du); + SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock); + return ret; + } + else { + SDL_AtomicUnlock(&s_ActiveSession->m_DecoderLock); + return DR_OK; + } +} + +bool Session::isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, + int videoFormat, int width, int height, int frameRate) +{ + IVideoDecoder* decoder; + + SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN); + if (!window) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Failed to create window for hardware decode test: %s", + SDL_GetError()); + return false; + } + + if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, decoder)) { + SDL_DestroyWindow(window); + return false; + } + + SDL_DestroyWindow(window); + + bool ret = decoder->isHardwareAccelerated(); + delete decoder; + return ret; +} + Session::Session(NvComputer* computer, NvApp& app) : m_Computer(computer), m_App(app), - m_Window(nullptr) + m_Window(nullptr), + m_VideoDecoder(nullptr), + m_DecoderLock(0) { LiInitializeVideoCallbacks(&m_VideoCallbacks); m_VideoCallbacks.setup = drSetup; - m_VideoCallbacks.cleanup = drCleanup; m_VideoCallbacks.submitDecodeUnit = drSubmitDecodeUnit; - m_VideoCallbacks.capabilities = getDecoderCapabilities(); + + // Submit for decode without using a separate thread + m_VideoCallbacks.capabilities |= CAPABILITY_DIRECT_SUBMIT; + + // Slice up to 4 times for parallel decode, once slice per core + m_VideoCallbacks.capabilities |= CAPABILITY_SLICES_PER_FRAME(qMin(MAX_SLICES, SDL_GetCPUCount())); LiInitializeStreamConfiguration(&m_StreamConfig); m_StreamConfig.width = m_Preferences.width; @@ -119,7 +204,8 @@ Session::Session(NvComputer* computer, NvApp& app) isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, VIDEO_FORMAT_H265, m_StreamConfig.width, - m_StreamConfig.height); + m_StreamConfig.height, + m_StreamConfig.fps); m_StreamConfig.enableHdr = false; break; case StreamingPreferences::VCC_FORCE_H264: @@ -166,7 +252,8 @@ bool Session::validateLaunch() else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, VIDEO_FORMAT_H265, m_StreamConfig.width, - m_StreamConfig.height)) { + m_StreamConfig.height, + m_StreamConfig.fps)) { // NOTE: HEVC currently uses only 1 slice regardless of what // we provide in CAPABILITY_SLICES_PER_FRAME(), so we should // never use it for software decoding (unless common-c starts @@ -191,7 +278,8 @@ bool Session::validateLaunch() else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, VIDEO_FORMAT_H265_MAIN10, m_StreamConfig.width, - m_StreamConfig.height)) { + m_StreamConfig.height, + m_StreamConfig.fps)) { emit displayLaunchWarning("Your client PC GPU doesn't support HEVC Main10 decoding for HDR streaming."); } else { @@ -414,12 +502,12 @@ void Session::exec() SDL_GETEVENT, SDL_USEREVENT, SDL_USEREVENT) == 1) { - dropFrame(&event.user); + m_VideoDecoder->dropFrame(&event.user); event = nextEvent; } // Render the last frame - renderFrame(&event.user); + m_VideoDecoder->renderFrame(&event.user); break; } case SDL_KEYUP: @@ -463,6 +551,12 @@ DispatchDeferredCleanup: SDL_HideWindow(m_Window); } + // Destroy the decoder, since this must be done on the main thread + SDL_AtomicLock(&m_DecoderLock); + delete m_VideoDecoder; + m_VideoDecoder = nullptr; + SDL_AtomicUnlock(&m_DecoderLock); + // Cleanup can take a while, so dispatch it to a worker thread. // When it is complete, it will release our s_ActiveSessionSemaphore // reference. diff --git a/app/streaming/session.hpp b/app/streaming/session.hpp index 8999dcc8..a6cd0bf9 100644 --- a/app/streaming/session.hpp +++ b/app/streaming/session.hpp @@ -7,13 +7,7 @@ #include "backend/computermanager.h" #include "settings/streamingpreferences.h" #include "input.hpp" -#include "renderers/renderer.h" - -extern "C" { -#include -} - -#define SDL_CODE_FRAME_READY 0 +#include "video/decoder.h" class Session : public QObject { @@ -45,26 +39,14 @@ private: int sdlDetermineAudioConfiguration(); - static - bool chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, - SDL_Window* window, - int videoFormat, - int width, int height, - AVCodec*& chosenDecoder, - const AVCodecHWConfig*& chosenHwConfig, - IRenderer*& newRenderer); - - static - enum AVPixelFormat getHwFormat(AVCodecContext*, - const enum AVPixelFormat* pixFmts); - static bool isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, - int videoFormat, int width, int height); + int videoFormat, int width, int height, int frameRate); - void renderFrame(SDL_UserEvent* event); - - void dropFrame(SDL_UserEvent* event); + static + bool chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, int videoFormat, int width, int height, + int frameRate, IVideoDecoder*& chosenDecoder); static void clStageStarting(int stage); @@ -110,13 +92,8 @@ private: NvComputer* m_Computer; NvApp m_App; SDL_Window* m_Window; - - static AVPacket s_Pkt; - static AVCodecContext* s_VideoDecoderCtx; - static QByteArray s_DecodeBuffer; - static AVBufferRef* s_HwDeviceCtx; - static const AVCodecHWConfig* s_HwDecodeCfg; - static IRenderer* s_Renderer; + IVideoDecoder* m_VideoDecoder; + SDL_SpinLock m_DecoderLock; static SDL_AudioDeviceID s_AudioDevice; static OpusMSDecoder* s_OpusDecoder; diff --git a/app/streaming/video/decoder.h b/app/streaming/video/decoder.h new file mode 100644 index 00000000..2719557f --- /dev/null +++ b/app/streaming/video/decoder.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "settings/streamingpreferences.h" + +#define SDL_CODE_FRAME_READY 0 + +#define MAX_SLICES 4 + +class IVideoDecoder { +public: + virtual ~IVideoDecoder() {} + virtual bool initialize(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, + int height, + int frameRate) = 0; + virtual bool isHardwareAccelerated() = 0; + virtual int submitDecodeUnit(PDECODE_UNIT du) = 0; + virtual void renderFrame(SDL_UserEvent* event) = 0; + virtual void dropFrame(SDL_UserEvent* event) = 0; + + virtual void queueFrame(void* data1 = nullptr, + void* data2 = nullptr) + { + SDL_Event event; + + event.type = SDL_USEREVENT; + event.user.code = SDL_CODE_FRAME_READY; + event.user.data1 = data1; + event.user.data2 = data2; + + SDL_PushEvent(&event); + } +}; diff --git a/app/streaming/renderers/dxva2.cpp b/app/streaming/video/ffmpeg-renderers/dxva2.cpp similarity index 100% rename from app/streaming/renderers/dxva2.cpp rename to app/streaming/video/ffmpeg-renderers/dxva2.cpp diff --git a/app/streaming/renderers/dxva2.h b/app/streaming/video/ffmpeg-renderers/dxva2.h similarity index 97% rename from app/streaming/renderers/dxva2.h rename to app/streaming/video/ffmpeg-renderers/dxva2.h index 5ca663bd..f9589bef 100644 --- a/app/streaming/renderers/dxva2.h +++ b/app/streaming/video/ffmpeg-renderers/dxva2.h @@ -9,7 +9,7 @@ extern "C" { #include } -class DXVA2Renderer : public IRenderer +class DXVA2Renderer : public IFFmpegRenderer { public: DXVA2Renderer(); diff --git a/app/streaming/renderers/renderer.h b/app/streaming/video/ffmpeg-renderers/renderer.h similarity index 88% rename from app/streaming/renderers/renderer.h rename to app/streaming/video/ffmpeg-renderers/renderer.h index 729e5866..4b23ed30 100644 --- a/app/streaming/renderers/renderer.h +++ b/app/streaming/video/ffmpeg-renderers/renderer.h @@ -6,9 +6,9 @@ extern "C" { #include } -class IRenderer { +class IFFmpegRenderer { public: - virtual ~IRenderer() {} + virtual ~IFFmpegRenderer() {} virtual bool initialize(SDL_Window* window, int videoFormat, int width, @@ -17,7 +17,7 @@ public: virtual void renderFrame(AVFrame* frame) = 0; }; -class SdlRenderer : public IRenderer { +class SdlRenderer : public IFFmpegRenderer { public: SdlRenderer(); virtual ~SdlRenderer(); diff --git a/app/streaming/renderers/sdl.cpp b/app/streaming/video/ffmpeg-renderers/sdl.cpp similarity index 98% rename from app/streaming/renderers/sdl.cpp rename to app/streaming/video/ffmpeg-renderers/sdl.cpp index 64bbf3cd..89d05708 100644 --- a/app/streaming/renderers/sdl.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdl.cpp @@ -1,4 +1,4 @@ -#include "streaming/session.hpp" +#include "renderer.h" SdlRenderer::SdlRenderer() : m_Renderer(nullptr), diff --git a/app/streaming/renderers/vt.h b/app/streaming/video/ffmpeg-renderers/vt.h similarity index 81% rename from app/streaming/renderers/vt.h rename to app/streaming/video/ffmpeg-renderers/vt.h index b7cf1dd6..b4927e00 100644 --- a/app/streaming/renderers/vt.h +++ b/app/streaming/video/ffmpeg-renderers/vt.h @@ -7,5 +7,5 @@ class VTRendererFactory { public: static - IRenderer* createRenderer(); + IFFmpegRenderer* createRenderer(); }; diff --git a/app/streaming/renderers/vt.mm b/app/streaming/video/ffmpeg-renderers/vt.mm similarity index 98% rename from app/streaming/renderers/vt.mm rename to app/streaming/video/ffmpeg-renderers/vt.mm index 1e204b15..a8b3c388 100644 --- a/app/streaming/renderers/vt.mm +++ b/app/streaming/video/ffmpeg-renderers/vt.mm @@ -11,7 +11,7 @@ #import #import -class VTRenderer : public IRenderer +class VTRenderer : public IFFmpegRenderer { public: VTRenderer() @@ -194,6 +194,6 @@ private: NSView* m_View; }; -IRenderer* VTRendererFactory::createRenderer() { +IFFmpegRenderer* VTRendererFactory::createRenderer() { return new VTRenderer(); } diff --git a/app/streaming/video.cpp b/app/streaming/video/ffmpeg.cpp similarity index 54% rename from app/streaming/video.cpp rename to app/streaming/video/ffmpeg.cpp index 61723ed6..47bd3e83 100644 --- a/app/streaming/video.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -1,42 +1,22 @@ #include -#include "session.hpp" +#include "ffmpeg.h" #ifdef _WIN32 -#include "renderers/dxva2.h" +#include "ffmpeg-renderers/dxva2.h" #endif #ifdef __APPLE__ -#include "renderers/vt.h" +#include "ffmpeg-renderers/vt.h" #endif -AVPacket Session::s_Pkt; -AVCodecContext* Session::s_VideoDecoderCtx; -QByteArray Session::s_DecodeBuffer; -const AVCodecHWConfig* Session::s_HwDecodeCfg; -IRenderer* Session::s_Renderer; - -#define MAX_SLICES 4 - -int Session::getDecoderCapabilities() -{ - int caps = 0; - - // Submit for decode without using a separate thread - caps |= CAPABILITY_DIRECT_SUBMIT; - - // Slice up to 4 times for parallel decode, once slice per core - caps |= CAPABILITY_SLICES_PER_FRAME(qMin(MAX_SLICES, SDL_GetCPUCount())); - - return caps; -} - -bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, - SDL_Window* window, - int videoFormat, - int width, int height, - AVCodec*& chosenDecoder, - const AVCodecHWConfig*& chosenHwConfig, - IRenderer*& newRenderer) +bool FFmpegVideoDecoder::chooseDecoder( + StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, int height, + AVCodec*& chosenDecoder, + const AVCodecHWConfig*& chosenHwConfig, + IFFmpegRenderer*& newRenderer) { if (videoFormat & VIDEO_FORMAT_MASK_H264) { chosenDecoder = avcodec_find_decoder(AV_CODEC_ID_H264); @@ -106,125 +86,104 @@ bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, } } -bool Session::isHardwareDecodeAvailable( - StreamingPreferences::VideoDecoderSelection vds, - int videoFormat, int width, int height) +bool FFmpegVideoDecoder::isHardwareAccelerated() { - AVCodec* decoder; - const AVCodecHWConfig* hwConfig; - IRenderer* renderer; - - // Create temporary window to instantiate the decoder - SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN); - if (!window) { - return false; - } - - if (chooseDecoder(vds, window, videoFormat, width, height, decoder, hwConfig, renderer)) { - // The renderer may have referenced the window, so - // we must delete the renderer before the window. - delete renderer; - - SDL_DestroyWindow(window); - return hwConfig != nullptr; - } - else { - SDL_DestroyWindow(window); - // Failed to find *any* decoder, including software - return false; - } + return m_HwDecodeCfg != nullptr; } -int Session::drSetup(int videoFormat, int width, int height, int /* frameRate */, void*, int) +FFmpegVideoDecoder::FFmpegVideoDecoder() + : m_VideoDecoderCtx(nullptr), + m_DecodeBuffer(1024 * 1024, 0), + m_HwDecodeCfg(nullptr), + m_Renderer(nullptr) { - AVCodec* decoder; + av_init_packet(&m_Pkt); // Use linear filtering when renderer scaling is required SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); +} - av_init_packet(&s_Pkt); +FFmpegVideoDecoder::~FFmpegVideoDecoder() +{ + avcodec_close(m_VideoDecoderCtx); + av_free(m_VideoDecoderCtx); + m_VideoDecoderCtx = nullptr; - if (!chooseDecoder(s_ActiveSession->m_Preferences.videoDecoderSelection, - s_ActiveSession->m_Window, - videoFormat, width, height, - decoder, s_HwDecodeCfg, s_Renderer)) { + m_HwDecodeCfg = nullptr; + + delete m_Renderer; + m_Renderer = nullptr; +} + +bool FFmpegVideoDecoder::initialize( + StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, + int height, + int) +{ + AVCodec* decoder; + + if (!chooseDecoder(vds, window, videoFormat, width, height, + decoder, m_HwDecodeCfg, m_Renderer)) { // Error logged in chooseDecoder() - return -1; + return false; } - s_VideoDecoderCtx = avcodec_alloc_context3(decoder); - if (!s_VideoDecoderCtx) { + m_VideoDecoderCtx = avcodec_alloc_context3(decoder); + if (!m_VideoDecoderCtx) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to allocate video decoder context"); - delete s_Renderer; - return -1; + return false; } // Always request low delay decoding - s_VideoDecoderCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; + m_VideoDecoderCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; // Enable slice multi-threading for software decoding - if (!s_HwDecodeCfg) { - s_VideoDecoderCtx->thread_type = FF_THREAD_SLICE; - s_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetCPUCount()); + if (!m_HwDecodeCfg) { + m_VideoDecoderCtx->thread_type = FF_THREAD_SLICE; + m_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetCPUCount()); } else { // No threading for HW decode - s_VideoDecoderCtx->thread_count = 1; + m_VideoDecoderCtx->thread_count = 1; } // Setup decoding parameters - s_VideoDecoderCtx->width = width; - s_VideoDecoderCtx->height = height; - s_VideoDecoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // FIXME: HDR + m_VideoDecoderCtx->width = width; + m_VideoDecoderCtx->height = height; + m_VideoDecoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // FIXME: HDR // Allow the renderer to attach data to this decoder - if (!s_Renderer->prepareDecoderContext(s_VideoDecoderCtx)) { - delete s_Renderer; - av_free(s_VideoDecoderCtx); - return -1; + if (!m_Renderer->prepareDecoderContext(m_VideoDecoderCtx)) { + return false; } - int err = avcodec_open2(s_VideoDecoderCtx, decoder, nullptr); + int err = avcodec_open2(m_VideoDecoderCtx, decoder, nullptr); if (err < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to open decoder for format: %x", videoFormat); - delete s_Renderer; - av_free(s_VideoDecoderCtx); - return -1; + return false; } - // 1MB frame buffer to start - s_DecodeBuffer = QByteArray(1024 * 1024, 0); - - return 0; + return true; } -void Session::drCleanup() -{ - avcodec_close(s_VideoDecoderCtx); - av_free(s_VideoDecoderCtx); - s_VideoDecoderCtx = nullptr; - - s_HwDecodeCfg = nullptr; - - delete s_Renderer; - s_Renderer = nullptr; -} - -int Session::drSubmitDecodeUnit(PDECODE_UNIT du) +int FFmpegVideoDecoder::submitDecodeUnit(PDECODE_UNIT du) { PLENTRY entry = du->bufferList; int err; - if (du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE > s_DecodeBuffer.length()) { - s_DecodeBuffer = QByteArray(du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE, 0); + if (du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE > m_DecodeBuffer.length()) { + m_DecodeBuffer = QByteArray(du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE, 0); } int offset = 0; while (entry != nullptr) { - memcpy(&s_DecodeBuffer.data()[offset], + memcpy(&m_DecodeBuffer.data()[offset], entry->data, entry->length); offset += entry->length; @@ -233,10 +192,10 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du) SDL_assert(offset == du->fullLength); - s_Pkt.data = reinterpret_cast(s_DecodeBuffer.data()); - s_Pkt.size = du->fullLength; + m_Pkt.data = reinterpret_cast(m_DecodeBuffer.data()); + m_Pkt.size = du->fullLength; - err = avcodec_send_packet(s_VideoDecoderCtx, &s_Pkt); + err = avcodec_send_packet(m_VideoDecoderCtx, &m_Pkt); if (err < 0) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Decoding failed: %d", err); @@ -256,7 +215,7 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du) return DR_OK; } - err = avcodec_receive_frame(s_VideoDecoderCtx, frame); + err = avcodec_receive_frame(m_VideoDecoderCtx, frame); if (err == 0) { SDL_Event event; @@ -275,15 +234,15 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du) } // Called on main thread -void Session::renderFrame(SDL_UserEvent* event) +void FFmpegVideoDecoder::renderFrame(SDL_UserEvent* event) { AVFrame* frame = reinterpret_cast(event->data1); - s_Renderer->renderFrame(frame); + m_Renderer->renderFrame(frame); av_frame_free(&frame); } // Called on main thread -void Session::dropFrame(SDL_UserEvent* event) +void FFmpegVideoDecoder::dropFrame(SDL_UserEvent* event) { AVFrame* frame = reinterpret_cast(event->data1); av_frame_free(&frame); diff --git a/app/streaming/video/ffmpeg.h b/app/streaming/video/ffmpeg.h new file mode 100644 index 00000000..c6e51cb6 --- /dev/null +++ b/app/streaming/video/ffmpeg.h @@ -0,0 +1,41 @@ +#pragma once + +#include "decoder.h" +#include "ffmpeg-renderers/renderer.h" + +extern "C" { +#include +} + +class FFmpegVideoDecoder : public IVideoDecoder { +public: + FFmpegVideoDecoder(); + virtual ~FFmpegVideoDecoder(); + virtual bool initialize(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, + int height, + int frameRate) override; + virtual bool isHardwareAccelerated() override; + virtual int submitDecodeUnit(PDECODE_UNIT du) override; + virtual void renderFrame(SDL_UserEvent* event) override; + virtual void dropFrame(SDL_UserEvent* event) override; + +private: + bool + chooseDecoder( + StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, int height, + AVCodec*& chosenDecoder, + const AVCodecHWConfig*& chosenHwConfig, + IFFmpegRenderer*& newRenderer); + + AVPacket m_Pkt; + AVCodecContext* m_VideoDecoderCtx; + QByteArray m_DecodeBuffer; + const AVCodecHWConfig* m_HwDecodeCfg; + IFFmpegRenderer* m_Renderer; +};