Allow DrmRenderer to act as a non-DRM hwaccel backend

This case basically works like a degenerate case of a DRM hwaccel
without DRM master where we just provide EGL export functionality
except that we don't even need a DRM FD in this case.

There are patches floating around the FFmpeg list for this:
https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=12604
This commit is contained in:
Cameron Gutman 2024-08-17 16:51:48 -05:00
parent 8e2aa87c4f
commit 1bb16be183
3 changed files with 77 additions and 47 deletions

View File

@ -71,10 +71,10 @@ extern "C" {
#include <QDir>
DrmRenderer::DrmRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer)
DrmRenderer::DrmRenderer(AVHWDeviceType hwDeviceType, IFFmpegRenderer *backendRenderer)
: m_BackendRenderer(backendRenderer),
m_DrmPrimeBackend(backendRenderer && backendRenderer->canExportDrmPrime()),
m_HwAccelBackend(hwaccel),
m_HwDeviceType(hwDeviceType),
m_HwContext(nullptr),
m_DrmFd(-1),
m_SdlOwnsDrmFd(false),
@ -180,7 +180,7 @@ bool DrmRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary**
// https://doc-en.rvspace.org/VisionFive2/DG_Multimedia/JH7110_SDK/hevc_omx.html
av_dict_set(options, "omx_pix_fmt", "nv12", 0);
if (m_HwAccelBackend) {
if (m_HwDeviceType != AV_HWDEVICE_TYPE_NONE) {
context->hw_device_ctx = av_buffer_ref(m_HwContext);
}
@ -316,29 +316,19 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
}
}
}
}
// Create the device context first because it is needed whether we can
// actually use direct rendering or not.
if (m_HwDeviceType == AV_HWDEVICE_TYPE_DRM) {
// A real DRM FD is required for DRM-backed hwaccels
if (m_DrmFd < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to open DRM device: %d",
errno);
return false;
}
}
// Fetch version details about the DRM driver to use later
m_Version = drmGetVersion(m_DrmFd);
if (m_Version == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"drmGetVersion() failed: %d",
errno);
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"GPU driver: %s", m_Version->name);
// Create the device context first because it is needed whether we can
// actually use direct rendering or not.
m_HwContext = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
if (m_HwContext == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -358,6 +348,19 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
err);
return false;
}
}
else if (m_HwDeviceType != AV_HWDEVICE_TYPE_NONE) {
// We got some other non-DRM hwaccel that outputs DRM_PRIME frames.
// Create it with default parameters and hope for the best.
int err = av_hwdevice_ctx_create(&m_HwContext, m_HwDeviceType, nullptr, nullptr, 0);
if (err < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"av_hwdevice_ctx_create(%u) failed: %d",
m_HwDeviceType,
err);
return false;
}
}
// Still return true if we fail to initialize DRM direct rendering
// stuff, since we have EGLRenderer and SDLRenderer that we can use
@ -368,6 +371,24 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
// that's the whole point it's trying to use us for.
const bool DIRECT_RENDERING_INIT_FAILED = (m_BackendRenderer == nullptr);
if (m_DrmFd < 0) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Direct rendering via DRM is unavailable due to lack of DRM devices");
return DIRECT_RENDERING_INIT_FAILED;
}
// Fetch version details about the DRM driver to use later
m_Version = drmGetVersion(m_DrmFd);
if (m_Version == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"drmGetVersion() failed: %d",
errno);
return DIRECT_RENDERING_INIT_FAILED;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"GPU driver: %s", m_Version->name);
// If we're not sharing the DRM FD with SDL, that means we don't
// have DRM master, so we can't call drmModeSetPlane(). We can
// use EGLRenderer or SDLRenderer to render in this situation.
@ -692,7 +713,7 @@ enum AVPixelFormat DrmRenderer::getPreferredPixelFormat(int videoFormat)
}
bool DrmRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) {
if (m_HwAccelBackend) {
if (m_HwDeviceType != AV_HWDEVICE_TYPE_NONE) {
return pixelFormat == AV_PIX_FMT_DRM_PRIME;
}
else if (m_DrmPrimeBackend) {
@ -765,7 +786,7 @@ int DrmRenderer::getRendererAttributes()
// Restrict streaming resolution to 1080p on the Pi 4 while in the desktop environment.
// EGL performance is extremely poor and just barely hits 1080p60 on Bookworm. This also
// covers the MMAL H.264 case which maxes out at 1080p60 too.
if (!m_SupportsDirectRendering &&
if (!m_SupportsDirectRendering && m_Version &&
(strcmp(m_Version->name, "vc4") == 0 || strcmp(m_Version->name, "v3d") == 0) &&
qgetenv("RPI_ALLOW_EGL_4K") != "1") {
drmDevicePtr device;

View File

@ -48,7 +48,7 @@ namespace DrmDefs
class DrmRenderer : public IFFmpegRenderer {
public:
DrmRenderer(bool hwaccel = false, IFFmpegRenderer *backendRenderer = nullptr);
DrmRenderer(AVHWDeviceType hwDeviceType = AV_HWDEVICE_TYPE_NONE, IFFmpegRenderer *backendRenderer = nullptr);
virtual ~DrmRenderer() override;
virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
@ -80,7 +80,7 @@ private:
IFFmpegRenderer* m_BackendRenderer;
SDL_Window* m_Window;
bool m_DrmPrimeBackend;
bool m_HwAccelBackend;
AVHWDeviceType m_HwDeviceType;
AVBufferRef* m_HwContext;
int m_DrmFd;
bool m_SdlOwnsDrmFd;

View File

@ -324,7 +324,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
// not currently support this (and even if it did, Mesa and Wayland don't
// currently have protocols to actually get that metadata to the display).
if (m_BackendRenderer->canExportDrmPrime()) {
m_FrontendRenderer = new DrmRenderer(false, m_BackendRenderer);
m_FrontendRenderer = new DrmRenderer(AV_HWDEVICE_TYPE_NONE, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params) && (m_FrontendRenderer->getRendererAttributes() & RENDERER_ATTRIBUTE_HDR_SUPPORT)) {
return true;
}
@ -385,7 +385,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
#if (defined(VULKAN_IS_SLOW) || defined(GL_IS_SLOW)) && defined(HAVE_DRM)
// Try DrmRenderer first if we have a slow GPU
m_FrontendRenderer = new DrmRenderer(false, m_BackendRenderer);
m_FrontendRenderer = new DrmRenderer(AV_HWDEVICE_TYPE_NONE, m_BackendRenderer);
if (m_FrontendRenderer->initialize(params)) {
return true;
}
@ -899,16 +899,25 @@ IFFmpegRenderer* FFmpegVideoDecoder::createHwAccelRenderer(const AVCodecHWConfig
#endif
#ifdef HAVE_DRM
case AV_HWDEVICE_TYPE_DRM:
return new DrmRenderer(true);
return new DrmRenderer(hwDecodeCfg->device_type);
#endif
#ifdef HAVE_LIBPLACEBO_VULKAN
case AV_HWDEVICE_TYPE_VULKAN:
return new PlVkRenderer(true);
#endif
default:
switch (hwDecodeCfg->pix_fmt) {
#ifdef HAVE_DRM
case AV_PIX_FMT_DRM_PRIME:
// Support out-of-tree non-DRM hwaccels that output DRM_PRIME frames
// https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=12604
return new DrmRenderer(hwDecodeCfg->device_type);
#endif
default:
return nullptr;
}
}
}
// Second pass for our second-tier hwaccel implementations
else if (pass == 1) {
switch (hwDecodeCfg->device_type) {