From 1bb16be183d3a4f28bf6f54fef74981f00366a7d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 17 Aug 2024 16:51:48 -0500 Subject: [PATCH] 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 --- app/streaming/video/ffmpeg-renderers/drm.cpp | 103 +++++++++++-------- app/streaming/video/ffmpeg-renderers/drm.h | 4 +- app/streaming/video/ffmpeg.cpp | 17 ++- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index 702eb584..91931bf0 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -71,10 +71,10 @@ extern "C" { #include -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,47 +316,50 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) } } } - - 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, - "av_hwdevice_ctx_alloc(DRM) failed"); - return false; + 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; + } + + m_HwContext = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); + if (m_HwContext == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "av_hwdevice_ctx_alloc(DRM) failed"); + return false; + } + + AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data; + AVDRMDeviceContext* drmDeviceContext = (AVDRMDeviceContext*)deviceContext->hwctx; + + drmDeviceContext->fd = m_DrmFd; + + int err = av_hwdevice_ctx_init(m_HwContext); + if (err < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "av_hwdevice_ctx_init(DRM) failed: %d", + err); + return false; + } } - - AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data; - AVDRMDeviceContext* drmDeviceContext = (AVDRMDeviceContext*)deviceContext->hwctx; - - drmDeviceContext->fd = m_DrmFd; - - int err = av_hwdevice_ctx_init(m_HwContext); - if (err < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "av_hwdevice_ctx_init(DRM) failed: %d", - 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 @@ -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; diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index 1e96cae5..82662d52 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -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; diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index 23313800..1fadeca1 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -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,14 +899,23 @@ 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: - return nullptr; + 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