31 int integral =
int(floor(value));
32 value -= qreal(integral);
36 const int dMax = 1000;
37 int n1 = 0, d1 = 1, n2 = 1, d2 = 1;
39 while (d1 <= dMax && d2 <= dMax) {
40 mid = qreal(n1 + n2) / (d1 + d2);
42 if (qAbs(value - mid) < 0.000001) {
44 }
else if (value > mid) {
54 return {n1 + n2 + integral * (d1 + d2), d1 + d2};
56 return { n2 + integral * d2, d2 };
58 return { n1 + integral * d1, d1 };
88 if (requested.isEmpty())
91 namespace ranges = QtMultimediaPrivate::ranges;
93 if (ranges::contains(supportedResolutions, requested))
96 const qreal requestedAspectRatio =
97 static_cast<qreal>(requested.width()) /
static_cast<qreal>(requested.height());
98 const auto aspectRatioDiff = [&](QSize size) {
99 return qAbs((
static_cast<qreal>(size.width()) /
static_cast<qreal>(size.height()))
100 - requestedAspectRatio);
102 const auto pixelCountDiff = [&](QSize size) {
103 return qAbs((
static_cast<qint64>(size.width()) * size.height())
104 - (
static_cast<qint64>(requested.width()) * requested.height()));
107 const auto *
const bestAspectRatio =
108 ranges::min_element(supportedResolutions, [&](QSize lhs, QSize rhs) {
109 return aspectRatioDiff(lhs) < aspectRatioDiff(rhs);
112 if (bestAspectRatio == supportedResolutions.end())
116 constexpr qreal aspectRatioTolerance = 0.01;
117 const qreal maxAspectRatioDiff = aspectRatioDiff(*bestAspectRatio) + aspectRatioTolerance;
119 QSize closestResolution;
120 for (
const QSize candidate : supportedResolutions) {
121 if (candidate.isEmpty())
123 if (aspectRatioDiff(candidate) > maxAspectRatioDiff)
125 if (!closestResolution.isValid()
126 || pixelCountDiff(candidate) < pixelCountDiff(closestResolution)) {
127 closestResolution = candidate;
131 return closestResolution;
166qNormalizedSurfaceTransformation(
const QVideoFrameFormat &format)
168 VideoTransformation result;
169 result.mirrorVertically(format.scanLineDirection() == QVideoFrameFormat::BottomToTop);
170 result.rotate(format.rotation());
171 result.mirrorHorizontally(format.isMirrored());
176 VideoTransformation videoOutputTransformation)
178 VideoTransformation result = qNormalizedSurfaceTransformation(frame.surfaceFormat());
179 result.rotate(frame.rotation());
180 result.mirrorHorizontally(frame.mirrored());
181 result.rotate(videoOutputTransformation.rotation);
182 result.mirrorHorizontally(videoOutputTransformation.mirroredHorizontallyAfterRotation);
206 const qreal absScaleX =
std::hypot(matrix.m11(), matrix.m12());
207 const qreal absScaleY =
std::hypot(matrix.m21(), matrix.m22());
209 if (qFuzzyIsNull(absScaleX) || qFuzzyIsNull(absScaleY))
212 qreal cos1 = matrix.m11() / absScaleX;
213 qreal sin1 = matrix.m12() / absScaleX;
216 const qreal sin2 = -matrix.m21() / absScaleY;
217 const qreal cos2 = matrix.m22() / absScaleY;
219 VideoTransformation result;
223 if (std::abs(cos1) + std::abs(cos2) > std::abs(sin1) + std::abs(sin2))
224 result.mirroredHorizontallyAfterRotation = std::signbit(cos1) != std::signbit(cos2);
226 result.mirroredHorizontallyAfterRotation = std::signbit(sin1) != std::signbit(sin2);
228 if (result.mirroredHorizontallyAfterRotation) {
233 const qreal maxDiscrepancy = 0.2;
235 if (
std::abs(cos1 - cos2) > maxDiscrepancy ||
std::abs(sin1 - sin2) > maxDiscrepancy)
238 const qreal angle = atan2(sin1 + sin2, cos1 + cos2);
239 Q_ASSERT(!
std::isnan(angle));
241 result.rotation = qVideoRotationFromDegrees(qRound(angle /
M_PI_2) * 90);
263#if QT_CONFIG(temporaryfile)
264 std::unique_ptr<QTemporaryFile> tempFile;
266 if constexpr (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Android) {
267 tempFile = std::unique_ptr<QTemporaryFile>(QTemporaryFile::createNativeFile(qrcFile));
269 return q23::unexpected(u"Failed to establish temporary file during playback"_s);
272 const QFileInfo mediaInfo(qrcUrl.path());
273 const QString targetDirPath = QDir::tempPath() + mediaInfo.path();
274 if (!QDir().mkpath(targetDirPath))
275 return q23::unexpected(
276 u"Could not create a temporary directory: %1"_s.arg(targetDirPath));
279 const QString baseName = mediaInfo.completeBaseName() + QLatin1Char(
'_')
280 + QUuid::createUuid().toString(QUuid::WithoutBraces);
283 const QString suffix = mediaInfo.suffix();
285 const QString newName = targetDirPath + QLatin1Char(
'/') + baseName
286 + (suffix.isEmpty() ? QString() : QLatin1Char(
'.') + suffix);
288 if (!tempFile->rename(newName))
289 return q23::unexpected(u"Could not rename temporary file to: %1"_s.arg(newName));
291 tempFile = std::make_unique<QTemporaryFile>();
295 const QString suffix = QFileInfo(qrcFile).suffix();
296 if (!suffix.isEmpty())
297 tempFile->setFileTemplate(tempFile->fileTemplate() + QLatin1Char(
'.') + suffix);
299 if (!tempFile->open())
300 return q23::unexpected(tempFile->errorString());
305 qint64 len = qrcFile.read(buffer,
sizeof(buffer));
308 tempFile->write(buffer, len);
313 QUrl url = QUrl::fromLocalFile(tempFile->fileName());
319 return q23::unexpected(u"Qt was built with -no-feature-temporaryfile: playback from "
320 "resource file is not supported!"_s);