Separate FFmpeg decoder from the Session class (#4)

This commit is contained in:
Cameron Gutman 2018-07-17 20:00:16 -07:00 committed by GitHub
parent ec68f2ae89
commit a89cadc520
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 276 additions and 166 deletions

View File

@ -55,16 +55,16 @@ SOURCES += \
streaming/input.cpp \ streaming/input.cpp \
streaming/session.cpp \ streaming/session.cpp \
streaming/audio.cpp \ streaming/audio.cpp \
streaming/video.cpp \ streaming/video/ffmpeg.cpp \
gui/computermodel.cpp \ gui/computermodel.cpp \
gui/appmodel.cpp \ gui/appmodel.cpp \
streaming/renderers/sdl.cpp streaming/video/ffmpeg-renderers/sdl.cpp
win32 { win32 {
SOURCES += streaming/renderers/dxva2.cpp SOURCES += streaming/video/ffmpeg-renderers/dxva2.cpp
} }
macx { macx {
SOURCES += streaming/renderers/vt.mm SOURCES += streaming/video/ffmpeg-renderers/vt.mm
} }
HEADERS += \ HEADERS += \
@ -79,13 +79,15 @@ HEADERS += \
streaming/session.hpp \ streaming/session.hpp \
gui/computermodel.h \ gui/computermodel.h \
gui/appmodel.h \ gui/appmodel.h \
streaming/renderers/renderer.h streaming/video/decoder.h \
streaming/video/ffmpeg.h \
streaming/video/ffmpeg-renderers/renderer.h
win32 { win32 {
HEADERS += streaming/renderers/dxva2.h HEADERS += streaming/video/ffmpeg-renderers/dxva2.h
} }
macx { macx {
HEADERS += streaming/renderers/vt.h HEADERS += streaming/video/ffmpeg-renderers/vt.h
} }
RESOURCES += \ RESOURCES += \

View File

@ -5,6 +5,8 @@
#include <SDL.h> #include <SDL.h>
#include "utils.h" #include "utils.h"
#include "video/ffmpeg.h"
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QtEndian> #include <QtEndian>
#include <QCoreApplication> #include <QCoreApplication>
@ -76,16 +78,99 @@ void Session::clLogMessage(const char* format, ...)
va_end(ap); 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) Session::Session(NvComputer* computer, NvApp& app)
: m_Computer(computer), : m_Computer(computer),
m_App(app), m_App(app),
m_Window(nullptr) m_Window(nullptr),
m_VideoDecoder(nullptr),
m_DecoderLock(0)
{ {
LiInitializeVideoCallbacks(&m_VideoCallbacks); LiInitializeVideoCallbacks(&m_VideoCallbacks);
m_VideoCallbacks.setup = drSetup; m_VideoCallbacks.setup = drSetup;
m_VideoCallbacks.cleanup = drCleanup;
m_VideoCallbacks.submitDecodeUnit = drSubmitDecodeUnit; 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); LiInitializeStreamConfiguration(&m_StreamConfig);
m_StreamConfig.width = m_Preferences.width; m_StreamConfig.width = m_Preferences.width;
@ -119,7 +204,8 @@ Session::Session(NvComputer* computer, NvApp& app)
isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height); m_StreamConfig.height,
m_StreamConfig.fps);
m_StreamConfig.enableHdr = false; m_StreamConfig.enableHdr = false;
break; break;
case StreamingPreferences::VCC_FORCE_H264: case StreamingPreferences::VCC_FORCE_H264:
@ -166,7 +252,8 @@ bool Session::validateLaunch()
else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height)) { m_StreamConfig.height,
m_StreamConfig.fps)) {
// NOTE: HEVC currently uses only 1 slice regardless of what // NOTE: HEVC currently uses only 1 slice regardless of what
// we provide in CAPABILITY_SLICES_PER_FRAME(), so we should // we provide in CAPABILITY_SLICES_PER_FRAME(), so we should
// never use it for software decoding (unless common-c starts // never use it for software decoding (unless common-c starts
@ -191,7 +278,8 @@ bool Session::validateLaunch()
else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection, else if (!isHardwareDecodeAvailable(m_Preferences.videoDecoderSelection,
VIDEO_FORMAT_H265_MAIN10, VIDEO_FORMAT_H265_MAIN10,
m_StreamConfig.width, 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."); emit displayLaunchWarning("Your client PC GPU doesn't support HEVC Main10 decoding for HDR streaming.");
} }
else { else {
@ -414,12 +502,12 @@ void Session::exec()
SDL_GETEVENT, SDL_GETEVENT,
SDL_USEREVENT, SDL_USEREVENT,
SDL_USEREVENT) == 1) { SDL_USEREVENT) == 1) {
dropFrame(&event.user); m_VideoDecoder->dropFrame(&event.user);
event = nextEvent; event = nextEvent;
} }
// Render the last frame // Render the last frame
renderFrame(&event.user); m_VideoDecoder->renderFrame(&event.user);
break; break;
} }
case SDL_KEYUP: case SDL_KEYUP:
@ -463,6 +551,12 @@ DispatchDeferredCleanup:
SDL_HideWindow(m_Window); 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. // Cleanup can take a while, so dispatch it to a worker thread.
// When it is complete, it will release our s_ActiveSessionSemaphore // When it is complete, it will release our s_ActiveSessionSemaphore
// reference. // reference.

