mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-04-14 11:46:18 +00:00
DXVA2 Hardware Decoding (#3)
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
#include <Limelight.h>
|
||||
#include "session.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "renderers/dxva2.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
|
||||
|
||||
@@ -15,31 +21,122 @@ int Session::getDecoderCapabilities()
|
||||
caps |= CAPABILITY_DIRECT_SUBMIT;
|
||||
|
||||
// Slice up to 4 times for parallel decode, once slice per core
|
||||
caps |= CAPABILITY_SLICES_PER_FRAME(std::min(MAX_SLICES, SDL_GetCPUCount()));
|
||||
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) {
|
||||
chosenDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
}
|
||||
else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
|
||||
chosenDecoder = avcodec_find_decoder(AV_CODEC_ID_HEVC);
|
||||
}
|
||||
else {
|
||||
Q_ASSERT(false);
|
||||
chosenDecoder = nullptr;
|
||||
}
|
||||
|
||||
if (!chosenDecoder) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unable to find decoder for format: %x",
|
||||
videoFormat);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig *config = avcodec_get_hw_config(chosenDecoder, i);
|
||||
if (!config || vds == StreamingPreferences::VDS_FORCE_SOFTWARE) {
|
||||
// No matching hardware acceleration support.
|
||||
// This is not an error.
|
||||
chosenHwConfig = nullptr;
|
||||
newRenderer = new SdlRenderer();
|
||||
if (vds != StreamingPreferences::VDS_FORCE_HARDWARE &&
|
||||
newRenderer->initialize(window, videoFormat, width, height)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
delete newRenderer;
|
||||
newRenderer = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for acceleration types we support
|
||||
switch (config->device_type) {
|
||||
#ifdef _WIN32
|
||||
case AV_HWDEVICE_TYPE_DXVA2:
|
||||
newRenderer = new DXVA2Renderer();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newRenderer->initialize(window, videoFormat, width, height)) {
|
||||
chosenHwConfig = config;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Failed to initialize
|
||||
delete newRenderer;
|
||||
newRenderer = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::isHardwareDecodeAvailable(
|
||||
StreamingPreferences::VideoDecoderSelection vds,
|
||||
int videoFormat, int width, int height)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int Session::drSetup(int videoFormat, int width, int height, int /* frameRate */, void*, int)
|
||||
{
|
||||
AVCodec* decoder;
|
||||
|
||||
av_init_packet(&s_Pkt);
|
||||
|
||||
switch (videoFormat) {
|
||||
case VIDEO_FORMAT_H264:
|
||||
decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
break;
|
||||
case VIDEO_FORMAT_H265:
|
||||
case VIDEO_FORMAT_H265_MAIN10:
|
||||
decoder = avcodec_find_decoder(AV_CODEC_ID_HEVC);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!decoder) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unable to find decoder for format: %x",
|
||||
videoFormat);
|
||||
if (!chooseDecoder(s_ActiveSession->m_Preferences.videoDecoderSelection,
|
||||
s_ActiveSession->m_Window,
|
||||
videoFormat, width, height,
|
||||
decoder, s_HwDecodeCfg, s_Renderer)) {
|
||||
// Error logged in chooseDecoder()
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -47,24 +144,41 @@ int Session::drSetup(int videoFormat, int width, int height, int /* frameRate */
|
||||
if (!s_VideoDecoderCtx) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unable to allocate video decoder context");
|
||||
delete s_Renderer;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Enable slice multi-threading for software decoding
|
||||
// Always request low delay decoding
|
||||
s_VideoDecoderCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
s_VideoDecoderCtx->thread_type = FF_THREAD_SLICE;
|
||||
s_VideoDecoderCtx->thread_count = std::min(MAX_SLICES, SDL_GetCPUCount());
|
||||
|
||||
// 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());
|
||||
}
|
||||
else {
|
||||
// No threading for HW decode
|
||||
s_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
|
||||
|
||||
// Allow the renderer to attach data to this decoder
|
||||
if (!s_Renderer->prepareDecoderContext(s_VideoDecoderCtx)) {
|
||||
delete s_Renderer;
|
||||
av_free(s_VideoDecoderCtx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = avcodec_open2(s_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;
|
||||
}
|
||||
@@ -80,6 +194,11 @@ 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)
|
||||
@@ -147,18 +266,7 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
|
||||
void Session::renderFrame(SDL_UserEvent* event)
|
||||
{
|
||||
AVFrame* frame = reinterpret_cast<AVFrame*>(event->data1);
|
||||
|
||||
SDL_UpdateYUVTexture(m_Texture, nullptr,
|
||||
frame->data[0],
|
||||
frame->linesize[0],
|
||||
frame->data[1],
|
||||
frame->linesize[1],
|
||||
frame->data[2],
|
||||
frame->linesize[2]);
|
||||
SDL_RenderClear(m_Renderer);
|
||||
SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(m_Renderer);
|
||||
|
||||
s_Renderer->renderFrame(frame);
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user