mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2025-01-10 10:18:46 +00:00
Support direct KMSDRM output for platforms with slow GL implementations
This commit is contained in:
parent
ba507d8046
commit
90f26dfa45
3 changed files with 345 additions and 60 deletions
|
@ -29,6 +29,8 @@ extern "C" {
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "streaming/streamutils.h"
|
#include "streaming/streamutils.h"
|
||||||
#include "streaming/session.h"
|
#include "streaming/session.h"
|
||||||
|
|
||||||
|
@ -43,8 +45,10 @@ extern "C" {
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
DrmRenderer::DrmRenderer(IFFmpegRenderer *backendRenderer)
|
DrmRenderer::DrmRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer)
|
||||||
: m_BackendRenderer(backendRenderer),
|
: m_BackendRenderer(backendRenderer),
|
||||||
|
m_DrmPrimeBackend(backendRenderer && backendRenderer->canExportDrmPrime()),
|
||||||
|
m_HwAccelBackend(hwaccel),
|
||||||
m_HwContext(nullptr),
|
m_HwContext(nullptr),
|
||||||
m_DrmFd(-1),
|
m_DrmFd(-1),
|
||||||
m_SdlOwnsDrmFd(false),
|
m_SdlOwnsDrmFd(false),
|
||||||
|
@ -60,7 +64,9 @@ DrmRenderer::DrmRenderer(IFFmpegRenderer *backendRenderer)
|
||||||
m_ColorEncodingProp(nullptr),
|
m_ColorEncodingProp(nullptr),
|
||||||
m_ColorRangeProp(nullptr),
|
m_ColorRangeProp(nullptr),
|
||||||
m_HdrOutputMetadataProp(nullptr),
|
m_HdrOutputMetadataProp(nullptr),
|
||||||
m_HdrOutputMetadataBlobId(0)
|
m_HdrOutputMetadataBlobId(0),
|
||||||
|
m_SwFrameMapper(this),
|
||||||
|
m_CurrentSwFrameIdx(0)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
m_EGLExtDmaBuf = false;
|
m_EGLExtDmaBuf = false;
|
||||||
|
@ -69,6 +75,8 @@ DrmRenderer::DrmRenderer(IFFmpegRenderer *backendRenderer)
|
||||||
m_eglDestroyImage = nullptr;
|
m_eglDestroyImage = nullptr;
|
||||||
m_eglDestroyImageKHR = nullptr;
|
m_eglDestroyImageKHR = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SDL_zero(m_SwFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmRenderer::~DrmRenderer()
|
DrmRenderer::~DrmRenderer()
|
||||||
|
@ -76,6 +84,22 @@ DrmRenderer::~DrmRenderer()
|
||||||
// Ensure we're out of HDR mode
|
// Ensure we're out of HDR mode
|
||||||
setHdrMode(false);
|
setHdrMode(false);
|
||||||
|
|
||||||
|
for (int i = 0; i < k_SwFrameCount; i++) {
|
||||||
|
if (m_SwFrame[i].primeFd) {
|
||||||
|
close(m_SwFrame[i].primeFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_SwFrame[i].mapping) {
|
||||||
|
munmap(m_SwFrame[i].mapping, m_SwFrame[i].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_SwFrame[i].handle) {
|
||||||
|
struct drm_mode_destroy_dumb destroyBuf = {};
|
||||||
|
destroyBuf.handle = m_SwFrame[i].handle;
|
||||||
|
drmIoctl(m_DrmFd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroyBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_CurrentFbId != 0) {
|
if (m_CurrentFbId != 0) {
|
||||||
drmModeRmFB(m_DrmFd, m_CurrentFbId);
|
drmModeRmFB(m_DrmFd, m_CurrentFbId);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +135,9 @@ bool DrmRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary**
|
||||||
// buffers that we get back. We only support NV12 buffers now.
|
// buffers that we get back. We only support NV12 buffers now.
|
||||||
av_dict_set_int(options, "pixel_format", AV_PIX_FMT_NV12, 0);
|
av_dict_set_int(options, "pixel_format", AV_PIX_FMT_NV12, 0);
|
||||||
|
|
||||||
|
if (m_HwAccelBackend) {
|
||||||
context->hw_device_ctx = av_buffer_ref(m_HwContext);
|
context->hw_device_ctx = av_buffer_ref(m_HwContext);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Using DRM renderer");
|
"Using DRM renderer");
|
||||||
|
@ -124,6 +150,7 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
m_Main10Hdr = (params->videoFormat & VIDEO_FORMAT_MASK_10BIT);
|
m_Main10Hdr = (params->videoFormat & VIDEO_FORMAT_MASK_10BIT);
|
||||||
|
m_SwFrameMapper.setVideoFormat(params->videoFormat);
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 15)
|
#if SDL_VERSION_ATLEAST(2, 0, 15)
|
||||||
SDL_SysWMinfo info;
|
SDL_SysWMinfo info;
|
||||||
|
@ -437,22 +464,39 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params)
|
||||||
enum AVPixelFormat DrmRenderer::getPreferredPixelFormat(int videoFormat)
|
enum AVPixelFormat DrmRenderer::getPreferredPixelFormat(int videoFormat)
|
||||||
{
|
{
|
||||||
// DRM PRIME buffers, or whatever the backend renderer wants
|
// DRM PRIME buffers, or whatever the backend renderer wants
|
||||||
if (m_BackendRenderer != nullptr) {
|
if (m_HwAccelBackend) {
|
||||||
|
return AV_PIX_FMT_DRM_PRIME;
|
||||||
|
}
|
||||||
|
else if (m_BackendRenderer != nullptr) {
|
||||||
|
// Unlike isPixelFormatSupported(), we always return the format
|
||||||
|
// requested by the backend renderer since it's used for setting
|
||||||
|
// up the AVCodecContext.
|
||||||
return m_BackendRenderer->getPreferredPixelFormat(videoFormat);
|
return m_BackendRenderer->getPreferredPixelFormat(videoFormat);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return AV_PIX_FMT_DRM_PRIME;
|
// If we're acting as a non-hwaccel renderer, we'll ask for NV12 or P010
|
||||||
|
return (videoFormat & VIDEO_FORMAT_MASK_10BIT) ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) {
|
bool DrmRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) {
|
||||||
// Pass through the backend renderer if we have one. Otherwise we use
|
if (m_HwAccelBackend) {
|
||||||
// the default behavior which only supports the preferred format.
|
return pixelFormat == AV_PIX_FMT_DRM_PRIME;
|
||||||
if (m_BackendRenderer != nullptr) {
|
}
|
||||||
|
else if (m_DrmPrimeBackend) {
|
||||||
return m_BackendRenderer->isPixelFormatSupported(videoFormat, pixelFormat);
|
return m_BackendRenderer->isPixelFormatSupported(videoFormat, pixelFormat);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return getPreferredPixelFormat(videoFormat);
|
// If we're going to need to map this as a software frame, check
|
||||||
|
// against the set of formats we support in mapSoftwareFrame().
|
||||||
|
switch (pixelFormat) {
|
||||||
|
case AV_PIX_FMT_NV12:
|
||||||
|
case AV_PIX_FMT_NV21:
|
||||||
|
case AV_PIX_FMT_P010:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,42 +578,213 @@ void DrmRenderer::setHdrMode(bool enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmRenderer::renderFrame(AVFrame* frame)
|
bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedFrame)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
bool freeFrame;
|
||||||
|
auto drmFrame = &m_SwFrame[m_CurrentSwFrameIdx];
|
||||||
|
uint32_t drmFormat;
|
||||||
|
|
||||||
|
SDL_assert(frame->format != AV_PIX_FMT_DRM_PRIME);
|
||||||
|
SDL_assert(!m_DrmPrimeBackend);
|
||||||
|
|
||||||
|
// If this is a non-DRM hwframe that cannot be exported to DRM format, we must
|
||||||
|
// use the SwFrameMapper to map it to a swframe before we can copy it to dumb buffers.
|
||||||
|
if (frame->hw_frames_ctx != nullptr) {
|
||||||
|
frame = m_SwFrameMapper.getSwFrameFromHwFrame(frame);
|
||||||
|
if (frame == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeFrame = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
freeFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bpc;
|
||||||
|
|
||||||
|
// NB: Keep this list updated with isPixelFormatSupported()
|
||||||
|
switch (frame->format) {
|
||||||
|
case AV_PIX_FMT_NV12:
|
||||||
|
drmFormat = DRM_FORMAT_NV12;
|
||||||
|
bpc = 8;
|
||||||
|
break;
|
||||||
|
case AV_PIX_FMT_NV21:
|
||||||
|
drmFormat = DRM_FORMAT_NV21;
|
||||||
|
bpc = 8;
|
||||||
|
break;
|
||||||
|
case AV_PIX_FMT_P010:
|
||||||
|
drmFormat = DRM_FORMAT_P010;
|
||||||
|
bpc = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Unable to map frame with unsupported format: %d",
|
||||||
|
frame->format);
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new dumb buffer if needed
|
||||||
|
if (!drmFrame->handle) {
|
||||||
|
struct drm_mode_create_dumb createBuf = {};
|
||||||
|
|
||||||
|
createBuf.width = frame->width;
|
||||||
|
createBuf.height = frame->height * 2; // Y + CbCr at 2x2 subsampling
|
||||||
|
createBuf.bpp = bpc;
|
||||||
|
|
||||||
|
int err = drmIoctl(m_DrmFd, DRM_IOCTL_MODE_CREATE_DUMB, &createBuf);
|
||||||
|
if (err < 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"DRM_IOCTL_MODE_CREATE_DUMB failed: %d",
|
||||||
|
errno);
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFrame->handle = createBuf.handle;
|
||||||
|
drmFrame->pitch = createBuf.pitch;
|
||||||
|
drmFrame->size = createBuf.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the dumb buffer if needed
|
||||||
|
if (!drmFrame->mapping) {
|
||||||
|
struct drm_mode_map_dumb mapBuf = {};
|
||||||
|
mapBuf.handle = drmFrame->handle;
|
||||||
|
|
||||||
|
int err = drmIoctl(m_DrmFd, DRM_IOCTL_MODE_MAP_DUMB, &mapBuf);
|
||||||
|
if (err < 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"DRM_IOCTL_MODE_MAP_DUMB failed: %d",
|
||||||
|
errno);
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFrame->mapping = (uint8_t*)mmap(nullptr, drmFrame->size, PROT_WRITE, MAP_SHARED, m_DrmFd, mapBuf.offset);
|
||||||
|
if (drmFrame->mapping == nullptr) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"mmap() failed for dumb buffer: %d",
|
||||||
|
errno);
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert this buffer handle to a FD if needed
|
||||||
|
if (!drmFrame->primeFd) {
|
||||||
|
int err = drmPrimeHandleToFD(m_DrmFd, drmFrame->handle, O_CLOEXEC, &drmFrame->primeFd);
|
||||||
|
if (err < 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"drmPrimeHandleToFD() failed: %d",
|
||||||
|
errno);
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Construct the AVDRMFrameDescriptor and copy our frame data into the dumb buffer
|
||||||
|
SDL_zerop(mappedFrame);
|
||||||
|
|
||||||
|
// We use a single dumb buffer for semi/fully planar formats because some DRM
|
||||||
|
// drivers (i915, at least) don't support multi-buffer FBs.
|
||||||
|
mappedFrame->nb_objects = 1;
|
||||||
|
mappedFrame->objects[0].fd = drmFrame->primeFd;
|
||||||
|
mappedFrame->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR;
|
||||||
|
mappedFrame->objects[0].size = drmFrame->size;
|
||||||
|
|
||||||
|
mappedFrame->nb_layers = 1;
|
||||||
|
|
||||||
|
auto &layer = mappedFrame->layers[0];
|
||||||
|
layer.format = drmFormat;
|
||||||
|
|
||||||
|
int lastPlaneSize = 0;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (frame->data[i] != nullptr) {
|
||||||
|
auto &plane = layer.planes[layer.nb_planes];
|
||||||
|
|
||||||
|
plane.object_index = 0;
|
||||||
|
plane.pitch = drmFrame->pitch;
|
||||||
|
plane.offset = i == 0 ? 0 : (layer.planes[layer.nb_planes - 1].offset + lastPlaneSize);
|
||||||
|
|
||||||
|
int planeHeight;
|
||||||
|
if (i == 0) {
|
||||||
|
// Y plane is not subsampled
|
||||||
|
planeHeight = frame->height;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// UV/VU planes are 2x2 subsampled.
|
||||||
|
//
|
||||||
|
// NB: The pitch is the same between Y and UV/VU, becasuse the 2x subsampling
|
||||||
|
// is cancelled out by the 2x plane size of UV/VU vs U/V alone.
|
||||||
|
planeHeight = frame->height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the plane data into the dumb buffer
|
||||||
|
if (frame->linesize[i] == (int)drmFrame->pitch) {
|
||||||
|
// We can do a single memcpy() if the pitch is compatible
|
||||||
|
memcpy(drmFrame->mapping + plane.offset,
|
||||||
|
frame->data[i],
|
||||||
|
frame->linesize[i] * planeHeight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The pitch is incompatible, so we must copy line-by-line
|
||||||
|
for (int j = 0; j < planeHeight; j++) {
|
||||||
|
memcpy(drmFrame->mapping + (j * drmFrame->pitch) + plane.offset,
|
||||||
|
frame->data[i] + (j * frame->linesize[i]),
|
||||||
|
qMin(frame->linesize[i], (int)drmFrame->pitch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.nb_planes++;
|
||||||
|
|
||||||
|
lastPlaneSize = drmFrame->pitch * planeHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
m_CurrentSwFrameIdx = (m_CurrentSwFrameIdx + 1) % k_SwFrameCount;
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
if (freeFrame) {
|
||||||
|
av_frame_free(&frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrmRenderer::addFbForFrame(AVFrame *frame, uint32_t* newFbId)
|
||||||
{
|
{
|
||||||
AVDRMFrameDescriptor mappedFrame;
|
AVDRMFrameDescriptor mappedFrame;
|
||||||
AVDRMFrameDescriptor* drmFrame;
|
AVDRMFrameDescriptor* drmFrame;
|
||||||
|
int err;
|
||||||
|
|
||||||
// If we are acting as the frontend renderer, we'll need to have the backend
|
// If we don't have a DRM PRIME frame here, we'll need to map into one
|
||||||
// map this frame into a DRM PRIME descriptor that we can render.
|
if (frame->format != AV_PIX_FMT_DRM_PRIME) {
|
||||||
if (m_BackendRenderer != nullptr) {
|
if (m_DrmPrimeBackend) {
|
||||||
|
// If the backend supports DRM PRIME directly, use that.
|
||||||
if (!m_BackendRenderer->mapDrmPrimeFrame(frame, &mappedFrame)) {
|
if (!m_BackendRenderer->mapDrmPrimeFrame(frame, &mappedFrame)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, we'll map it to a software format and use dumb buffers
|
||||||
|
if (!mapSoftwareFrame(frame, &mappedFrame)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drmFrame = &mappedFrame;
|
drmFrame = &mappedFrame;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If we're the backend renderer, the frame should already have it.
|
|
||||||
SDL_assert(frame->format == AV_PIX_FMT_DRM_PRIME);
|
SDL_assert(frame->format == AV_PIX_FMT_DRM_PRIME);
|
||||||
drmFrame = (AVDRMFrameDescriptor*)frame->data[0];
|
drmFrame = (AVDRMFrameDescriptor*)frame->data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int err;
|
|
||||||
uint32_t handles[4] = {};
|
uint32_t handles[4] = {};
|
||||||
uint32_t pitches[4] = {};
|
uint32_t pitches[4] = {};
|
||||||
uint32_t offsets[4] = {};
|
uint32_t offsets[4] = {};
|
||||||
uint64_t modifiers[4] = {};
|
uint64_t modifiers[4] = {};
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
|
|
||||||
SDL_Rect src, dst;
|
|
||||||
|
|
||||||
src.x = src.y = 0;
|
|
||||||
src.w = frame->width;
|
|
||||||
src.h = frame->height;
|
|
||||||
dst = m_OutputRect;
|
|
||||||
|
|
||||||
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
|
||||||
|
|
||||||
// DRM requires composed layers rather than separate layers per plane
|
// DRM requires composed layers rather than separate layers per plane
|
||||||
SDL_assert(drmFrame->nb_layers == 1);
|
SDL_assert(drmFrame->nb_layers == 1);
|
||||||
|
|
||||||
|
@ -582,11 +797,11 @@ void DrmRenderer::renderFrame(AVFrame* frame)
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"drmPrimeFDToHandle() failed: %d",
|
"drmPrimeFDToHandle() failed: %d",
|
||||||
errno);
|
errno);
|
||||||
if (m_BackendRenderer != nullptr) {
|
if (m_DrmPrimeBackend) {
|
||||||
SDL_assert(drmFrame == &mappedFrame);
|
SDL_assert(drmFrame == &mappedFrame);
|
||||||
m_BackendRenderer->unmapDrmPrimeFrame(drmFrame);
|
m_BackendRenderer->unmapDrmPrimeFrame(drmFrame);
|
||||||
}
|
}
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pitches[i] = layer.planes[i].pitch;
|
pitches[i] = layer.planes[i].pitch;
|
||||||
|
@ -599,19 +814,15 @@ void DrmRenderer::renderFrame(AVFrame* frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember the last FB object we created so we can free it
|
|
||||||
// when we are finished rendering this one (if successful).
|
|
||||||
uint32_t lastFbId = m_CurrentFbId;
|
|
||||||
|
|
||||||
// Create a frame buffer object from the PRIME buffer
|
// Create a frame buffer object from the PRIME buffer
|
||||||
// NB: It is an error to pass modifiers without DRM_MODE_FB_MODIFIERS set.
|
// NB: It is an error to pass modifiers without DRM_MODE_FB_MODIFIERS set.
|
||||||
err = drmModeAddFB2WithModifiers(m_DrmFd, frame->width, frame->height,
|
err = drmModeAddFB2WithModifiers(m_DrmFd, frame->width, frame->height,
|
||||||
drmFrame->layers[0].format,
|
drmFrame->layers[0].format,
|
||||||
handles, pitches, offsets,
|
handles, pitches, offsets,
|
||||||
(flags & DRM_MODE_FB_MODIFIERS) ? modifiers : NULL,
|
(flags & DRM_MODE_FB_MODIFIERS) ? modifiers : NULL,
|
||||||
&m_CurrentFbId, flags);
|
newFbId, flags);
|
||||||
|
|
||||||
if (m_BackendRenderer != nullptr) {
|
if (m_DrmPrimeBackend) {
|
||||||
SDL_assert(drmFrame == &mappedFrame);
|
SDL_assert(drmFrame == &mappedFrame);
|
||||||
m_BackendRenderer->unmapDrmPrimeFrame(drmFrame);
|
m_BackendRenderer->unmapDrmPrimeFrame(drmFrame);
|
||||||
}
|
}
|
||||||
|
@ -620,6 +831,30 @@ void DrmRenderer::renderFrame(AVFrame* frame)
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"drmModeAddFB2WithModifiers() failed: %d",
|
"drmModeAddFB2WithModifiers() failed: %d",
|
||||||
errno);
|
errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrmRenderer::renderFrame(AVFrame* frame)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
SDL_Rect src, dst;
|
||||||
|
|
||||||
|
src.x = src.y = 0;
|
||||||
|
src.w = frame->width;
|
||||||
|
src.h = frame->height;
|
||||||
|
dst = m_OutputRect;
|
||||||
|
|
||||||
|
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
||||||
|
|
||||||
|
// Remember the last FB object we created so we can free it
|
||||||
|
// when we are finished rendering this one (if successful).
|
||||||
|
uint32_t lastFbId = m_CurrentFbId;
|
||||||
|
|
||||||
|
// Register a frame buffer object for this frame
|
||||||
|
if (!addFbForFrame(frame, &m_CurrentFbId)) {
|
||||||
m_CurrentFbId = lastFbId;
|
m_CurrentFbId = lastFbId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -739,23 +974,15 @@ bool DrmRenderer::needsTestFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmRenderer::testRenderFrame(AVFrame* frame) {
|
bool DrmRenderer::testRenderFrame(AVFrame* frame) {
|
||||||
// If we have a backend renderer, we must make sure it can
|
uint32_t fbId;
|
||||||
// successfully export DRM PRIME frames.
|
|
||||||
if (m_BackendRenderer != nullptr) {
|
|
||||||
AVDRMFrameDescriptor drmDescriptor;
|
|
||||||
|
|
||||||
// We shouldn't get here unless the backend at least claims
|
// Ensure we can export DRM PRIME frames (if applicable) and
|
||||||
// it can export DRM PRIME frames.
|
// add a FB object with the provided DRM format.
|
||||||
SDL_assert(m_BackendRenderer->canExportDrmPrime());
|
if (!addFbForFrame(frame, &fbId)) {
|
||||||
|
|
||||||
if (!m_BackendRenderer->mapDrmPrimeFrame(frame, &drmDescriptor)) {
|
|
||||||
// It can't, so we can't use this renderer.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_BackendRenderer->unmapDrmPrimeFrame(&drmDescriptor);
|
drmModeRmFB(m_DrmFd, fbId);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,7 +1013,12 @@ const char* DrmRenderer::getDrmColorRangeValue(AVFrame* frame)
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
|
|
||||||
bool DrmRenderer::canExportEGL() {
|
bool DrmRenderer::canExportEGL() {
|
||||||
if (qgetenv("DRM_FORCE_DIRECT") == "1") {
|
if (!m_HwAccelBackend) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Using direct rendering due to non-hwaccel backend");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (qgetenv("DRM_FORCE_DIRECT") == "1") {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Using direct rendering due to environment variable");
|
"Using direct rendering due to environment variable");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
|
#include "swframemapper.h"
|
||||||
|
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
|
@ -43,7 +44,7 @@ namespace DrmDefs
|
||||||
|
|
||||||
class DrmRenderer : public IFFmpegRenderer {
|
class DrmRenderer : public IFFmpegRenderer {
|
||||||
public:
|
public:
|
||||||
DrmRenderer(IFFmpegRenderer *backendRenderer = nullptr);
|
DrmRenderer(bool hwaccel = false, IFFmpegRenderer *backendRenderer = nullptr);
|
||||||
virtual ~DrmRenderer() override;
|
virtual ~DrmRenderer() 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;
|
||||||
|
@ -66,8 +67,12 @@ public:
|
||||||
private:
|
private:
|
||||||
const char* getDrmColorEncodingValue(AVFrame* frame);
|
const char* getDrmColorEncodingValue(AVFrame* frame);
|
||||||
const char* getDrmColorRangeValue(AVFrame* frame);
|
const char* getDrmColorRangeValue(AVFrame* frame);
|
||||||
|
bool mapSoftwareFrame(AVFrame* frame, AVDRMFrameDescriptor* mappedFrame);
|
||||||
|
bool addFbForFrame(AVFrame* frame, uint32_t* newFbId);
|
||||||
|
|
||||||
IFFmpegRenderer* m_BackendRenderer;
|
IFFmpegRenderer* m_BackendRenderer;
|
||||||
|
bool m_DrmPrimeBackend;
|
||||||
|
bool m_HwAccelBackend;
|
||||||
AVBufferRef* m_HwContext;
|
AVBufferRef* m_HwContext;
|
||||||
int m_DrmFd;
|
int m_DrmFd;
|
||||||
bool m_SdlOwnsDrmFd;
|
bool m_SdlOwnsDrmFd;
|
||||||
|
@ -86,6 +91,17 @@ private:
|
||||||
uint32_t m_HdrOutputMetadataBlobId;
|
uint32_t m_HdrOutputMetadataBlobId;
|
||||||
SDL_Rect m_OutputRect;
|
SDL_Rect m_OutputRect;
|
||||||
|
|
||||||
|
static constexpr int k_SwFrameCount = 2;
|
||||||
|
SwFrameMapper m_SwFrameMapper;
|
||||||
|
int m_CurrentSwFrameIdx;
|
||||||
|
struct {
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t pitch;
|
||||||
|
uint64_t size;
|
||||||
|
uint8_t* mapping;
|
||||||
|
int primeFd;
|
||||||
|
} m_SwFrame[k_SwFrameCount];
|
||||||
|
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
bool m_EGLExtDmaBuf;
|
bool m_EGLExtDmaBuf;
|
||||||
PFNEGLCREATEIMAGEPROC m_eglCreateImage;
|
PFNEGLCREATEIMAGEPROC m_eglCreateImage;
|
||||||
|
|
|
@ -111,8 +111,8 @@ int FFmpegVideoDecoder::getDecoderCapabilities()
|
||||||
}
|
}
|
||||||
else if (m_HwDecodeCfg == nullptr) {
|
else if (m_HwDecodeCfg == nullptr) {
|
||||||
// We have a non-hwaccel hardware decoder. This will always
|
// We have a non-hwaccel hardware decoder. This will always
|
||||||
// be using SDLRenderer so we will pick decoder capabilities
|
// be using SDLRenderer/DrmRenderer so we will pick decoder
|
||||||
// based on the decoder name.
|
// capabilities based on the decoder name.
|
||||||
for (int i = 0; i < SDL_arraysize(k_NonHwaccelCodecInfo); i++) {
|
for (int i = 0; i < SDL_arraysize(k_NonHwaccelCodecInfo); i++) {
|
||||||
if (strcmp(m_VideoDecoderCtx->codec->name, k_NonHwaccelCodecInfo[i].codec) == 0) {
|
if (strcmp(m_VideoDecoderCtx->codec->name, k_NonHwaccelCodecInfo[i].codec) == 0) {
|
||||||
capabilities = k_NonHwaccelCodecInfo[i].capabilities;
|
capabilities = k_NonHwaccelCodecInfo[i].capabilities;
|
||||||
|
@ -287,7 +287,7 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
|
||||||
// not currently support this (and even if it did, Mesa and Wayland don't
|
// 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).
|
// currently have protocols to actually get that metadata to the display).
|
||||||
if ((params->videoFormat & VIDEO_FORMAT_MASK_10BIT) && m_BackendRenderer->canExportDrmPrime()) {
|
if ((params->videoFormat & VIDEO_FORMAT_MASK_10BIT) && m_BackendRenderer->canExportDrmPrime()) {
|
||||||
m_FrontendRenderer = new DrmRenderer(m_BackendRenderer);
|
m_FrontendRenderer = new DrmRenderer(false, m_BackendRenderer);
|
||||||
if (m_FrontendRenderer->initialize(params)) {
|
if (m_FrontendRenderer->initialize(params)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,17 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The backend renderer cannot directly render to the display, so
|
// The backend renderer cannot directly render to the display, so
|
||||||
// we will create an SDL renderer to draw the frames.
|
// we will create an SDL or DRM renderer to draw the frames.
|
||||||
|
|
||||||
|
#if defined(HAVE_DRM) && defined(GL_IS_SLOW)
|
||||||
|
m_FrontendRenderer = new DrmRenderer(false, m_BackendRenderer);
|
||||||
|
if (m_FrontendRenderer->initialize(params)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delete m_FrontendRenderer;
|
||||||
|
m_FrontendRenderer = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
m_FrontendRenderer = new SdlRenderer();
|
m_FrontendRenderer = new SdlRenderer();
|
||||||
if (!m_FrontendRenderer->initialize(params)) {
|
if (!m_FrontendRenderer->initialize(params)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -681,7 +691,7 @@ IFFmpegRenderer* FFmpegVideoDecoder::createHwAccelRenderer(const AVCodecHWConfig
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_DRM
|
#ifdef HAVE_DRM
|
||||||
case AV_HWDEVICE_TYPE_DRM:
|
case AV_HWDEVICE_TYPE_DRM:
|
||||||
return new DrmRenderer();
|
return new DrmRenderer(true);
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -829,9 +839,21 @@ bool FFmpegVideoDecoder::tryInitializeRendererForDecoderByName(const char *decod
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoder->pix_fmts == NULL) {
|
if (decoder->pix_fmts == NULL) {
|
||||||
// Supported output pixel formats are unknown. We'll just try SDL and hope it can cope.
|
// Supported output pixel formats are unknown. We'll just try DRM/SDL and hope it can cope.
|
||||||
return tryInitializeRenderer(decoder, params, nullptr,
|
|
||||||
[]() -> IFFmpegRenderer* { return new SdlRenderer(); });
|
#if defined(HAVE_DRM) && defined(GL_IS_SLOW)
|
||||||
|
if (tryInitializeRenderer(decoder, params, nullptr,
|
||||||
|
[]() -> IFFmpegRenderer* { return new DrmRenderer(); })) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (tryInitializeRenderer(decoder, params, nullptr,
|
||||||
|
[]() -> IFFmpegRenderer* { return new SdlRenderer(); })) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_MMAL
|
#ifdef HAVE_MMAL
|
||||||
|
@ -856,7 +878,9 @@ bool FFmpegVideoDecoder::tryInitializeRendererForDecoderByName(const char *decod
|
||||||
#ifdef HAVE_DRM
|
#ifdef HAVE_DRM
|
||||||
TRY_PREFERRED_PIXEL_FORMAT(DrmRenderer);
|
TRY_PREFERRED_PIXEL_FORMAT(DrmRenderer);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef GL_IS_SLOW
|
||||||
TRY_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
TRY_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing prefers any of them. Let's see if anyone will tolerate one.
|
// Nothing prefers any of them. Let's see if anyone will tolerate one.
|
||||||
|
@ -864,8 +888,21 @@ bool FFmpegVideoDecoder::tryInitializeRendererForDecoderByName(const char *decod
|
||||||
#ifdef HAVE_DRM
|
#ifdef HAVE_DRM
|
||||||
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(DrmRenderer);
|
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(DrmRenderer);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef GL_IS_SLOW
|
||||||
|
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GL_IS_SLOW
|
||||||
|
// If we got here with GL_IS_SLOW, DrmRenderer didn't work, so we have
|
||||||
|
// to resort to SdlRenderer.
|
||||||
|
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
|
||||||
|
TRY_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
||||||
|
}
|
||||||
|
for (int i = 0; decoder->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
|
||||||
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
TRY_SUPPORTED_NON_PREFERRED_PIXEL_FORMAT(SdlRenderer);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// If we made it here, we couldn't find anything
|
// If we made it here, we couldn't find anything
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in a new issue