View File

@ -7,13 +7,7 @@
#include "backend/computermanager.h" #include "backend/computermanager.h"
#include "settings/streamingpreferences.h" #include "settings/streamingpreferences.h"
#include "input.hpp" #include "input.hpp"
#include "renderers/renderer.h" #include "video/decoder.h"
extern "C" {
#include <libavcodec/avcodec.h>
}
#define SDL_CODE_FRAME_READY 0
class Session : public QObject class Session : public QObject
{ {
@ -45,26 +39,14 @@ private:
int sdlDetermineAudioConfiguration(); 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 static
bool isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, bool isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height); int videoFormat, int width, int height, int frameRate);
void renderFrame(SDL_UserEvent* event); static
bool chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
void dropFrame(SDL_UserEvent* event); SDL_Window* window, int videoFormat, int width, int height,
int frameRate, IVideoDecoder*& chosenDecoder);
static static
void clStageStarting(int stage); void clStageStarting(int stage);
@ -110,13 +92,8 @@ private:
NvComputer* m_Computer; NvComputer* m_Computer;
NvApp m_App; NvApp m_App;
SDL_Window* m_Window; SDL_Window* m_Window;
IVideoDecoder* m_VideoDecoder;
static AVPacket s_Pkt; SDL_SpinLock m_DecoderLock;
static AVCodecContext* s_VideoDecoderCtx;
static QByteArray s_DecodeBuffer;
static AVBufferRef* s_HwDeviceCtx;
static const AVCodecHWConfig* s_HwDecodeCfg;
static IRenderer* s_Renderer;
static SDL_AudioDeviceID s_AudioDevice; static SDL_AudioDeviceID s_AudioDevice;
static OpusMSDecoder* s_OpusDecoder; static OpusMSDecoder* s_OpusDecoder;

View File

@ -0,0 +1,37 @@
#pragma once
#include <Limelight.h>
#include <SDL.h>
#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);
}
};

View File

