Qt 6.x
The Qt SDK
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
5#include "qvideoframe.h"
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 * 3 / 2 + 1) & ~1); },
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, 1,
217 [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
219 { { 1, 1 }, { 2, 2 }, { 2, 2 } }
220 },
221};
222
224{
225 return descriptions + format;
226}
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//
374{
375 auto colorSpace = format.colorSpace();
376 if (colorSpace == QVideoFrameFormat::ColorSpace_Undefined) {
377 if (format.frameHeight() > 576)
378 // HD video, assume BT709
380 else
381 // SD video, assume BT601
383 }
384 switch (colorSpace) {
386 return QMatrix4x4(
387 1.0f, 0.000f, 1.402f, -0.701f,
388 1.0f, -0.344f, -0.714f, 0.529f,
389 1.0f, 1.772f, 0.000f, -0.886f,
390 0.0f, 0.000f, 0.000f, 1.0000f);
391 default:
393 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
394 return QMatrix4x4(
395 1.f, 0.000f, 1.5748f, -0.8774f,
396 1.f, -0.187324f, -0.468124f, 0.327724f,
397 1.f, 1.8556f, 0.000f, -0.9278f,
398 0.0f, 0.000f, 0.000f, 1.0000f);
399 return QMatrix4x4(
400 1.1644f, 0.000f, 1.7928f, -0.9731f,
401 1.1644f, -0.2132f, -0.5329f, 0.3015f,
402 1.1644f, 2.1124f, 0.000f, -1.1335f,
403 0.0f, 0.000f, 0.000f, 1.0000f);
405 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
406 return QMatrix4x4(
407 1.f, 0.000f, 1.4746f, -0.7373f,
408 1.f, -0.2801f, -0.91666f, 0.5984f,
409 1.f, 1.8814f, 0.000f, -0.9407f,
410 0.0f, 0.000f, 0.000f, 1.0000f);
411 return QMatrix4x4(
412 1.1644f, 0.000f, 1.6787f, -0.9158f,
413 1.1644f, -0.1874f, -0.6511f, 0.3478f,
414 1.1644f, 2.1418f, 0.000f, -1.1483f,
415 0.0f, 0.000f, 0.000f, 1.0000f);
417 // Corresponds to the primaries used by NTSC BT601. For PAL BT601, we use the BT709 conversion
418 // as those are very close.
419 if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
420 return QMatrix4x4(
421 1.f, 0.000f, 1.772f, -0.886f,
422 1.f, -0.1646f, -0.57135f, 0.36795f,
423 1.f, 1.42f, 0.000f, -0.71f,
424 0.0f, 0.000f, 0.000f, 1.0000f);
425 return QMatrix4x4(
426 1.164f, 0.000f, 1.596f, -0.8708f,
427 1.164f, -0.392f, -0.813f, 0.5296f,
428 1.164f, 2.017f, 0.000f, -1.081f,
429 0.0f, 0.000f, 0.000f, 1.0000f);
430 }
431}
432
433#if 0
434static QMatrix4x4 yuvColorCorrectionMatrix(float brightness, float contrast, float hue, float saturation)
435{
436 // Color correction in YUV space is done as follows:
437
438 // The formulas assumes values in range 0-255, and a blackpoint of Y=16, whitepoint of Y=235
439 //
440 // Bightness: b
441 // Contrast: c
442 // Hue: h
443 // Saturation: s
444 //
445 // Y' = (Y - 16)*c + b + 16
446 // U' = ((U - 128)*cos(h) + (V - 128)*sin(h))*c*s + 128
447 // V' = ((V - 128)*cos(h) - (U - 128)*sin(h))*c*s + 128
448 //
449 // For normalized YUV values (0-1 range) as we have them in the pixel shader, this translates to:
450 //
451 // Y' = (Y - .0625)*c + b + .0625
452 // U' = ((U - .5)*cos(h) + (V - .5)*sin(h))*c*s + .5
453 // V' = ((V - .5)*cos(h) - (U - .5)*sin(h))*c*s + .5
454 //
455 // The values need to be clamped to 0-1 after the correction and before converting to RGB
456 // The transformation can be encoded in a 4x4 matrix assuming we have an A component of 1
457
458 float chcs = cos(hue)*contrast*saturation;
459 float shcs = sin(hue)*contrast*saturation;
460 return QMatrix4x4(contrast, 0, 0, .0625*(1 - contrast) + brightness,
461 0, chcs, shcs, .5*(1 - chcs - shcs),
462 0, -shcs, chcs, .5*(1 + shcs - chcs),
463 0, 0, 0, 1);
464}
465#endif
466
467// PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
468// or https://ieeexplore.ieee.org/document/7291452
469static float convertPQFromLinear(float sig)
470{
471 const float m1 = 1305.f/8192.f;
472 const float m2 = 2523.f/32.f;
473 const float c1 = 107.f/128.f;
474 const float c2 = 2413.f/128.f;
475 const float c3 = 2392.f/128.f;
476
477 const float SDR_LEVEL = 100.f;
478 sig *= SDR_LEVEL/10000.f;
479 float psig = powf(sig, m1);
480 float num = c1 + c2*psig;
481 float den = 1 + c3*psig;
482 return powf(num/den, m2);
483}
484
485float convertHLGFromLinear(float sig)
486{
487 const float a = 0.17883277f;
488 const float b = 0.28466892f; // = 1 - 4a
489 const float c = 0.55991073f; // = 0.5 - a ln(4a)
490
491 if (sig < 1.f/12.f)
492 return sqrtf(3.f*sig);
493 return a*logf(12.f*sig - b) + c;
494}
495
496static float convertSDRFromLinear(float sig)
497{
498 return sig;
499}
500
501void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity, float maxNits)
502{
503#ifndef Q_OS_ANDROID
505#endif
506
507 QMatrix4x4 cmat;
508 switch (format.pixelFormat()) {
510 return;
511
523
526 break;
543 cmat = colorMatrix(format);
544 break;
546 // get Android specific transform for the externalsampler texture
547 cmat = frame.videoBuffer()->externalTextureMatrix();
548 break;
550 {
551 // Similarly to SamplerExternalOES, the "color matrix" is used here to
552 // transform the texture coordinates. OpenGL texture rectangles expect
553 // non-normalized UVs, so apply a scale to have the fragment shader see
554 // UVs in range [width,height] instead of [0,1].
555 const QSize videoSize = frame.size();
556 cmat.scale(videoSize.width(), videoSize.height());
557 }
558 break;
559 }
560
561 // HDR with a PQ or HLG transfer function uses a BT2390 based tone mapping to cut off the HDR peaks
562 // This requires that we pass the max luminance the tonemapper should clip to over to the fragment
563 // shader. To reduce computations there, it's precomputed in PQ values here.
564 auto fromLinear = convertSDRFromLinear;
565 switch (format.colorTransfer()) {
567 fromLinear = convertPQFromLinear;
568 break;
570 fromLinear = convertHLGFromLinear;
571 break;
572 default:
573 break;
574 }
575
576 if (dst->size() < qsizetype(sizeof(UniformData)))
577 dst->resize(sizeof(UniformData));
578
579 auto ud = reinterpret_cast<UniformData*>(dst->data());
580 memcpy(ud->transformMatrix, transform.constData(), sizeof(ud->transformMatrix));
581 memcpy(ud->colorMatrix, cmat.constData(), sizeof(ud->transformMatrix));
582 ud->opacity = opacity;
583 ud->width = float(format.frameWidth());
584 ud->masteringWhite = fromLinear(float(format.maxLuminance())/100.f);
585 ud->maxLum = fromLinear(float(maxNits)/100.f);
586}
587
588static bool updateTextureWithMap(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr<QRhiTexture> &tex)
589{
590 if (!frame.map(QVideoFrame::ReadOnly)) {
591 qWarning() << "could not map data of QVideoFrame for upload";
592 return false;
593 }
594
595 QVideoFrameFormat fmt = frame.surfaceFormat();
596 QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
597 QSize size = fmt.frameSize();
598
599 const TextureDescription &texDesc = descriptions[pixelFormat];
600 QSize planeSize(size.width()/texDesc.sizeScale[plane].x, size.height()/texDesc.sizeScale[plane].y);
601
602 bool needsRebuild = !tex || tex->pixelSize() != planeSize || tex->format() != texDesc.textureFormat[plane];
603 if (!tex) {
604 tex.reset(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, {}));
605 if (!tex) {
606 qWarning("Failed to create new texture (size %dx%d)", planeSize.width(), planeSize.height());
607 return false;
608 }
609 }
610
611 if (needsRebuild) {
612 tex->setFormat(texDesc.textureFormat[plane]);
613 tex->setPixelSize(planeSize);
614 if (!tex->create()) {
615 qWarning("Failed to create texture (size %dx%d)", planeSize.width(), planeSize.height());
616 return false;
617 }
618 }
619
622 if (pixelFormat == QVideoFrameFormat::Format_Jpeg) {
623 image = frame.toImage();
624 image.convertTo(QImage::Format_ARGB32);
625 subresDesc.setData(QByteArray((const char *)image.bits(), image.bytesPerLine()*image.height()));
626 subresDesc.setDataStride(image.bytesPerLine());
627 } else {
628 subresDesc.setData(QByteArray::fromRawData((const char *)frame.bits(plane), frame.mappedBytes(plane)));
629 subresDesc.setDataStride(frame.bytesPerLine(plane));
630 }
631
632 QRhiTextureUploadEntry entry(0, 0, subresDesc);
634 rub->uploadTexture(tex.get(), desc);
635
636 return true;
637}
638
639static std::unique_ptr<QRhiTexture> createTextureFromHandle(const QVideoFrame &frame, QRhi *rhi, int plane)
640{
641 QVideoFrameFormat fmt = frame.surfaceFormat();
642 QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
643 QSize size = fmt.frameSize();
644
645 const TextureDescription &texDesc = descriptions[pixelFormat];
646 QSize planeSize(size.width()/texDesc.sizeScale[plane].x, size.height()/texDesc.sizeScale[plane].y);
647
648 QRhiTexture::Flags textureFlags = {};
650#ifdef Q_OS_ANDROID
651 if (rhi->backend() == QRhi::OpenGLES2)
652 textureFlags |= QRhiTexture::ExternalOES;
653#endif
654 }
655 if (pixelFormat == QVideoFrameFormat::Format_SamplerRect) {
656#ifdef Q_OS_MACOS
657 if (rhi->backend() == QRhi::OpenGLES2)
658 textureFlags |= QRhiTexture::TextureRectangleGL;
659#endif
660 }
661
662 if (quint64 handle = frame.videoBuffer()->textureHandle(plane); handle) {
663 std::unique_ptr<QRhiTexture> tex(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, textureFlags));
664 if (tex->createFrom({handle, 0}))
665 return tex;
666
667 qWarning("Failed to initialize QRhiTexture wrapper for native texture object %llu",handle);
668 }
669 return {};
670}
671
673{
674public:
675 using TextureArray = std::array<std::unique_ptr<QRhiTexture>, TextureDescription::maxPlanes>;
677 : m_textures(std::move(textures))
678 {}
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};
690
691static std::unique_ptr<QVideoFrameTextures> createTexturesFromHandles(const QVideoFrame &frame, QRhi *rhi)
692{
693 const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
694 bool ok = true;
696 for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
698 ok &= bool(textures[plane]);
699 }
700 if (ok)
701 return std::make_unique<QVideoFrameTexturesArray>(std::move(textures));
702 else
703 return {};
704}
705
706std::unique_ptr<QVideoFrameTextures> createTexturesFromMemory(const QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, QVideoFrameTextures *old)
707{
708 const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
710 auto oldArray = dynamic_cast<QVideoFrameTexturesArray *>(old);
711 if (oldArray)
712 textures = oldArray->takeTextures();
713
714 bool ok = true;
715 for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
716 ok &= updateTextureWithMap(frame, rhi, rub, plane, textures[plane]);
717 }
718 if (ok)
719 return std::make_unique<QVideoFrameTexturesArray>(std::move(textures));
720 else
721 return {};
722}
723
724std::unique_ptr<QVideoFrameTextures> createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr<QVideoFrameTextures> &&oldTextures)
725{
726 QAbstractVideoBuffer *vf = frame.videoBuffer();
727 if (!vf)
728 return {};
729
730 if (auto vft = vf->mapTextures(rhi))
731 return vft;
732
733 if (auto vft = createTexturesFromHandles(frame, rhi))
734 return vft;
735
736 return createTexturesFromMemory(frame, rhi, rub, oldTextures.get());
737}
738
740{
742 if (layout.text() == text && videoSize == frameSize)
743 return false;
744
746 QFont font;
747 // 0.045 - based on this https://www.md-subs.com/saa-subtitle-font-size
748 qreal fontSize = frameSize.height() * 0.045;
750
752 if (text.isEmpty()) {
753 bounds = {};
754 return true;
755 }
758 option.setUseDesignMetrics(true);
759 option.setAlignment(Qt::AlignCenter);
761
763 int leading = metrics.leading();
764
765 qreal lineWidth = videoSize.width()*.9;
766 qreal margin = videoSize.width()*.05;
767 qreal height = 0;
768 qreal textWidth = 0;
770 while (1) {
772 if (!line.isValid())
773 break;
774
775 line.setLineWidth(lineWidth);
776 height += leading;
777 line.setPosition(QPointF(margin, height));
778 height += line.height();
779 textWidth = qMax(textWidth, line.naturalTextWidth());
780 }
782
783 // put subtitles vertically in lower part of the video but not stuck to the bottom
784 int bottomMargin = videoSize.height() / 20;
785 qreal y = videoSize.height() - bottomMargin - height;
787 textWidth += fontSize/4.;
788
789 bounds = QRectF((videoSize.width() - textWidth)/2., y, textWidth, height);
790 return true;
791}
792
794{
795 painter->save();
798
799 QColor bgColor = Qt::black;
800 bgColor.setAlpha(128);
801 painter->setBrush(bgColor);
804
806 range.start = 0;
807 range.length = layout.text().size();
808 range.format.setForeground(Qt::white);
809 layout.draw(painter, {}, { range });
810 painter->restore();
811}
812
814{
815 auto size = bounds.size().toSize();
816 if (size.isEmpty())
817 return QImage();
819 QColor bgColor = Qt::black;
820 bgColor.setAlpha(128);
821 img.fill(bgColor);
822
826 range.start = 0;
827 range.length = layout.text().size();
828 range.format.setForeground(Qt::white);
829 layout.draw(&painter, {}, { range });
830 return img;
831}
832
833}
834
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
virtual std::unique_ptr< QVideoFrameTextures > mapTextures(QRhi *)
\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:394
@ LineSeparator
Definition qchar.h:64
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:20
void setPointSize(int)
Sets the point size to pointSize.
Definition qfont.cpp:970
\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
void scale(const QVector3D &vector)
Multiplies this matrix by another that scales coordinates by the components of vector.
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
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:214
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
\inmodule QtGui
Definition qrhi.h:1694
void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
Enqueues uploading the image data for one or more mip levels in one or more layers of the texture tex...
Definition qrhi.cpp:8681
Format
Describes the swapchain format.
Definition qrhi.h:1525
@ HDRExtendedSrgbLinear
Definition qrhi.h:1527
void setDataStride(quint32 stride)
Sets the data stride in bytes.
Definition qrhi.h:658
void setData(const QByteArray &data)
Sets data.
Definition qrhi.h:655
\inmodule QtGui
Definition qrhi.h:704
\inmodule QtGui
Definition qrhi.h:681
\inmodule QtGui
Definition qrhi.h:883
@ TextureRectangleGL
Definition qrhi.h:896
@ ExternalOES
Definition qrhi.h:894
@ UnknownFormat
Definition qrhi.h:903
\inmodule QtGui
Definition qrhi.h:1767
Implementation backend() const
Definition qrhi.cpp:8289
@ OpenGLES2
Definition qrhi.h:1772
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10133
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:390
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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
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:26
std::array< std::unique_ptr< QRhiTexture >, TextureDescription::maxPlanes > TextureArray
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)
static bool updateTextureWithMap(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr< QRhiTexture > &tex)
QString vertexShaderFileName(const QVideoFrameFormat &format)
static QMatrix4x4 colorMatrix(const QVideoFrameFormat &format)
static float convertPQFromLinear(float sig)
std::unique_ptr< QVideoFrameTextures > createTexturesFromMemory(const QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, QVideoFrameTextures *old)
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)
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:162
@ white
Definition qnamespace.h:30
@ black
Definition qnamespace.h:29
@ NoPen
Definition image.cpp:4
#define Q_FALLTHROUGH()
#define qWarning
Definition qlogging.h:162
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
GLuint GLenum option
GLuint num
static constexpr QSize frameSize(const T &frame)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
@ desc
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
double qreal
Definition qtypes.h:92
unsigned char quint8
Definition qtypes.h:41
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:17
bool update(const QSize &frameSize, QString text)
void draw(QPainter *painter, const QPointF &translate) const
QRhiTexture::Format textureFormat[maxPlanes]