Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpeghwaccel_vaapi.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
7
8#include <QtMultimedia/private/qmultimedia_drm_support_p.h>
9#include <QtMultimedia/private/qmultimedia_va_support_p.h>
10#include <QtMultimedia/private/qvideotexturehelper_p.h>
11
12#include <QtCore/qloggingcategory.h>
13
14extern "C" {
15#include <libavutil/hwcontext_vaapi.h>
16}
17
18#include <va/va.h>
19#include <va/va_drmcommon.h>
20
21#include <unistd.h>
22
23static_assert(QT_CONFIG(vaapi));
24
26
27Q_STATIC_LOGGING_CATEGORY(qLHWAccelVAAPI, "qt.multimedia.ffmpeg.hwaccelvaapi");
28
29namespace QFFmpeg {
30
32
33constexpr bool VAExportUseLayers = false;
34
35std::shared_ptr<VAAPITextureConverter> VAAPITextureConverter::create(QRhi *rhi)
36{
37 auto ret = std::shared_ptr<VAAPITextureConverter>(new VAAPITextureConverter(rhi));
38 return ret->rhi ? ret : nullptr;
39}
40
41VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
43{
44 qCDebug(qLHWAccelVAAPI) << ">>>> Creating VAAPI HW accelerator";
45
46 if (!eglContext.isValid()) {
47 this->rhi = nullptr;
48 return;
49 }
50
51 // everything ok, indicate that we can do zero copy
52 this->rhi = rhi;
53}
54
55VAAPITextureConverter::~VAAPITextureConverter() = default;
56
57QVideoFrameTexturesHandlesUPtr VAAPITextureConverter::createTextureHandles(AVFrame *frame)
58{
59 // qCDebug(qLHWAccelVAAPI) << "VAAPIAccel::createTextureHandles";
60 if (frame->format != AV_PIX_FMT_VAAPI || !eglContext.eglDisplay()) {
61 qCDebug(qLHWAccelVAAPI) << "format/egl error" << frame->format << eglContext.eglDisplay();
62 return nullptr;
63 }
64
65 if (!frame->hw_frames_ctx)
66 return nullptr;
67
68 auto *ctx = avFrameDeviceContext(frame);
69 if (!ctx)
70 return nullptr;
71
72 auto *vaCtx = (AVVAAPIDeviceContext *)ctx->hwctx;
73 auto vaDisplay = vaCtx->display;
74 if (!vaDisplay) {
75 qCDebug(qLHWAccelVAAPI) << " no VADisplay, disabling";
76 return nullptr;
77 }
78
79 VASurfaceID vaSurface = (uintptr_t)frame->data[3];
80
81 VADRMPRIMESurfaceDescriptor prime = {};
82 const auto exportStatus =
83 vaExportSurfaceHandle(vaDisplay, vaSurface, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
84 VA_EXPORT_SURFACE_READ_ONLY
85 | (VAExportUseLayers ? VA_EXPORT_SURFACE_SEPARATE_LAYERS
86 : VA_EXPORT_SURFACE_COMPOSED_LAYERS),
87 &prime);
88 if (exportStatus != VA_STATUS_SUCCESS) {
89 qWarning() << "vaExportSurfaceHandle failed:"
90 << QtMultimediaPrivate::VAStatus(exportStatus);
91 return nullptr;
92 }
93
94 // Make sure all fd's in 'prime' are closed when we return from this function
95 QScopeGuard closeObjectsGuard([&prime]() {
96 for (uint32_t i = 0; i < prime.num_objects; ++i)
97 close(prime.objects[i].fd);
98 });
99
100 // ### Check that prime.fourcc is what we expect
101 vaSyncSurface(vaDisplay, vaSurface);
102
103// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
104// << prime.num_objects << "num layers" << prime.num_layers;
105
106 AVPixelFormat fmt = HWAccel::format(frame);
107 bool needsConversion;
108 auto qtFormat = QFFmpegVideoBuffer::toQtPixelFormat(fmt, &needsConversion);
109 QSpan<const DRMFormat> drm_formats = QtMultimediaPrivate::dmaBufFourccFromPixelFormat(qtFormat);
110 if (drm_formats.empty() || needsConversion) {
111 qWarning() << "can't use DMA transfer for pixel format" << fmt << qtFormat;
112 return nullptr;
113 }
114
115 auto *desc = QVideoTextureHelper::textureDescription(qtFormat);
116 int nPlanes = int(drm_formats.size());
117 Q_ASSERT(nPlanes == desc->nplanes);
118 nPlanes = desc->nplanes;
119// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: nPlanes" << nPlanes;
120
121 DmaBufPlane planes[4];
122 for (int i = 0; i < nPlanes; ++i) {
123 const int layer = VAExportUseLayers ? i : 0;
124 const int plane = VAExportUseLayers ? 0 : i;
125
126 if constexpr (VAExportUseLayers) {
127 if (prime.layers[i].drm_format != quint32(drm_formats[i])) {
128 qWarning() << "expected DRM format check failed expected" << Qt::hex
129 << quint32(drm_formats[i]) << "got" << prime.layers[i].drm_format;
130 }
131 }
132
133 planes[i].fd = prime.objects[prime.layers[layer].object_index[plane]].fd;
134 planes[i].offset = prime.layers[layer].offset[plane];
135 planes[i].pitch = prime.layers[layer].pitch[plane];
136 planes[i].drmFormat = drm_formats[i];
137 planes[i].modifier = QtMultimediaPrivate::DRMModifier(
138 prime.objects[prime.layers[layer].object_index[plane]].drm_format_modifier);
139 }
140
141 auto textureHandles = importDmaBufTextures(*rhi, eglContext, QSpan(planes, nPlanes), qtFormat,
142 frameSize(frame), shared_from_this());
143 if (!textureHandles) {
144 if (textureHandles.error() == FailureSeverity::unrecoverable) {
145 qWarning() << "VAAPI driver:" << vaQueryVendorString(vaDisplay);
146 this->rhi = nullptr; // Disabling texture conversion here to fix QTBUG-112312
147 }
148 return nullptr;
149 }
150
151 return std::move(*textureHandles);
152}
153
154} // namespace QFFmpeg
155
156QT_END_NAMESPACE
QVideoFrameTexturesHandlesUPtr createTextureHandles(AVFrame *frame) override
constexpr bool VAExportUseLayers
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)