@ -9,7 +9,7 @@ extern "C" {
#include <libavcodec/dxva2.h> #include <libavcodec/dxva2.h>
} }
class DXVA2Renderer : public IRenderer class DXVA2Renderer : public IFFmpegRenderer
{ {
public: public:
DXVA2Renderer(); DXVA2Renderer();

View File

@ -6,9 +6,9 @@ extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
} }
class IRenderer { class IFFmpegRenderer {
public: public:
virtual ~IRenderer() {} virtual ~IFFmpegRenderer() {}
virtual bool initialize(SDL_Window* window, virtual bool initialize(SDL_Window* window,
int videoFormat, int videoFormat,
int width, int width,
@ -17,7 +17,7 @@ public:
virtual void renderFrame(AVFrame* frame) = 0; virtual void renderFrame(AVFrame* frame) = 0;
}; };
class SdlRenderer : public IRenderer { class SdlRenderer : public IFFmpegRenderer {
public: public:
SdlRenderer(); SdlRenderer();
virtual ~SdlRenderer(); virtual ~SdlRenderer();

View File

@ -1,4 +1,4 @@
#include "streaming/session.hpp" #include "renderer.h"
SdlRenderer::SdlRenderer() SdlRenderer::SdlRenderer()
: m_Renderer(nullptr), : m_Renderer(nullptr),

View File

@ -7,5 +7,5 @@
class VTRendererFactory { class VTRendererFactory {
public: public:
static static
IRenderer* createRenderer(); IFFmpegRenderer* createRenderer();
}; };

View File

@ -11,7 +11,7 @@
#import <VideoToolbox/VideoToolbox.h> #import <VideoToolbox/VideoToolbox.h>
#import <AVFoundation/AVFoundation.h> #import <AVFoundation/AVFoundation.h>
class VTRenderer : public IRenderer class VTRenderer : public IFFmpegRenderer
{ {
public: public:
VTRenderer() VTRenderer()
@ -194,6 +194,6 @@ private:
NSView* m_View; NSView* m_View;
}; };
IRenderer* VTRendererFactory::createRenderer() { IFFmpegRenderer* VTRendererFactory::createRenderer() {
return new VTRenderer(); return new VTRenderer();
} }

View File

@ -1,42 +1,22 @@
#include <Limelight.h> #include <Limelight.h>
#include "session.hpp" #include "ffmpeg.h"
#ifdef _WIN32 #ifdef _WIN32
#include "renderers/dxva2.h" #include "ffmpeg-renderers/dxva2.h"
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include "renderers/vt.h" #include "ffmpeg-renderers/vt.h"
#endif #endif
AVPacket Session::s_Pkt; bool FFmpegVideoDecoder::chooseDecoder(
AVCodecContext* Session::s_VideoDecoderCtx; StreamingPreferences::VideoDecoderSelection vds,
QByteArray Session::s_DecodeBuffer; SDL_Window* window,
const AVCodecHWConfig* Session::s_HwDecodeCfg; int videoFormat,
IRenderer* Session::s_Renderer; int width, int height,
AVCodec*& chosenDecoder,
#define MAX_SLICES 4 const AVCodecHWConfig*& chosenHwConfig,
IFFmpegRenderer*& newRenderer)
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)
{ {
if (videoFormat & VIDEO_FORMAT_MASK_H264) { if (videoFormat & VIDEO_FORMAT_MASK_H264) {
chosenDecoder = avcodec_find_decoder(AV_CODEC_ID_H264); chosenDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
@ -106,125 +86,104 @@ bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
} }
} }
bool Session::isHardwareDecodeAvailable( bool FFmpegVideoDecoder::isHardwareAccelerated()
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height)
{ {
AVCodec* decoder; return m_HwDecodeCfg != nullptr;
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;
}
} }
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 // Use linear filtering when renderer scaling is required
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); 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, m_HwDecodeCfg = nullptr;
s_ActiveSession->m_Window,
videoFormat, width, height, delete m_Renderer;
decoder, s_HwDecodeCfg, s_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() // Error logged in chooseDecoder()
return -1; return false;
} }
s_VideoDecoderCtx = avcodec_alloc_context3(decoder); m_VideoDecoderCtx = avcodec_alloc_context3(decoder);
if (!s_VideoDecoderCtx) { if (!m_VideoDecoderCtx) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to allocate video decoder context"); "Unable to allocate video decoder context");
delete s_Renderer; return false;
return -1;
} }
// Always request low delay decoding // 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 // Enable slice multi-threading for software decoding
if (!s_HwDecodeCfg) { if (!m_HwDecodeCfg) {
s_VideoDecoderCtx->thread_type = FF_THREAD_SLICE; m_VideoDecoderCtx->thread_type = FF_THREAD_SLICE;
s_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetCPUCount()); m_VideoDecoderCtx->thread_count = qMin(MAX_SLICES, SDL_GetCPUCount());
} }
else { else {
// No threading for HW decode // No threading for HW decode
s_VideoDecoderCtx->thread_count = 1; m_VideoDecoderCtx->thread_count = 1;
} }
// Setup decoding parameters // Setup decoding parameters
s_VideoDecoderCtx->width = width; m_VideoDecoderCtx->width = width;
s_VideoDecoderCtx->height = height; m_VideoDecoderCtx->height = height;
s_VideoDecoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // FIXME: HDR m_VideoDecoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // FIXME: HDR
// Allow the renderer to attach data to this decoder // Allow the renderer to attach data to this decoder
if (!s_Renderer->prepareDecoderContext(s_VideoDecoderCtx)) { if (!m_Renderer->prepareDecoderContext(m_VideoDecoderCtx)) {
delete s_Renderer; return false;
av_free(s_VideoDecoderCtx);
return -1;
} }
int err = avcodec_open2(s_VideoDecoderCtx, decoder, nullptr); int err = avcodec_open2(m_VideoDecoderCtx, decoder, nullptr);
if (err < 0) { if (err < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open decoder for format: %x", "Unable to open decoder for format: %x",
videoFormat); videoFormat);
delete s_Renderer; return false;
av_free(s_VideoDecoderCtx);
return -1;
} }
// 1MB frame buffer to start return true;
s_DecodeBuffer = QByteArray(1024 * 1024, 0);
return 0;
} }
void Session::drCleanup() int FFmpegVideoDecoder::submitDecodeUnit(PDECODE_UNIT du)
{
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)
{ {
PLENTRY entry = du->bufferList; PLENTRY entry = du->bufferList;
int err; int err;
if (du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE > s_DecodeBuffer.length()) { if (du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE > m_DecodeBuffer.length()) {
s_DecodeBuffer = QByteArray(du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE, 0); m_DecodeBuffer = QByteArray(du->fullLength + AV_INPUT_BUFFER_PADDING_SIZE, 0);
} }
int offset = 0; int offset = 0;
while (entry != nullptr) { while (entry != nullptr) {
memcpy(&s_DecodeBuffer.data()[offset], memcpy(&m_DecodeBuffer.data()[offset],
entry->data, entry->data,
entry->length); entry->length);
offset += entry->length; offset += entry->length;
@ -233,10 +192,10 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
SDL_assert(offset == du->fullLength); SDL_assert(offset == du->fullLength);
s_Pkt.data = reinterpret_cast<uint8_t*>(s_DecodeBuffer.data()); m_Pkt.data = reinterpret_cast<uint8_t*>(m_DecodeBuffer.data());
s_Pkt.size = du->fullLength; m_Pkt.size = du->fullLength;
err = avcodec_send_packet(s_VideoDecoderCtx, &s_Pkt); err = avcodec_send_packet(m_VideoDecoderCtx, &m_Pkt);
if (err < 0) { if (err < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Decoding failed: %d", err); "Decoding failed: %d", err);
@ -256,7 +215,7 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
return DR_OK; return DR_OK;
} }
err = avcodec_receive_frame(s_VideoDecoderCtx, frame); err = avcodec_receive_frame(m_VideoDecoderCtx, frame);
if (err == 0) { if (err == 0) {
SDL_Event event; SDL_Event event;
@ -275,15 +234,15 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
} }
// Called on main thread // Called on main thread
void Session::renderFrame(SDL_UserEvent* event) void FFmpegVideoDecoder::renderFrame(SDL_UserEvent* event)
{ {
AVFrame* frame = reinterpret_cast<AVFrame*>(event->data1); AVFrame* frame = reinterpret_cast<AVFrame*>(event->data1);
s_Renderer->renderFrame(frame); m_Renderer->renderFrame(frame);
av_frame_free(&frame); av_frame_free(&frame);
} }
// Called on main thread // Called on main thread
void Session::dropFrame(SDL_UserEvent* event) void FFmpegVideoDecoder::dropFrame(SDL_UserEvent* event)
{ {
AVFrame* frame = reinterpret_cast<AVFrame*>(event->data1); AVFrame* frame = reinterpret_cast<AVFrame*>(event->data1);
av_frame_free(&frame); av_frame_free(&frame);

View File

@ -0,0 +1,41 @@
#pragma once
#include "decoder.h"
#include "ffmpeg-renderers/renderer.h"
extern "C" {
#include <libavcodec/avcodec.h>
}
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;
};