Add overlay support to VDPAU renderer

This commit is contained in:
Cameron Gutman 2020-12-17 18:35:08 -06:00
parent 94b46a2173
commit 027c8dcd41
2 changed files with 186 additions and 0 deletions

View file

@ -1,3 +1,4 @@
#include <streaming/session.h>
#include "vdpau.h" #include "vdpau.h"
#include <streaming/streamutils.h> #include <streaming/streamutils.h>
@ -27,9 +28,11 @@ VDPAURenderer::VDPAURenderer()
m_PresentationQueueTarget(0), m_PresentationQueueTarget(0),
m_PresentationQueue(0), m_PresentationQueue(0),
m_VideoMixer(0), m_VideoMixer(0),
m_OverlayMutex(nullptr),
m_NextSurfaceIndex(0) m_NextSurfaceIndex(0)
{ {
SDL_zero(m_OutputSurface); SDL_zero(m_OutputSurface);
SDL_zero(m_OverlaySurface);
} }
VDPAURenderer::~VDPAURenderer() VDPAURenderer::~VDPAURenderer()
@ -52,6 +55,16 @@ VDPAURenderer::~VDPAURenderer()
} }
} }
for (int i = 0; i < Overlay::OverlayMax; i++) {
if (m_OverlaySurface[i] != 0) {
m_VdpBitmapSurfaceDestroy(m_OverlaySurface[i]);
}
}
if (m_OverlayMutex != nullptr) {
SDL_DestroyMutex(m_OverlayMutex);
}
// This must be done last as it frees VDPAU context required to call // This must be done last as it frees VDPAU context required to call
// the functions above. // the functions above.
if (m_HwContext != nullptr) { if (m_HwContext != nullptr) {
@ -136,6 +149,10 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, &m_VdpOutputSurfaceCreate); 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_DESTROY, &m_VdpOutputSurfaceDestroy);
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, &m_VdpOutputSurfaceQueryCapabilities); GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, &m_VdpOutputSurfaceQueryCapabilities);
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_CREATE, &m_VdpBitmapSurfaceCreate);
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_DESTROY, &m_VdpBitmapSurfaceDestroy);
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_PUT_BITS_NATIVE, &m_VdpBitmapSurfacePutBitsNative);
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE, &m_VdpOutputSurfaceRenderBitmapSurface);
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, &m_VdpVideoSurfaceGetParameters); GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, &m_VdpVideoSurfaceGetParameters);
GET_PROC_ADDRESS(VDP_FUNC_ID_GET_INFORMATION_STRING, &m_VdpGetInformationString); GET_PROC_ADDRESS(VDP_FUNC_ID_GET_INFORMATION_STRING, &m_VdpGetInformationString);
@ -263,6 +280,24 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
VdpColor color = {0.0, 0.0, 0.0, 1.0}; VdpColor color = {0.0, 0.0, 0.0, 1.0};
m_VdpPresentationQueueSetBackgroundColor(m_PresentationQueue, &color); m_VdpPresentationQueueSetBackgroundColor(m_PresentationQueue, &color);
// Populate blend state for overlays
m_OverlayBlendState.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION;
m_OverlayBlendState.blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_OverlayBlendState.blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_OverlayBlendState.blend_factor_source_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
m_OverlayBlendState.blend_factor_source_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
m_OverlayBlendState.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
m_OverlayBlendState.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
m_OverlayBlendState.blend_constant = {};
// Allocate mutex to synchronize overlay updates and rendering
m_OverlayMutex = SDL_CreateMutex();
if (m_OverlayMutex == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create overlay mutex");
return false;
}
return true; return true;
} }
@ -284,6 +319,98 @@ bool VDPAURenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary*
return true; return true;
} }
void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
{
VdpStatus status;
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
if (newSurface == nullptr && Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// There's no updated surface and the overlay is enabled, so just leave the old surface alone.
return;
}
// Destroy the old surface
// NB: The mutex ensures the surface is not currently being read for rendering.
// NB 2: It is safe to unlock here because this thread is the only surface producer.
SDL_LockMutex(m_OverlayMutex);
VdpBitmapSurface oldBitmapSurface = m_OverlaySurface[type];
m_OverlaySurface[type] = 0;
SDL_UnlockMutex(m_OverlayMutex);
if (oldBitmapSurface != 0) {
status = m_VdpBitmapSurfaceDestroy(oldBitmapSurface);
if (status != VDP_STATUS_OK) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpBitmapSurfaceDestroy() failed: %s",
m_VdpGetErrorString(status));
// This should never happen.
SDL_assert(false);
}
}
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
SDL_FreeSurface(newSurface);
return;
}
if (newSurface != nullptr) {
SDL_assert(!SDL_MUSTLOCK(newSurface));
VdpBitmapSurface newBitmapSurface = 0;
status = m_VdpBitmapSurfaceCreate(m_Device,
VDP_RGBA_FORMAT_B8G8R8A8,
newSurface->w,
newSurface->h,
VDP_TRUE, // Is this correct?
&newBitmapSurface);
if (status != VDP_STATUS_OK) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpBitmapSurfaceCreate() failed: %s",
m_VdpGetErrorString(status));
SDL_FreeSurface(newSurface);
return;
}
status = m_VdpBitmapSurfacePutBitsNative(newBitmapSurface,
&newSurface->pixels,
(const uint32_t*)&newSurface->pitch,
nullptr);
if (status != VDP_STATUS_OK) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpBitmapSurfacePutBitsNative() failed: %s",
m_VdpGetErrorString(status));
m_VdpBitmapSurfaceDestroy(newBitmapSurface);
SDL_FreeSurface(newSurface);
return;
}
// Surface data is no longer needed
SDL_FreeSurface(newSurface);
VdpRect overlayRect;
if (type == Overlay::OverlayStatusUpdate) {
// Bottom Left
overlayRect.x0 = 0;
overlayRect.y0 = m_DisplayHeight - newSurface->h;
}
else if (type == Overlay::OverlayDebug) {
// Top left
overlayRect.x0 = 0;
overlayRect.y0 = 0;
}
overlayRect.x1 = overlayRect.x0 + newSurface->w;
overlayRect.y1 = overlayRect.y0 + newSurface->h;
SDL_LockMutex(m_OverlayMutex);
m_OverlaySurface[type] = newBitmapSurface;
m_OverlayRect[type] = overlayRect;
SDL_UnlockMutex(m_OverlayMutex);
}
}
bool VDPAURenderer::needsTestFrame() bool VDPAURenderer::needsTestFrame()
{ {
// We need a test frame to see if this VDPAU driver // We need a test frame to see if this VDPAU driver
@ -300,6 +427,44 @@ int VDPAURenderer::getDecoderColorspace()
return COLORSPACE_REC_601; return COLORSPACE_REC_601;
} }
void VDPAURenderer::renderOverlay(VdpOutputSurface destination, Overlay::OverlayType type)
{
VdpStatus status;
// Don't even bother locking the mutex if the overlay is disabled
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
return;
}
if (SDL_TryLockMutex(m_OverlayMutex) != 0) {
// If the overlay is currently being updated, skip rendering it this frame.
return;
}
// Check if there's a surface to render
if (m_OverlaySurface[type] == 0) {
SDL_UnlockMutex(m_OverlayMutex);
return;
}
status = m_VdpOutputSurfaceRenderBitmapSurface(destination,
&m_OverlayRect[type],
m_OverlaySurface[type],
nullptr,
nullptr,
&m_OverlayBlendState,
0);
SDL_UnlockMutex(m_OverlayMutex);
if (status != VDP_STATUS_OK) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpOutputSurfaceRenderBitmapSurface() failed: %s",
m_VdpGetErrorString(status));
return;
}
}
void VDPAURenderer::renderFrame(AVFrame* frame) void VDPAURenderer::renderFrame(AVFrame* frame)
{ {
if (frame == nullptr) { if (frame == nullptr) {
@ -400,6 +565,11 @@ void VDPAURenderer::renderFrame(AVFrame* frame)
return; return;
} }
// Render overlays into the output surface before display
for (int i = 0; i < Overlay::OverlayMax; i++) {
renderOverlay(chosenSurface, (Overlay::OverlayType)i);
}
// Queue the frame for display immediately // Queue the frame for display immediately
status = m_VdpPresentationQueueDisplay(m_PresentationQueue, chosenSurface, 0, 0, 0); status = m_VdpPresentationQueueDisplay(m_PresentationQueue, chosenSurface, 0, 0, 0);
if (status != VDP_STATUS_OK) { if (status != VDP_STATUS_OK) {

View file

@ -15,11 +15,14 @@ public:
virtual ~VDPAURenderer() override; virtual ~VDPAURenderer() override;
virtual bool initialize(PDECODER_PARAMETERS params) override; virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
virtual void renderFrame(AVFrame* frame) override; virtual void renderFrame(AVFrame* frame) override;
virtual bool needsTestFrame() override; virtual bool needsTestFrame() override;
virtual int getDecoderColorspace() override; virtual int getDecoderColorspace() override;
private: private:
void renderOverlay(VdpOutputSurface destination, Overlay::OverlayType type);
uint32_t m_VideoWidth, m_VideoHeight; uint32_t m_VideoWidth, m_VideoHeight;
uint32_t m_DisplayWidth, m_DisplayHeight; uint32_t m_DisplayWidth, m_DisplayHeight;
AVBufferRef* m_HwContext; AVBufferRef* m_HwContext;
@ -29,6 +32,15 @@ private:
VdpRGBAFormat m_OutputSurfaceFormat; VdpRGBAFormat m_OutputSurfaceFormat;
VdpDevice m_Device; VdpDevice m_Device;
// We just have a single mutex to protect all overlay slots.
// This is fine because the majority of time spent in the mutex
// is by the render thread, which cannot contend with itself
// because overlays are rendered sequentially.
SDL_mutex* m_OverlayMutex;
VdpBitmapSurface m_OverlaySurface[Overlay::OverlayMax];
VdpRect m_OverlayRect[Overlay::OverlayMax];
VdpOutputSurfaceRenderBlendState m_OverlayBlendState;
#define OUTPUT_SURFACE_COUNT 3 #define OUTPUT_SURFACE_COUNT 3
VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT]; VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT];
int m_NextSurfaceIndex; int m_NextSurfaceIndex;
@ -50,6 +62,10 @@ private:
VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate; VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate;
VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy; VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy;
VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities; VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities;
VdpBitmapSurfaceCreate* m_VdpBitmapSurfaceCreate;
VdpBitmapSurfaceDestroy* m_VdpBitmapSurfaceDestroy;
VdpBitmapSurfacePutBitsNative* m_VdpBitmapSurfacePutBitsNative;
VdpOutputSurfaceRenderBitmapSurface* m_VdpOutputSurfaceRenderBitmapSurface;
VdpVideoSurfaceGetParameters* m_VdpVideoSurfaceGetParameters; VdpVideoSurfaceGetParameters* m_VdpVideoSurfaceGetParameters;
VdpGetInformationString* m_VdpGetInformationString; VdpGetInformationString* m_VdpGetInformationString;