Add VDPAU renderer
This commit is contained in:
parent
2853e498b0
commit
fca402138f
8 changed files with 384 additions and 6 deletions
|
|
@ -15,6 +15,7 @@ matrix:
|
|||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:beineri/opt-qt596-xenial'
|
||||
- sourceline: 'ppa:jonathonf/ffmpeg-4'
|
||||
packages:
|
||||
- qt59base
|
||||
- qt59quickcontrols2
|
||||
|
|
@ -22,6 +23,9 @@ matrix:
|
|||
- libesd0-dev
|
||||
- libgl-dev
|
||||
- libopus-dev
|
||||
- ffmpeg
|
||||
- libva-dev
|
||||
- libvdpau-dev
|
||||
|
||||
install:
|
||||
- '[ "$TRAVIS_OS_NAME" != osx ] || brew install qt5'
|
||||
|
|
|
|||
11
app/app.pro
11
app/app.pro
|
|
@ -53,6 +53,10 @@ unix:!macx {
|
|||
packagesExist(libva) {
|
||||
CONFIG += libva
|
||||
}
|
||||
|
||||
packagesExist(vdpau) {
|
||||
CONFIG += libvdpau
|
||||
}
|
||||
}
|
||||
}
|
||||
win32 {
|
||||
|
|
@ -112,6 +116,13 @@ libva {
|
|||
SOURCES += streaming/video/ffmpeg-renderers/vaapi.cpp
|
||||
HEADERS += streaming/video/ffmpeg-renderers/vaapi.h
|
||||
}
|
||||
libvdpau {
|
||||
message(VDPAU renderer selected)
|
||||
|
||||
DEFINES += HAVE_LIBVDPAU
|
||||
SOURCES += streaming/video/ffmpeg-renderers/vdpau.cpp
|
||||
HEADERS += streaming/video/ffmpeg-renderers/vdpau.h
|
||||
}
|
||||
config_SLVideo {
|
||||
message(SLVideo decoder/renderer selected)
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,10 @@ bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_FFMPEG) && !defined(HAVE_SLVIDEO)
|
||||
#error No video decoding libraries available!
|
||||
#endif
|
||||
|
||||
// If we reach this, we didn't initialize any decoders successfully
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ VAAPIRenderer::initialize(SDL_Window* window, int, int width, int height)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
err = av_hwdevice_ctx_create(&m_HwContext,
|
||||
AV_HWDEVICE_TYPE_VAAPI,
|
||||
nullptr, nullptr, 0);
|
||||
|
|
@ -81,6 +80,14 @@ VAAPIRenderer::initialize(SDL_Window* window, int, int width, int height)
|
|||
return false;
|
||||
}
|
||||
|
||||
// This quirk is set for the VDPAU wrapper which doesn't work with our VAAPI renderer
|
||||
if (((AVVAAPIDeviceContext*)((AVHWDeviceContext*)(m_HwContext->data))->hwctx)->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES) {
|
||||
// Fail and let our VDPAU renderer pick this up
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Avoiding VDPAU wrapper for VAAPI decoding");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,6 @@ public:
|
|||
virtual void renderFrame(AVFrame* frame);
|
||||
|
||||
private:
|
||||
static
|
||||
enum AVPixelFormat ffGetFormat(AVCodecContext*,
|
||||
const enum AVPixelFormat* pixFmts);
|
||||
|
||||
Window m_XWindow;
|
||||
AVBufferRef* m_HwContext;
|
||||
void* m_X11VaLibHandle;
|
||||
|
|
@ -51,4 +47,3 @@ private:
|
|||
int m_DisplayWidth;
|
||||
int m_DisplayHeight;
|
||||
};
|
||||
|
||||
|
|
|
|||
292
app/streaming/video/ffmpeg-renderers/vdpau.cpp
Normal file
292
app/streaming/video/ffmpeg-renderers/vdpau.cpp
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
#include "vdpau.h"
|
||||
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#define BAIL_ON_FAIL(status, something) if ((status) != VDP_STATUS_OK) { \
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, \
|
||||
#something " failed: %d", (status)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define GET_PROC_ADDRESS(id, func) status = vdpauCtx->get_proc_address(vdpauCtx->device, id, (void**)func); \
|
||||
BAIL_ON_FAIL(status, id)
|
||||
|
||||
const VdpRGBAFormat VDPAURenderer::k_OutputFormats[] = {
|
||||
VDP_RGBA_FORMAT_B8G8R8A8,
|
||||
VDP_RGBA_FORMAT_R8G8B8A8
|
||||
};
|
||||
|
||||
VDPAURenderer::VDPAURenderer()
|
||||
: m_HwContext(nullptr),
|
||||
m_PresentationQueueTarget(0),
|
||||
m_PresentationQueue(0),
|
||||
m_VideoMixer(0),
|
||||
m_NextSurfaceIndex(0)
|
||||
{
|
||||
SDL_zero(m_OutputSurface);
|
||||
}
|
||||
|
||||
VDPAURenderer::~VDPAURenderer()
|
||||
{
|
||||
for (int i = 0; i < OUTPUT_SURFACE_COUNT; i++) {
|
||||
if (m_OutputSurface[i] != 0) {
|
||||
m_VdpOutputSurfaceDestroy(m_OutputSurface[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_VideoMixer != 0) {
|
||||
m_VdpVideoMixerDestroy(m_VideoMixer);
|
||||
}
|
||||
|
||||
if (m_PresentationQueue != 0) {
|
||||
m_VdpPresentationQueueDestroy(m_PresentationQueue);
|
||||
}
|
||||
|
||||
if (m_PresentationQueueTarget != 0) {
|
||||
m_VdpPresentationQueueTargetDestroy(m_PresentationQueueTarget);
|
||||
}
|
||||
|
||||
// This must be done last as it frees VDPAU context required to call
|
||||
// the functions above.
|
||||
if (m_HwContext != nullptr) {
|
||||
av_buffer_unref(&m_HwContext);
|
||||
}
|
||||
}
|
||||
|
||||
bool VDPAURenderer::initialize(SDL_Window* window, int, int width, int height)
|
||||
{
|
||||
int err;
|
||||
VdpStatus status;
|
||||
SDL_SysWMinfo info;
|
||||
|
||||
m_VideoWidth = width;
|
||||
m_VideoHeight = height;
|
||||
|
||||
err = av_hwdevice_ctx_create(&m_HwContext,
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
nullptr, nullptr, 0);
|
||||
if (err < 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to create VDPAU context: %d",
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVHWDeviceContext* devCtx = (AVHWDeviceContext*)m_HwContext->data;
|
||||
AVVDPAUDeviceContext* vdpauCtx = (AVVDPAUDeviceContext*)devCtx->hwctx;
|
||||
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_GET_ERROR_STRING, &m_VdpGetErrorString);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY, &m_VdpPresentationQueueTargetDestroy);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_MIXER_CREATE, &m_VdpVideoMixerCreate);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_MIXER_DESTROY, &m_VdpVideoMixerDestroy);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_MIXER_RENDER, &m_VdpVideoMixerRender);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, &m_VdpPresentationQueueCreate);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY, &m_VdpPresentationQueueDestroy);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, &m_VdpPresentationQueueDisplay);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_SET_BACKGROUND_COLOR, &m_VdpPresentationQueueSetBackgroundColor);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE, &m_VdpPresentationQueueBlockUntilSurfaceIdle);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, &m_VdpOutputSurfaceCreate);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, &m_VdpOutputSurfaceDestroy);
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, &m_VdpOutputSurfaceQueryCapabilities);
|
||||
|
||||
SDL_GetWindowSize(window, (int*)&m_DisplayWidth, (int*)&m_DisplayHeight);
|
||||
|
||||
SDL_VERSION(&info.version);
|
||||
|
||||
if (!SDL_GetWindowWMInfo(window, &info)) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"SDL_GetWindowWMInfo() failed: %s",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_assert(info.subsystem == SDL_SYSWM_X11);
|
||||
|
||||
if (info.subsystem == SDL_SYSWM_X11) {
|
||||
GET_PROC_ADDRESS(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11,
|
||||
&m_VdpPresentationQueueTargetCreateX11);
|
||||
status = m_VdpPresentationQueueTargetCreateX11(vdpauCtx->device,
|
||||
info.info.x11.window,
|
||||
&m_PresentationQueueTarget);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpPresentationQueueTargetCreateX11() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (info.subsystem == SDL_SYSWM_WAYLAND) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VDPAU backend does not currently support Wayland");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Unsupported VDPAU rendering subsystem: %d",
|
||||
info.subsystem);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try our available output formats to find something the GPU supports
|
||||
bool foundFormat = false;
|
||||
for (int i = 0; i < OUTPUT_SURFACE_FORMAT_COUNT; i++) {
|
||||
VdpBool supported;
|
||||
uint32_t maxWidth, maxHeight;
|
||||
status = m_VdpOutputSurfaceQueryCapabilities(vdpauCtx->device, k_OutputFormats[i],
|
||||
&supported, &maxWidth, &maxHeight);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpOutputSurfaceQueryCapabilities() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
if (m_DisplayWidth <= maxWidth && m_DisplayHeight <= maxHeight) {
|
||||
m_OutputSurfaceFormat = k_OutputFormats[i];
|
||||
foundFormat = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Display size not within capabilities %dx%d vs %dx%d",
|
||||
m_DisplayWidth, m_DisplayWidth,
|
||||
maxWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFormat) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"No compatible output surface format found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the output surfaces
|
||||
for (int i = 0; i < OUTPUT_SURFACE_COUNT; i++) {
|
||||
status = m_VdpOutputSurfaceCreate(vdpauCtx->device, m_OutputSurfaceFormat,
|
||||
m_DisplayWidth, m_DisplayHeight,
|
||||
&m_OutputSurface[i]);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpOutputSurfaceCreate() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
status = m_VdpPresentationQueueCreate(vdpauCtx->device, m_PresentationQueueTarget,
|
||||
&m_PresentationQueue);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpPresentationQueueCreate() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the background to black
|
||||
VdpColor color;
|
||||
SDL_zero(color);
|
||||
m_VdpPresentationQueueSetBackgroundColor(m_PresentationQueue, &color);
|
||||
|
||||
#define PARAM_COUNT 2
|
||||
const VdpVideoMixerParameter params[PARAM_COUNT] = {
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
|
||||
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
|
||||
};
|
||||
const void* const paramValues[PARAM_COUNT] = {
|
||||
&m_VideoWidth,
|
||||
&m_VideoHeight,
|
||||
};
|
||||
|
||||
status = m_VdpVideoMixerCreate(vdpauCtx->device, 0, nullptr,
|
||||
PARAM_COUNT, params, paramValues,
|
||||
&m_VideoMixer);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpVideoMixerCreate() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VDPAURenderer::prepareDecoderContext(AVCodecContext* context)
|
||||
{
|
||||
context->hw_device_ctx = av_buffer_ref(m_HwContext);
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Using VDPAU accelerated renderer");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VDPAURenderer::renderFrame(AVFrame* frame)
|
||||
{
|
||||
VdpStatus status;
|
||||
VdpVideoSurface videoSurface = (VdpVideoSurface)(uintptr_t)frame->data[3];
|
||||
|
||||
// This is safe without locking because this is always called on the main thread
|
||||
VdpOutputSurface chosenSurface = m_OutputSurface[m_NextSurfaceIndex];
|
||||
m_NextSurfaceIndex = (m_NextSurfaceIndex + 1) % OUTPUT_SURFACE_COUNT;
|
||||
|
||||
// Wait for this frame to be off the screen
|
||||
VdpTime pts;
|
||||
m_VdpPresentationQueueBlockUntilSurfaceIdle(m_PresentationQueue, chosenSurface, &pts);
|
||||
|
||||
VdpRect outputRect;
|
||||
|
||||
// Center in frame and preserve aspect ratio
|
||||
double srcAspectRatio = (double)m_VideoWidth / (double)m_VideoHeight;
|
||||
double dstAspectRatio = (double)m_DisplayWidth / (double)m_DisplayHeight;
|
||||
if (dstAspectRatio < srcAspectRatio) {
|
||||
// Greater height per width
|
||||
uint32_t drawHeight = (uint32_t)(m_DisplayWidth / srcAspectRatio);
|
||||
outputRect.y0 = (m_DisplayHeight - drawHeight) / 2;
|
||||
outputRect.y1 = outputRect.y0 + drawHeight;
|
||||
outputRect.x0 = 0;
|
||||
outputRect.x1 = outputRect.x0 + m_DisplayWidth;
|
||||
}
|
||||
else {
|
||||
// Greater width per height
|
||||
uint32_t drawWidth = (uint32_t)(m_DisplayHeight * srcAspectRatio);
|
||||
outputRect.y0 = 0;
|
||||
outputRect.y1 = outputRect.y0 + m_DisplayHeight;
|
||||
outputRect.x0 = (m_DisplayWidth - drawWidth) / 2;
|
||||
outputRect.x1 = outputRect.x0 + drawWidth;
|
||||
}
|
||||
|
||||
// Render the next frame into the output surface
|
||||
status = m_VdpVideoMixerRender(m_VideoMixer,
|
||||
VDP_INVALID_HANDLE, nullptr,
|
||||
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME,
|
||||
0, nullptr,
|
||||
videoSurface,
|
||||
0, nullptr,
|
||||
nullptr,
|
||||
chosenSurface,
|
||||
&outputRect,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
// The decoder can have this surface back now
|
||||
av_frame_free(&frame);
|
||||
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpVideoMixerRender() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue the frame for display immediately
|
||||
status = m_VdpPresentationQueueDisplay(m_PresentationQueue, chosenSurface, 0, 0, 0);
|
||||
if (status != VDP_STATUS_OK) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"VdpPresentationQueueDisplay() failed: %s",
|
||||
m_VdpGetErrorString(status));
|
||||
return;
|
||||
}
|
||||
}
|
||||
57
app/streaming/video/ffmpeg-renderers/vdpau.h
Normal file
57
app/streaming/video/ffmpeg-renderers/vdpau.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
extern "C" {
|
||||
#include <vdpau/vdpau.h>
|
||||
#include <vdpau/vdpau_x11.h>
|
||||
#include <libavutil/hwcontext_vdpau.h>
|
||||
}
|
||||
|
||||
class VDPAURenderer : public IFFmpegRenderer
|
||||
{
|
||||
public:
|
||||
VDPAURenderer();
|
||||
virtual ~VDPAURenderer();
|
||||
virtual bool initialize(SDL_Window* window,
|
||||
int videoFormat,
|
||||
int width,
|
||||
int height);
|
||||
virtual bool prepareDecoderContext(AVCodecContext* context);
|
||||
virtual void renderFrame(AVFrame* frame);
|
||||
|
||||
private:
|
||||
uint32_t m_VideoWidth, m_VideoHeight;
|
||||
uint32_t m_DisplayWidth, m_DisplayHeight;
|
||||
AVBufferRef* m_HwContext;
|
||||
VdpPresentationQueueTarget m_PresentationQueueTarget;
|
||||
VdpPresentationQueue m_PresentationQueue;
|
||||
VdpVideoMixer m_VideoMixer;
|
||||
VdpRGBAFormat m_OutputSurfaceFormat;
|
||||
|
||||
#define OUTPUT_SURFACE_COUNT 3
|
||||
VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT];
|
||||
int m_NextSurfaceIndex;
|
||||
|
||||
#define OUTPUT_SURFACE_FORMAT_COUNT 2
|
||||
static const VdpRGBAFormat k_OutputFormats[OUTPUT_SURFACE_FORMAT_COUNT];
|
||||
|
||||
VdpGetErrorString* m_VdpGetErrorString;
|
||||
VdpPresentationQueueTargetDestroy* m_VdpPresentationQueueTargetDestroy;
|
||||
VdpVideoMixerCreate* m_VdpVideoMixerCreate;
|
||||
VdpVideoMixerDestroy* m_VdpVideoMixerDestroy;
|
||||
VdpVideoMixerRender* m_VdpVideoMixerRender;
|
||||
VdpPresentationQueueCreate* m_VdpPresentationQueueCreate;
|
||||
VdpPresentationQueueDestroy* m_VdpPresentationQueueDestroy;
|
||||
VdpPresentationQueueDisplay* m_VdpPresentationQueueDisplay;
|
||||
VdpPresentationQueueSetBackgroundColor* m_VdpPresentationQueueSetBackgroundColor;
|
||||
VdpPresentationQueueBlockUntilSurfaceIdle* m_VdpPresentationQueueBlockUntilSurfaceIdle;
|
||||
VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate;
|
||||
VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy;
|
||||
VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities;
|
||||
|
||||
// X11 stuff
|
||||
VdpPresentationQueueTargetCreateX11* m_VdpPresentationQueueTargetCreateX11;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -13,6 +13,10 @@
|
|||
#include "ffmpeg-renderers/vaapi.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBVDPAU
|
||||
#include "ffmpeg-renderers/vdpau.h"
|
||||
#endif
|
||||
|
||||
// This is gross but it allows us to use sizeof()
|
||||
#include "ffmpeg_videosamples.cpp"
|
||||
|
||||
|
|
@ -195,6 +199,10 @@ IFFmpegRenderer* FFmpegVideoDecoder::createAcceleratedRenderer(const AVCodecHWCo
|
|||
#ifdef HAVE_LIBVA
|
||||
case AV_HWDEVICE_TYPE_VAAPI:
|
||||
return new VAAPIRenderer();
|
||||
#endif
|
||||
#ifdef HAVE_LIBVDPAU
|
||||
case AV_HWDEVICE_TYPE_VDPAU:
|
||||
return new VDPAURenderer();
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue