Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qvideotexturehelper.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
7
8#include <qpainter.h>
9#include <qloggingcategory.h>
10
12
14{
15
17 // Format_Invalid
18 { 0, 0,
19 [](int, int) { return 0; },
21 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
22 },
23 // Format_ARGB8888
24 { 1, 4,
25 [](int stride, int height) { return stride*height; },
27 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
28 },
29 // Format_ARGB8888_Premultiplied
30 { 1, 4,
31 [](int stride, int height) { return stride*height; },
33 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
34 },
35 // Format_XRGB8888
36 { 1, 4,
37 [](int stride, int height) { return stride*height; },
39 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
40 },
41 // Format_BGRA8888
42 { 1, 4,
43 [](int stride, int height) { return stride*height; },
45 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
46 },
47 // Format_BGRA8888_Premultiplied
48 { 1, 4,
49 [](int stride, int height) { return stride*height; },
51 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
52 },
53 // Format_BGRX8888
54 { 1, 4,
55 [](int stride, int height) { return stride*height; },
57 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
58 },
59 // Format_ABGR8888
60 { 1, 4,
61 [](int stride, int height) { return stride*height; },
63 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
64 },
65 // Format_XBGR8888
66 { 1, 4,
67 [](int stride, int height) { return stride*height; },
69 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
70 },
71 // Format_RGBA8888
72 { 1, 4,
73 [](int stride, int height) { return stride*height; },
75 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
76 },
77 // Format_RGBX8888
78 { 1, 4,
79 [](int stride, int height) { return stride*height; },
81 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
82 },
83 // Format_AYUV
84 { 1, 4,
85 [](int stride, int height) { return stride*height; },
87 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
88 },
89 // Format_AYUV_Premultiplied
90 { 1, 4,
91 [](int stride, int height) { return stride*height; },
93 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
94 },
95 // Format_YUV420P
96 { 3, 1,
97 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
99 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
100 },
101 // Format_YUV422P
102 { 3, 1,
103 [](int stride, int height) { return stride * height * 2; },
105 { { 1, 1 }, { 2, 1 }, { 2, 1 } }
106 },
107 // Format_YV12
108 { 3, 1,
109 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
111 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
112 },
113 // Format_UYVY
114 { 1, 2,
115 [](int stride, int height) { return stride*height; },
117 { { 2, 1 }, { 1, 1 }, { 1, 1 } }
118 },
119 // Format_YUYV
120 { 1, 2,
121 [](int stride, int height) { return stride*height; },
123 { { 2, 1 }, { 1, 1 }, { 1, 1 } }
124 },
125 // Format_NV12
126 { 2, 1,
127 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
129 { { 1, 1 }, { 2, 2 }, { 1, 1 } }
130 },
131 // Format_NV21
132 { 2, 1,
133 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
135 { { 1, 1 }, { 2, 2 }, { 1, 1 } }
136 },
137 // Format_IMC1
138 { 3, 1,
139 [](int stride, int height) {
140 // IMC1 requires that U and V components are aligned on a multiple of 16 lines
141 int h = (height + 15) & ~15;
142 h += 2*(((h/2) + 15) & ~15);
143 return stride * h;
144 },
146 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
147 },
148 // Format_IMC2
149 { 2, 1,
150 [](int stride, int height) { return 2*stride*height; },
152 { { 1, 1 }, { 1, 2 }, { 1, 1 } }
153 },
154 // Format_IMC3
155 { 3, 1,
156 [](int stride, int height) {
157 // IMC3 requires that U and V components are aligned on a multiple of 16 lines
158 int h = (height + 15) & ~15;
159 h += 2*(((h/2) + 15) & ~15);
160 return stride * h;
161 },
163 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
164 },
165 // Format_IMC4
166 { 2, 1,
167 [](int stride, int height) { return 2*stride*height; },
169 { { 1, 1 }, { 1, 2 }, { 1, 1 } }
170 },
171 // Format_Y8
172 { 1, 1,
173 [](int stride, int height) { return stride*height; },
175 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
176 },
177 // Format_Y16
178 { 1, 2,
179 [](int stride, int height) { return stride*height; },
181 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
182 },
183 // Format_P010
184 { 2, 2,
185 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
187 { { 1, 1 }, { 2, 2 }, { 1, 1 } }
188 },
189 // Format_P016
190 { 2, 2,
191 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
193 { { 1, 1 }, { 2, 2 }, { 1, 1 } }
194 },
195 // Format_SamplerExternalOES
196 {
197 1, 0,
198 [](int, int) { return 0; },
200 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
201 },
202 // Format_Jpeg
203 { 1, 4,
204 [](int stride, int height) { return stride*height; },
206 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
207 },
208 // Format_SamplerRect
209 {
210 1, 0,
211 [](int, int) { return 0; },
213 { { 1, 1 }, { 1, 1 }, { 1, 1 } }
214 },
215 // Format_YUV420P10
216 { 3, 2,
217 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
219 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
220 },
221};
222
227
229{
230 auto fmt = format.pixelFormat();
231 Q_UNUSED(fmt);
232
233#if 1//def Q_OS_ANDROID
235 return QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb");
236#endif
237#if 1//def Q_OS_MACOS
239 return QStringLiteral(":/qt-project.org/multimedia/shaders/rectsampler.vert.qsb");
240#endif
241
242 return QStringLiteral(":/qt-project.org/multimedia/shaders/vertex.vert.qsb");
243}
244
246{
247 const char *shader = nullptr;
248 switch (format.pixelFormat()) {
251 shader = "y";
252 break;
255 shader = "ayuv";
256 break;
260 shader = "argb";
261 break;
264 shader = "abgr";
265 break;
266 case QVideoFrameFormat::Format_Jpeg: // Jpeg is decoded transparently into an ARGB texture
267 shader = "bgra";
268 break;
274 shader = "rgba";
275 break;
279 shader = "yuv_triplanar";
280 break;
282 shader = "yuv_triplanar_p10";
283 break;
286 shader = "yvu_triplanar";
287 break;
289 shader = "imc2";
290 break;
292 shader = "imc4";
293 break;
295 shader = "uyvy";
296 break;
298 shader = "yuyv";
299 break;
302 // P010/P016 have the same layout as NV12, just 16 instead of 8 bits per pixel
303 if (format.colorTransfer() == QVideoFrameFormat::ColorTransfer_ST2084) {
304 shader = "nv12_bt2020_pq";
305 break;
306 }
307 if (format.colorTransfer() == QVideoFrameFormat::ColorTransfer_STD_B67) {
308 shader = "nv12_bt2020_hlg";
309 break;
310 }
311 // Fall through, should be bt709
314 shader = "nv12";
315 break;
317 shader = "nv21";
318 break;
320#if 1//def Q_OS_ANDROID
321 shader = "externalsampler";
322 break;
323#endif
325#if 1//def Q_OS_MACOS
326 shader = "rectsampler_bgra";
327 break;
328#endif
329 // fallthrough
331 default:
332 break;
333 }
334 if (!shader)
335 return QString();
336 QString shaderFile = QStringLiteral(":/qt-project.org/multimedia/shaders/") + QString::fromLatin1(shader);
337 if (surfaceFormat == QRhiSwapChain::HDRExtendedSrgbLinear)
338 shaderFile += QLatin1String("_linear");
339 shaderFile += QStringLiteral(".frag.qsb");
340 return shaderFile;
341}
342
343// Matrices are calculated from
344// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf
345// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf
346// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-2-201510-I!!PDF-E.pdf
347//
348// For BT2020, we also need to convert the Rec2020 RGB colorspace to sRGB see
349// shaders/colorconvert.glsl for details.
350//
351// Doing the math gives the following (Y, U & V normalized to [0..1] range):
352//
353// Y = a*R + b*G + c*B
354// R = Y + e*V
355// G = Y - c*d/b*U - a*e/b*V
356// B = Y + d*U
357
358// BT2020:
359// a = .2627, b = 0.6780, c = 0.0593
360// d = 1.8814
361// e = 1.4746
362//
363// BT709:
364// a = 0.2126, b = 0.7152, c = 0.0722
365// d = 1.8556
366// e = 1.5748
367//
368// BT601:
369// a = 0.299, b = 0.578, c = 0.114
370// d = 1.42
371// e = 1.772
372//
373
374// clang-format off
376{
377 auto colorSpace = format.colorSpace();
378 if (colorSpace == QVideoFrameFormat::ColorSpace_Undefined) {
379 if (format.frameHeight() > 576)
380 // HD video, assume BT709
382 else
383 // SD video, assume BT601
385 }
386 switch (colorSpace) {
388 return {
389 1.0f, 0.000f, 1.402f, -0.701f,
390 1.0f, -0.344f, -0.714f, 0.529f,
391 1.0f, 1.772f, 0.000f, -0.886f,
392 0.0f, 0.000f, 0.000f, 1.000f
393 };
394 default:
396 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
397 return {
398 1.0f, 0.0f, 1.5748f, -0.790488f,
399 1.0f, -0.187324f, -0.468124f, 0.329010f,
400 1.0f, 1.855600f, 0.0f, -0.931439f,
401 0.0f, 0.0f, 0.0f, 1.0f
402 };
403 return {
404 1.1644f, 0.0000f, 1.7927f, -0.9729f,
405 1.1644f, -0.2132f, -0.5329f, 0.3015f,
406 1.1644f, 2.1124f, 0.0000f, -1.1334f,
407 0.0000f, 0.0000f, 0.0000f, 1.0000f
408 };
410 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
411 return {
412 1.f, 0.0000f, 1.4746f, -0.7402f,
413 1.f, -0.1646f, -0.5714f, 0.3694f,
414 1.f, 1.8814f, 0.000f, -0.9445f,
415 0.0f, 0.0000f, 0.000f, 1.0000f
416 };
417 return {
418 1.1644f, 0.000f, 1.6787f, -0.9157f,
419 1.1644f, -0.1874f, -0.6504f, 0.3475f,
420 1.1644f, 2.1418f, 0.0000f, -1.1483f,
421 0.0000f, 0.0000f, 0.0000f, 1.0000f
422 };
424 // Corresponds to the primaries used by NTSC BT601. For PAL BT601, we use the BT709 conversion
425 // as those are very close.
426 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
427 return {
428 1.f, 0.000f, 1.772f, -0.886f,
429 1.f, -0.1646f, -0.57135f, 0.36795f,
430 1.f, 1.42f, 0.000f, -0.71f,
431 0.0f, 0.000f, 0.000f, 1.0000f
432 };
433 return {
434 1.164f, 0.000f, 1.596f, -0.8708f,
435 1.164f, -0.392f, -0.813f, 0.5296f,
436 1.164f, 2.017f, 0.000f, -1.0810f,
437 0.000f, 0.000f, 0.000f, 1.0000f
438 };
439 }
440}
441// clang-format on
442
443// PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
444// or https://ieeexplore.ieee.org/document/7291452
445static float convertPQFromLinear(float sig)
446{
447 const float m1 = 1305.f/8192.f;
448 const float m2 = 2523.f/32.f;
449 const float c1 = 107.f/128.f;
450 const float c2 = 2413.f/128.f;
451 const float c3 = 2392.f/128.f;
452
453 const float SDR_LEVEL = 100.f;
454 sig *= SDR_LEVEL/10000.f;
455 float psig = powf(sig, m1);
456 float num = c1 + c2*psig;
457 float den = 1 + c3*psig;
458 return powf(num/den, m2);
459}
460
461float convertHLGFromLinear(float sig)
462{
463 const float a = 0.17883277f;
464 const float b = 0.28466892f; // = 1 - 4a
465 const float c = 0.55991073f; // = 0.5 - a ln(4a)
466
467 if (sig < 1.f/12.f)
468 return sqrtf(3.f*sig);
469 return a*logf(12.f*sig - b) + c;
470}
471
472static float convertSDRFromLinear(float sig)
473{
474 return sig;
475}
476
477void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity, float maxNits)
478{
479#ifndef Q_OS_ANDROID
481#endif
482
483 QMatrix4x4 cmat;
484 switch (format.pixelFormat()) {
486 return;
487
499
502 break;
519 cmat = colorMatrix(format);
520 break;
522 // get Android specific transform for the externalsampler texture
523 cmat = frame.videoBuffer()->externalTextureMatrix();
524 break;
526 {
527 // Similarly to SamplerExternalOES, the "color matrix" is used here to
528 // transform the texture coordinates. OpenGL texture rectangles expect
529 // non-normalized UVs, so apply a scale to have the fragment shader see
530 // UVs in range [width,height] instead of [0,1].
531 const QSize videoSize = frame.size();
532 cmat.scale(videoSize.width(), videoSize.height());
533 }
534 break;
535 }
536
537 // HDR with a PQ or HLG transfer function uses a BT2390 based tone mapping to cut off the HDR peaks
538 // This requires that we pass the max luminance the tonemapper should clip to over to the fragment
539 // shader. To reduce computations there, it's precomputed in PQ values here.
540 auto fromLinear = convertSDRFromLinear;
541 switch (format.colorTransfer()) {
543 fromLinear = convertPQFromLinear;
544 break;
546 fromLinear = convertHLGFromLinear;
547 break;
548 default:
549 break;
550 }
551
552 if (dst->size() < qsizetype(sizeof(UniformData)))
553 dst->resize(sizeof(UniformData));
554
555 auto ud = reinterpret_cast<UniformData*>(dst->data());
556 memcpy(ud->transformMatrix, transform.constData(), sizeof(ud->transformMatrix));
557 memcpy(ud->colorMatrix, cmat.constData(), sizeof(ud->transformMatrix));
558 ud->opacity = opacity;
559 ud->width = float(format.frameWidth());
560 ud->masteringWhite = fromLinear(float(format.maxLuminance())/100.f);
561 ud->maxLum = fromLinear(float(maxNits)/100.f);
562}
563
569
571 QRhiResourceUpdateBatch *rub, int plane,
572 std::unique_ptr<QRhiTexture> &tex)
573{
574 Q_ASSERT(frame.isMapped());
575
576 QVideoFrameFormat fmt = frame.surfaceFormat();
577 QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
578 QSize size = fmt.frameSize();
579
580 const TextureDescription &texDesc = descriptions[pixelFormat];
581 QSize planeSize(size.width()/texDesc.sizeScale[plane].x, size.height()/texDesc.sizeScale[plane].y);
582
583 bool needsRebuild = !tex || tex->pixelSize() != planeSize || tex->format() != texDesc.textureFormat[plane];
584 if (!tex) {
585 tex.reset(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, {}));
586 if (!tex) {
587 qWarning("Failed to create new texture (size %dx%d)", planeSize.width(), planeSize.height());
589 }
590 }
591
592 if (needsRebuild) {
593 tex->setFormat(texDesc.textureFormat[plane]);
594 tex->setPixelSize(planeSize);
595 if (!tex->create()) {
596 qWarning("Failed to create texture (size %dx%d)", planeSize.width(), planeSize.height());
598 }
599 }
600
602
604
605 if (pixelFormat == QVideoFrameFormat::Format_Jpeg) {
606 Q_ASSERT(plane == 0);
607
609
610 // calling QVideoFrame::toImage is not accurate. To be fixed.
611 image = frame.toImage();
612 image.convertTo(QImage::Format_ARGB32);
613 subresDesc.setImage(image);
614
615 } else {
616 // Note, QByteArray::fromRawData creare QByteArray as a view without data copying
618 reinterpret_cast<const char *>(frame.bits(plane)), frame.mappedBytes(plane)));
619 subresDesc.setDataStride(frame.bytesPerLine(plane));
621 }
622
623 QRhiTextureUploadEntry entry(0, 0, subresDesc);
625 rub->uploadTexture(tex.get(), desc);
626
627 return result;
628}
629
630static std::unique_ptr<QRhiTexture> createTextureFromHandle(const QVideoFrame &frame, QRhi *rhi, int plane)
631{
632 QVideoFrameFormat fmt = frame.surfaceFormat();
633 QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
634 QSize size = fmt.frameSize();
635
636 const TextureDescription &texDesc = descriptions[pixelFormat];
637 QSize planeSize(size.width()/texDesc.sizeScale[plane].x, size.height()/texDesc.sizeScale[plane].y);
638
639 QRhiTexture::Flags textureFlags = {};
641#ifdef Q_OS_ANDROID
642 if (rhi->backend() == QRhi::OpenGLES2)
643 textureFlags |= QRhiTexture::ExternalOES;
644#endif
645 }
646 if (pixelFormat == QVideoFrameFormat::Format_SamplerRect) {
647#ifdef Q_OS_MACOS
648 if (rhi->backend() == QRhi::OpenGLES2)
649 textureFlags |= QRhiTexture::TextureRectangleGL;
650#endif
651 }
652
653 if (quint64 handle = frame.videoBuffer()->textureHandle(rhi, plane); handle) {
654 std::unique_ptr<QRhiTexture> tex(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, textureFlags));
655 if (tex->createFrom({handle, 0}))
656 return tex;
657
658 qWarning("Failed to initialize QRhiTexture wrapper for native texture object %llu",handle);
659 }
660 return {};
661}
662
664{
665public:
666 using TextureArray = std::array<std::unique_ptr<QRhiTexture>, TextureDescription::maxPlanes>;
668 : m_textures(std::move(textures)), m_mappedFrame(std::move(mappedFrame))
669 {
670 Q_ASSERT(!m_mappedFrame.isValid() || m_mappedFrame.isReadable());
671 }
672
673 // We keep the source frame mapped during the target texture lifetime.
674 // Alternatively, we may use setting a custom image to QRhiTextureSubresourceUploadDescription,
675 // unsig videoFramePlaneAsImage, however, the OpenGL rendering pipeline in QRhi
676 // may keep QImage, and consequently the mapped QVideoFrame,
677 // even after the target texture is deleted: QTBUG-123174.
678 ~QVideoFrameTexturesArray() { m_mappedFrame.unmap(); }
679
680 QRhiTexture *texture(uint plane) const override
681 {
682 return plane < std::size(m_textures) ? m_textures[plane].get() : nullptr;
683 }
684
685 TextureArray takeTextures() { return std::move(m_textures); }
686
687private:
688 TextureArray m_textures;
689 QVideoFrame m_mappedFrame;
690};
691
692static std::unique_ptr<QVideoFrameTextures> createTexturesFromHandles(const QVideoFrame &frame, QRhi *rhi)
693{
694 const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
695 bool ok = true;
697 for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
699 ok &= bool(textures[plane]);
700 }
701 if (ok)
702 return std::make_unique<QVideoFrameTexturesArray>(std::move(textures));
703 else
704 return {};
705}
706
707static std::unique_ptr<QVideoFrameTextures> createTexturesFromMemory(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, QVideoFrameTextures *old)
708{
709 const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
711 auto oldArray = dynamic_cast<QVideoFrameTexturesArray *>(old);
712 if (oldArray)
713 textures = oldArray->takeTextures();
714
715 if (!frame.map(QVideoFrame::ReadOnly)) {
716 qWarning() << "Cannot map a video frame in ReadOnly mode!";
717 return {};
718 }
719
720 auto unmapFrameGuard = qScopeGuard([&frame] { frame.unmap(); });
721
722 bool shouldKeepMapping = false;
723 for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
724 const auto result = updateTextureWithMap(frame, rhi, rub, plane, textures[plane]);
726 return {};
727
729 shouldKeepMapping = true;
730 }
731
732 // as QVideoFrame::unmap does nothing with null frames, we just move the frame to the result
733 return std::make_unique<QVideoFrameTexturesArray>(
734 std::move(textures), shouldKeepMapping ? std::move(frame) : QVideoFrame());
735}
736
737std::unique_ptr<QVideoFrameTextures> createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr<QVideoFrameTextures> &&oldTextures)
738{
739 QAbstractVideoBuffer *vf = frame.videoBuffer();
740 if (!vf)
741 return {};
742
743 if (auto vft = vf->mapTextures(rhi))
744 return vft;
745
746 if (auto vft = createTexturesFromHandles(frame, rhi))
747 return vft;
748
749 return createTexturesFromMemory(frame, rhi, rub, oldTextures.get());
750}
751
753{
754 text.replace(QLatin1Char('\n'), QChar::LineSeparator);
755 if (layout.text() == text && videoSize == frameSize)
756 return false;
757
759 QFont font;
760 // 0.045 - based on this https://www.md-subs.com/saa-subtitle-font-size
761 qreal fontSize = frameSize.height() * 0.045;
763
765 if (text.isEmpty()) {
766 bounds = {};
767 return true;
768 }
772 option.setAlignment(Qt::AlignCenter);
774
776 int leading = metrics.leading();
777
778 qreal lineWidth = videoSize.width()*.9;
779 qreal margin = videoSize.width()*.05;
780 qreal height = 0;
781 qreal textWidth = 0;
783 while (1) {
785 if (!line.isValid())
786 break;
787
788 line.setLineWidth(lineWidth);
789 height += leading;
790 line.setPosition(QPointF(margin, height));
791 height += line.height();
792 textWidth = qMax(textWidth, line.naturalTextWidth());
793 }
795
796 // put subtitles vertically in lower part of the video but not stuck to the bottom
797 int bottomMargin = videoSize.height() / 20;
798 qreal y = videoSize.height() - bottomMargin - height;
800 textWidth += fontSize/4.;
801
802 bounds = QRectF((videoSize.width() - textWidth)/2., y, textWidth, height);
803 return true;
804}
805
807{
808 painter->save();
811
812 QColor bgColor = Qt::black;
813 bgColor.setAlpha(128);
814 painter->setBrush(bgColor);
817
819 range.start = 0;
820 range.length = layout.text().size();
821 range.format.setForeground(Qt::white);
822 layout.draw(painter, {}, { range });
823 painter->restore();
824}
825
827{
828 auto size = bounds.size().toSize();
829 if (size.isEmpty())
830 return QImage();
832 QColor bgColor = Qt::black;
833 bgColor.setAlpha(128);
834 img.fill(bgColor);
835
839 range.start = 0;
840 range.length = layout.text().size();
841 range.format.setForeground(Qt::white);
842 layout.draw(&painter, {}, { range });
843 return img;
844}
845
846}
847
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
void setAlpha(int alpha)
Sets the alpha of this color to alpha.
Definition qcolor.cpp:1481
\reentrant \inmodule QtGui
\reentrant
Definition qfont.h:22
void setPointSize(int)
Sets the point size to pointSize.
Definition qfont.cpp:985
\inmodule QtGui
Definition qimage.h:37
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_ARGB32
Definition qimage.h:47
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void drawRect(const QRectF &rect)
Draws the current rectangle with the current pen and brush.
Definition qpainter.h:519
void setPen(const QColor &color)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void restore()
Restores the current painter state (pops a saved state off the stack).
void setCompositionMode(CompositionMode mode)
Sets the composition mode to the given mode.
void save()
Saves the current painter state (pushes the state onto a stack).
void setBrush(const QBrush &brush)
Sets the painter's brush to the given brush.
@ CompositionMode_SourceOver
Definition qpainter.h:98
void translate(const QPointF &offset)
Translates the coordinate system by the given offset; i.e.
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:511
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:735
\inmodule QtGui
Definition qrhi.h:1731
Format
Describes the swapchain format.
Definition qrhi.h:1561
@ HDRExtendedSrgbLinear
Definition qrhi.h:1563
void setDataStride(quint32 stride)
Sets the data stride in bytes.
Definition qrhi.h:670
void setData(const QByteArray &data)
Sets data.
Definition qrhi.h:667
void setImage(const QImage &image)
Sets image.
Definition qrhi.h:664
\inmodule QtGui
Definition qrhi.h:716
\inmodule QtGui
Definition qrhi.h:693
\inmodule QtGui
Definition qrhi.h:895
@ TextureRectangleGL
Definition qrhi.h:908
@ ExternalOES
Definition qrhi.h:906
@ UnknownFormat
Definition qrhi.h:915
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
@ OpenGLES2
Definition qrhi.h:1809
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10562
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
void scale(int w, int h, Qt::AspectRatioMode mode) noexcept
Scales the size to a rectangle with the given width and height, according to the specified mode:
Definition qsize.h:145
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
void setFont(const QFont &f)
Sets the layout's font to the given font.
QTextLine createLine()
Returns a new text line to be laid out if there is text to be inserted into the layout; otherwise ret...
void beginLayout()
Begins the layout process.
void setPosition(const QPointF &p)
Moves the text layout to point p.
void setText(const QString &string)
Sets the layout's text to the given string.
QString text() const
Returns the layout's text.
void setTextOption(const QTextOption &option)
Sets the text option structure that controls the layout process to the given option.
void draw(QPainter *p, const QPointF &pos, const QList< FormatRange > &selections=QList< FormatRange >(), const QRectF &clip=QRectF()) const
Draws the whole layout on the painter p at the position specified by pos.
void endLayout()
Ends the layout process.
\reentrant
\reentrant
Definition qtextoption.h:18
void setUseDesignMetrics(bool b)
If enable is true then the layout will use design metrics; otherwise it will use the metrics of the p...
Definition qtextoption.h:91
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
static constexpr int NPixelFormats
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
void unmap()
Releases the memory mapped by the map() function.
bool isReadable() const
Identifies if the mapped contents of a video frame were read from the frame when it was mapped.
bool isValid() const
Identifies whether a video frame is valid.
std::array< std::unique_ptr< QRhiTexture >, TextureDescription::maxPlanes > TextureArray
QVideoFrameTexturesArray(TextureArray &&textures, QVideoFrame mappedFrame={})
QRhiTexture * texture(uint plane) const override
QSize size
the size of the widget excluding any window frame
Definition qwidget.h:113
QString text
Combined button and popup list for selecting options.
float convertHLGFromLinear(float sig)
QString vertexShaderFileName(const QVideoFrameFormat &format)
static QMatrix4x4 colorMatrix(const QVideoFrameFormat &format)
static float convertPQFromLinear(float sig)
static UpdateTextureWithMapResult updateTextureWithMap(const QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr< QRhiTexture > &tex)
static float convertSDRFromLinear(float sig)
QString fragmentShaderFileName(const QVideoFrameFormat &format, QRhiSwapChain::Format surfaceFormat)
static const TextureDescription descriptions[QVideoFrameFormat::NPixelFormats]
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
static std::unique_ptr< QRhiTexture > createTextureFromHandle(const QVideoFrame &frame, QRhi *rhi, int plane)
static std::unique_ptr< QVideoFrameTextures > createTexturesFromMemory(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, QVideoFrameTextures *old)
std::unique_ptr< QVideoFrameTextures > createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr< QVideoFrameTextures > &&oldTextures)
void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity, float maxNits)
static std::unique_ptr< QVideoFrameTextures > createTexturesFromHandles(const QVideoFrame &frame, QRhi *rhi)
@ AlignCenter
Definition qnamespace.h:163
@ white
Definition qnamespace.h:31
@ black
Definition qnamespace.h:30
@ NoPen
Definition image.cpp:4
#define Q_FALLTHROUGH()
#define qWarning
Definition qlogging.h:166
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint const GLuint GLuint const GLuint * textures
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLsizei range
const void GLsizei GLsizei stride
GLenum GLenum dst
GLenum GLsizeiptr fontSize
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint entry
GLuint shader
Definition qopenglext.h:665
GLuint64EXT * result
[6]
GLuint GLenum option
GLuint num
static constexpr QSize frameSize(const T &frame)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
unsigned char quint8
Definition qtypes.h:46
QVideoFrameFormat::PixelFormat fmt
static bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent, int *x, int *y)
MyCustomStruct c2
QPainter painter(this)
[7]
QFrame frame
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
bool update(const QSize &frameSize, QString text)
void draw(QPainter *painter, const QPointF &translate) const