1// Copyright (C) 2016 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
4#include "qmultimediautils_p.h"
5#include "qvideoframe.h"
6#include "qvideoframeformat.h"
7
8#include <QtCore/qdir.h>
9#include <QtCore/qloggingcategory.h>
10
11#include <cmath>
12
13QT_BEGIN_NAMESPACE
14
15static Q_LOGGING_CATEGORY(qLcMultimediaUtils, "qt.multimedia.utils");
16
17Fraction qRealToFraction(qreal value)
18{
19 int integral = int(floor(x: value));
20 value -= qreal(integral);
21 if (value == 0.)
22 return {.numerator: integral, .denominator: 1};
23
24 const int dMax = 1000;
25 int n1 = 0, d1 = 1, n2 = 1, d2 = 1;
26 qreal mid = 0.;
27 while (d1 <= dMax && d2 <= dMax) {
28 mid = qreal(n1 + n2) / (d1 + d2);
29
30 if (qAbs(t: value - mid) < 0.000001) {
31 break;
32 } else if (value > mid) {
33 n1 = n1 + n2;
34 d1 = d1 + d2;
35 } else {
36 n2 = n1 + n2;
37 d2 = d1 + d2;
38 }
39 }
40
41 if (d1 + d2 <= dMax)
42 return {.numerator: n1 + n2 + integral * (d1 + d2), .denominator: d1 + d2};
43 else if (d2 < d1)
44 return { .numerator: n2 + integral * d2, .denominator: d2 };
45 else
46 return { .numerator: n1 + integral * d1, .denominator: d1 };
47}
48
49QSize qCalculateFrameSize(QSize resolution, Fraction par)
50{
51 if (par.numerator == par.denominator || par.numerator < 1 || par.denominator < 1)
52 return resolution;
53
54 if (par.numerator > par.denominator)
55 return { resolution.width() * par.numerator / par.denominator, resolution.height() };
56
57 return { resolution.width(), resolution.height() * par.denominator / par.numerator };
58}
59
60QSize qRotatedFrameSize(QSize size, int rotation)
61{
62 Q_ASSERT(rotation % 90 == 0);
63 return rotation % 180 ? size.transposed() : size;
64}
65
66QSize qRotatedFramePresentationSize(const QVideoFrame &frame)
67{
68 // For mirrored frames the rotation can be +/- 180 degrees,
69 // but this inaccuracy doesn't impact on the result.
70 const int rotation = qToUnderlying(e: frame.rotation()) + qToUnderlying(e: frame.surfaceFormat().rotation());
71 return qRotatedFrameSize(size: frame.size(), rotation);
72}
73
74QUrl qMediaFromUserInput(QUrl url)
75{
76 return QUrl::fromUserInput(userInput: url.toString(), workingDirectory: QDir::currentPath(), options: QUrl::AssumeLocalFile);
77}
78
79bool qIsAutoHdrEnabled()
80{
81 static const bool autoHdrEnabled = qEnvironmentVariableIntValue(varName: "QT_MEDIA_AUTO_HDR");
82
83 return autoHdrEnabled;
84}
85
86QRhiSwapChain::Format qGetRequiredSwapChainFormat(const QVideoFrameFormat &format)
87{
88 constexpr auto sdrMaxLuminance = 100.0f;
89 const auto formatMaxLuminance = format.maxLuminance();
90
91 return formatMaxLuminance > sdrMaxLuminance ? QRhiSwapChain::HDRExtendedSrgbLinear
92 : QRhiSwapChain::SDR;
93}
94
95bool qShouldUpdateSwapChainFormat(QRhiSwapChain *swapChain,
96 QRhiSwapChain::Format requiredSwapChainFormat)
97{
98 if (!swapChain)
99 return false;
100
101 return qIsAutoHdrEnabled() && swapChain->format() != requiredSwapChainFormat
102 && swapChain->isFormatSupported(f: requiredSwapChainFormat);
103}
104
105Q_MULTIMEDIA_EXPORT VideoTransformation
106qNormalizedSurfaceTransformation(const QVideoFrameFormat &format)
107{
108 VideoTransformation result;
109 result.mirrorVertically(mirror: format.scanLineDirection() == QVideoFrameFormat::BottomToTop);
110 result.rotate(rotation: format.rotation());
111 result.mirrorHorizontally(mirror: format.isMirrored());
112 return result;
113}
114
115VideoTransformation qNormalizedFrameTransformation(const QVideoFrame &frame, int additionalRotaton)
116{
117 VideoTransformation result = qNormalizedSurfaceTransformation(format: frame.surfaceFormat());
118 result.rotate(rotation: frame.rotation());
119 result.mirrorHorizontally(mirror: frame.mirrored());
120 result.rotate(rotation: qVideoRotationFromDegrees(clockwiseDegrees: additionalRotaton));
121 return result;
122}
123
124// Only accepts inputs divisible by 90.
125// Invalid input returns no rotation.
126QtVideo::Rotation qVideoRotationFromDegrees(int clockwiseDegrees)
127{
128 if (clockwiseDegrees % 90 != 0) {
129 qCWarning(qLcMultimediaUtils) << "qVideoRotationFromAngle(int) received "
130 "input not divisible by 90. Input was: "
131 << clockwiseDegrees;
132 return QtVideo::Rotation::None;
133 }
134
135 int newDegrees = clockwiseDegrees % 360;
136 // Adjust negative rotations into positive ones.
137 if (newDegrees < 0)
138 newDegrees += 360;
139 return static_cast<QtVideo::Rotation>(newDegrees);
140}
141
142VideoTransformationOpt qVideoTransformationFromMatrix(const QTransform &matrix)
143{
144 const qreal absScaleX = std::hypot(x: matrix.m11(), y: matrix.m12());
145 const qreal absScaleY = std::hypot(x: matrix.m21(), y: matrix.m22());
146
147 if (qFuzzyIsNull(d: absScaleX) || qFuzzyIsNull(d: absScaleY))
148 return {}; // the matrix is malformed
149
150 qreal cos1 = matrix.m11() / absScaleX;
151 qreal sin1 = matrix.m12() / absScaleX;
152
153 // const: consider yScale > 0, as negative yScale can be compensated via negative xScale
154 const qreal sin2 = -matrix.m21() / absScaleY;
155 const qreal cos2 = matrix.m22() / absScaleY;
156
157 VideoTransformation result;
158
159 // try detecting the best pair option to detect mirroring
160
161 if (std::abs(x: cos1) + std::abs(x: cos2) > std::abs(x: sin1) + std::abs(x: sin2))
162 result.mirrorredHorizontallyAfterRotation = std::signbit(x: cos1) != std::signbit(x: cos2);
163 else
164 result.mirrorredHorizontallyAfterRotation = std::signbit(x: sin1) != std::signbit(x: sin2);
165
166 if (result.mirrorredHorizontallyAfterRotation) {
167 cos1 *= -1;
168 sin1 *= -1;
169 }
170
171 const qreal maxDiscrepancy = 0.2;
172
173 if (std::abs(x: cos1 - cos2) > maxDiscrepancy || std::abs(x: sin1 - sin2) > maxDiscrepancy)
174 return {}; // the matrix is sheared too much, this is not supported currently
175
176 const qreal angle = atan2(y: sin1 + sin2, x: cos1 + cos2);
177 Q_ASSERT(!std::isnan(angle)); // checked upon scale validation
178
179 result.rotation = qVideoRotationFromDegrees(clockwiseDegrees: qRound(d: angle / M_PI_2) * 90);
180 return result;
181}
182
183QT_END_NAMESPACE
184

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtmultimedia/src/multimedia/qmultimediautils.cpp