Compare commits

...
Author SHA1 Message Date
Cameron Gutman ac296864ac Fix some error handling in the libplacebo renderer 2026-10-24 22:41:06 -05:00
Cameron Gutman 6fc7e81683 Handle H.264 SPS read/write failure in fixup path 2026-10-24 21:26:32 -05:00
Cameron Gutman 9fee675c91 Fix controller number check for adaptive triggers 2026-10-24 20:27:07 -05:00
Cameron Gutman d606709d83 Add some missing error handling 2026-10-24 19:54:47 -05:00
Cameron Gutman ba700d67ec Fix shadowed member variable with base class 2026-10-24 19:54:47 -05:00
Cameron Gutman 465c41b5c0 Fix uninitialized InitFailureReason field 2026-10-24 19:54:47 -05:00
Cameron Gutman 29a280eb38 Fix return type of EGLRenderer::loadAndBuildShader() 2026-10-24 19:46:14 -05:00
Cameron Gutman fb9daf85ab Keep the VBO alive while the VAO references it 2026-10-24 19:39:58 -05:00
Cameron Gutman df7db4018e Fix some EGL error handling paths 2026-10-24 19:39:58 -05:00
Cameron Gutman bfc6751f0f Fix fd leak in VAAPI KMSDRM codepath 2026-10-24 19:39:58 -05:00
Cameron Gutman 2b690d14ad Initialize VAAPIRenderer class members 2026-10-24 19:39:47 -05:00
Cameron Gutman 4158fa7823 Don't add EGL modifiers for VA images if they aren't set 2026-10-24 18:49:46 -05:00
Cameron Gutman 372887b8a2 Don't copy unpopulated EGLAttribs 2026-10-24 18:49:46 -05:00
Cameron Gutman 0c4f7aefb6 Handle unexpected appVersion/gfeVersion quads 2026-10-24 18:49:46 -05:00
Cameron Gutman 1826d4c232 Fix incorrect mmap() failure check 2026-10-24 18:49:46 -05:00
Cameron Gutman 40f83bd294 Avoid wedging VDPAU renderer if mixer creation fails 2026-10-24 18:49:46 -05:00
Chris 8f2e52e540 Refuse to open a pre-existing log file (#2068)
On macOS, release builds log to /tmp/Moonlight-<epoch>.log. /tmp is
shared by all users and the file name is predictable, so another local
user could plant symlinks at upcoming log names and have Moonlight
truncate and overwrite any file the victim can write to. The log was
also created world-readable.

Open the log with QIODevice::NewOnly (O_CREAT|O_EXCL) so an existing
file or symlink is never opened, and create it readable only by the
owner on Qt 6.3+. If the open fails we keep logging to stderr, as we
already did for any other open failure.
2026-10-24 18:19:58 -05:00
Chris c8474c515c Avoid signed overflow in bs_read_se() (#2071)
bs_read_ue() can return values above INT32_MAX. bs_read_se() stored the
result in an int32_t and computed (r+1)/2, which is undefined behavior
when the code is 0x7FFFFFFF and gives wrong values for codes above
INT32_MAX. The SPS being parsed comes from the host during the H.264
SPS fixup.

Do the conversion in unsigned arithmetic. bs_read_ue() returns at most
0xFFFFFFFE, so both branches produce in-range int32_t values, and every
code that was previously well-defined decodes to the same value.
2026-10-24 18:17:00 -05:00
Cameron Gutman 781b26fd1c Completely disable connection caching on Qt 6.12+ 2026-10-22 23:01:46 -05:00
Cameron Gutman 74173b26eb Enforce a TCP/TLS HTTP timeout on Qt 6.3+
Timeouts for long requests like launch or resume timed the entire
HTTP operation. If the host was no longer reachable, we could
spend 2 minutes sitting there, even though we hadn't even made
successful contact with the host to give it our request yet.

Now we will time out a request if we can't send it in 5 seconds,
regardless of how long we want to wait for the server to respond.
2026-10-22 22:54:32 -05:00
Cameron Gutman b709bf5402 Handle musl headers without __NR_riscv_hwprobe defined 2026-10-22 22:14:04 -05:00
Cameron Gutman d79536b56d Only accept usable bounds if they differ from the full bounds
Some platforms (Wayland) just report the full display area, so we
incorrectly assume the entire display is usable for a window.

Fixes #2056
2026-10-22 20:53:30 -05:00
Cameron Gutman 74020b537b Don't start polling from the GUI for a CLI launch
We can end up missing early computerStateChanged signals.

Fixes #2039
2026-10-22 19:28:12 -05:00
Cameron Gutman dc92b6d2f3 Update moonlight-common-c 2026-10-20 00:35:04 -05:00
Cameron Gutman 9af0e9f539 Improve robustness and error handling of DXVsyncSource 2026-10-20 00:29:25 -05:00
Cameron Gutman e4db5c2c18 Check for pacing queue emptiness before dequeuing
This shouldn't actually be reachable when the queue
is empty, but we can check anyway to be safe.

Ref: #2054
2026-10-19 00:25:00 -05:00
Cameron Gutman 9b8886a6f5 Fix integer truncation warning on MSVC 2026-10-19 00:15:26 -05:00
Cameron Gutman f018a71a8a Override NSApplicationUpdateCycleEnabled to fix reduced mouse polling rate
Apps built with the macOS 26+ SDK default to an event loop synchronized to the display which caps input polling rates.

We have to use the plist for the opt-out, since NSApp is already set up by Qt before SDL can set this default.

Fixes #2036
2026-10-17 14:06:10 -05:00
Cameron Gutman b0af84ead1 Deploy the OpenSSL TLS backend for Qt
It supports TLS 1.3 and PQ-KEX back to Windows 10, unlike Schannel
2026-10-14 02:01:01 -05:00
Cameron Gutman 289782c6f3 Fix possible UB shift in bs_write_u() for large values of n 2026-10-14 01:51:15 -05:00
Cameron Gutman ee557e83c6 Include the trailing 16 bytes in the challengeResponseData check 2026-10-14 01:49:44 -05:00
Cameron Gutman 2bd4b9f721 Add missing checks for X509 parsing failures 2026-10-14 01:39:00 -05:00
Cameron Gutman fa6ae80940 Clamp additional values that can be used as array indices
These aren't exploitable in moonlight due to clamping done on the read side.
2026-10-14 01:09:54 -05:00
Cameron Gutman 71c3191e0c Remove warn_off for h264bitstream 2026-10-12 02:31:48 -05:00
Cameron Gutman a614924502 Don't hardcode the QSslKey algorithm 2026-10-12 02:31:21 -05:00
Cameron Gutman a57e947d71 Re-add the h264bitstream file headers mistakenly removed during vendoring 2026-10-07 20:19:06 -05:00
34 changed files with 462 additions and 160 deletions

No files matched your search

+2
View File
@@ -8,6 +8,8 @@
<string>Moonlight</string> <string>Moonlight</string>
<key>LSSupportsGameMode</key> <key>LSSupportsGameMode</key>
<true/> <true/>
<key>NSApplicationUpdateCycleEnabled</key>
<false/>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>Stream games and other applications from another PC</string> <string>Stream games and other applications from another PC</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
+1 -1
View File
@@ -186,7 +186,7 @@ IdentityManager::getSslKey()
BUF_MEM* mem; BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem); BIO_get_mem_ptr(bio, &mem);
m_CachedSslKey = QSslKey(QByteArray(mem->data, (int)mem->length), QSsl::Rsa); m_CachedSslKey = QSslKey(QByteArray(mem->data, (int)mem->length), getSslCertificate().publicKey().algorithm());
BIO_free(bio); BIO_free(bio);
EVP_PKEY_free(pk); EVP_PKEY_free(pk);
+38 -4
View File
@@ -507,23 +507,47 @@ NvHTTP::openConnection(QUrl baseUrl,
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false); request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
#endif #endif
#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
// Use fine-grained idle timeouts to avoid calling QNetworkAccessManager::clearAccessCache(), // Use fine-grained idle timeouts to avoid calling QNetworkAccessManager::clearAccessCache(),
// which tears down the NAM's global thread each time. We must not keep persistent connections // which tears down the NAM's global thread each time. We must not keep persistent connections
// or GFE will puke. // or GFE will puke.
#if QT_VERSION >= QT_VERSION_CHECK(6, 12, 0)
request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute, -1);
#elif QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute, 0); request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute, 0);
#endif #endif
auto sslErrorsConnection = connect(m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors); auto sslErrorsConnection = connect(m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors);
QNetworkReply* reply = m_Nam->get(request); QNetworkReply* reply = m_Nam->get(request);
// Run the request with a timeout if requested
QEventLoop loop; QEventLoop loop;
// Connect the quit signals
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit);
// Enforce a transfer timeout if requested
if (timeoutMs) { if (timeoutMs) {
QTimer::singleShot(timeoutMs, &loop, &QEventLoop::quit); QTimer::singleShot(timeoutMs, &loop, &QEventLoop::quit);
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
// Time the request to distinguish a slow response from an unreachable host
auto* connectTimer = new QTimer(reply);
connectTimer->setSingleShot(true);
connect(connectTimer, &QTimer::timeout, reply, [reply, logLevel, url]() {
if (!reply->isFinished()) {
if (logLevel >= NvLogLevel::NVLL_ERROR) {
qWarning() << "Aborting timed out connection for" << url.toString();
}
reply->setProperty("timeout_reason", "connection_timeout");
reply->abort();
}
});
connect(reply, &QNetworkReply::requestSent, connectTimer, &QTimer::stop);
connectTimer->start(REQUEST_TIMEOUT_MS);
#endif
if (logLevel >= NvLogLevel::NVLL_VERBOSE) { if (logLevel >= NvLogLevel::NVLL_VERBOSE) {
qInfo() << "Executing request:" << url.toString(); qInfo() << "Executing request:" << url.toString();
} }
@@ -559,7 +583,11 @@ NvHTTP::openConnection(QUrl baseUrl,
throw exception; throw exception;
} }
else if (reply->error() == QNetworkReply::OperationCanceledError) { else if (reply->error() == QNetworkReply::OperationCanceledError) {
QtNetworkReplyException exception(QNetworkReply::TimeoutError, "Request timed out"); QString reason = reply->property("timeout_reason").toString();
QtNetworkReplyException exception(QNetworkReply::TimeoutError,
reason == "connection_timeout" ?
"Connection timed out" :
"Request timed out");
delete reply; delete reply;
throw exception; throw exception;
} }
@@ -570,5 +598,11 @@ NvHTTP::openConnection(QUrl baseUrl,
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
// The requestSent signal must have stopped the timer for any successful request,
// otherwise we could falsely time out a slow response when we connected fine.
Q_ASSERT(!connectTimer->isActive());
#endif
return reply; return reply;
} }
+12 -3
View File
@@ -140,6 +140,9 @@ NvPairingManager::getSignatureFromPemCert(const QByteArray& certificate)
X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
BIO_free_all(bio); BIO_free_all(bio);
if (cert == nullptr) {
return QByteArray();
}
QByteArray signature = getSignatureFromCert(cert); QByteArray signature = getSignatureFromCert(cert);
X509_free(cert); X509_free(cert);
@@ -159,9 +162,15 @@ NvPairingManager::verifySignature(const QByteArray& data, const QByteArray& sign
X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
BIO_free_all(bio); BIO_free_all(bio);
if (cert == nullptr) {
return false;
}
EVP_PKEY* pubKey = X509_get_pubkey(cert); EVP_PKEY* pubKey = X509_get_pubkey(cert);
THROW_BAD_ALLOC_IF_NULL(pubKey); if (pubKey == nullptr) {
X509_free(cert);
return false;
}
EVP_MD_CTX* mdctx = EVP_MD_CTX_create(); EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
THROW_BAD_ALLOC_IF_NULL(mdctx); THROW_BAD_ALLOC_IF_NULL(mdctx);
@@ -206,7 +215,7 @@ NvPairingManager::saltPin(const QByteArray& salt, QString pin)
NvPairingManager::PairState NvPairingManager::PairState
NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverCert) NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverCert)
{ {
int serverMajorVersion = NvHTTP::parseQuad(appVersion).at(0); int serverMajorVersion = NvHTTP::parseQuad(appVersion).value(0);
qInfo() << "Pairing with server generation:" << serverMajorVersion; qInfo() << "Pairing with server generation:" << serverMajorVersion;
QCryptographicHash::Algorithm hashAlgo; QCryptographicHash::Algorithm hashAlgo;
@@ -278,7 +287,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
} }
QByteArray challengeResponseData = decrypt(m_Http.getXmlStringFromHex(challengeXml, "challengeresponse"), aesKey); QByteArray challengeResponseData = decrypt(m_Http.getXmlStringFromHex(challengeXml, "challengeresponse"), aesKey);
if (challengeResponseData.size() < hashLength) { if (challengeResponseData.size() < hashLength + 16) {
qCritical() << "Invalid challengeresponse at stage #2"; qCritical() << "Invalid challengeresponse at stage #2";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
+36 -28
View File
@@ -51,7 +51,7 @@ ApplicationWindow {
} }
// Display any modal dialogs for configuration warnings // Display any modal dialogs for configuration warnings
if (runConfigChecks) { if (isGuiLaunch) {
if (SystemProperties.isWow64) { if (SystemProperties.isWow64) {
wow64Dialog.open() wow64Dialog.open()
} }
@@ -171,24 +171,28 @@ ApplicationWindow {
} }
onVisibleChanged: { onVisibleChanged: {
// When we become invisible while streaming is going on, // Polling is only managed from the window for a normal GUI launch;
// stop polling immediately. // CLI commands (stream/quit/pair) manage it via the ComputerSeeker.
if (!visible) { if (isGuiLaunch) {
inactivityTimer.stop() // When we become invisible while streaming is going on,
// stop polling immediately.
if (!visible) {
inactivityTimer.stop()
if (pollingActive) { if (pollingActive) {
ComputerManager.stopPollingAsync() ComputerManager.stopPollingAsync()
pollingActive = false pollingActive = false
}
} }
} else if (active) {
else if (active) { // When we become visible and active again, start polling
// When we become visible and active again, start polling inactivityTimer.stop()
inactivityTimer.stop()
// Restart polling if it was stopped // Restart polling if it was stopped
if (!pollingActive) { if (!pollingActive) {
ComputerManager.startPolling() ComputerManager.startPolling()
pollingActive = true pollingActive = true
}
} }
} }
@@ -197,20 +201,24 @@ ApplicationWindow {
} }
onActiveChanged: { onActiveChanged: {
if (active) { // Polling is only managed from the window for a normal GUI launch;
// Stop the inactivity timer // CLI commands (stream/quit/pair) manage it via the ComputerSeeker.
inactivityTimer.stop() if (isGuiLaunch) {
if (active) {
// Stop the inactivity timer
inactivityTimer.stop()
// Restart polling if it was stopped // Restart polling if it was stopped
if (!pollingActive) { if (!pollingActive) {
ComputerManager.startPolling() ComputerManager.startPolling()
pollingActive = true pollingActive = true
}
}
else {
// Start the inactivity timer to stop polling
// if focus does not return within a few minutes.
inactivityTimer.restart()
} }
}
else {
// Start the inactivity timer to stop polling
// if focus does not return within a few minutes.
inactivityTimer.restart()
} }
// Poll for gamepad input only when the window is in focus // Poll for gamepad input only when the window is in focus
+11 -2
View File
@@ -465,7 +465,16 @@ int main(int argc, char *argv[])
#endif #endif
{ {
s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch()))); s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch())));
if (s_LoggerFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
// The log directory may be shared with other users (/tmp on macOS), so
// we must never open a file or symlink that someone else planted there.
// NewOnly fails if anything already exists at this path.
QIODevice::OpenMode logOpenMode = QIODevice::WriteOnly | QIODevice::Text | QIODevice::NewOnly;
#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
if (s_LoggerFile->open(logOpenMode, QFileDevice::ReadOwner | QFileDevice::WriteOwner)) {
#else
if (s_LoggerFile->open(logOpenMode)) {
#endif
QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl; QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl;
s_LoggerStream.setDevice(s_LoggerFile); s_LoggerStream.setDevice(s_LoggerFile);
} }
@@ -1034,7 +1043,7 @@ int main(int argc, char *argv[])
if (hasGUI) { if (hasGUI) {
engine.rootContext()->setContextProperty("initialView", initialView); engine.rootContext()->setContextProperty("initialView", initialView);
engine.rootContext()->setContextProperty("runConfigChecks", commandLineParserResult == GlobalCommandLineParser::NormalStartRequested); engine.rootContext()->setContextProperty("isGuiLaunch", commandLineParserResult == GlobalCommandLineParser::NormalStartRequested);
// Load the main.qml file // Load the main.qml file
engine.load(QUrl(QStringLiteral("qrc:/gui/main.qml"))); engine.load(QUrl(QStringLiteral("qrc:/gui/main.qml")));
+1 -1
View File
@@ -965,7 +965,7 @@ void SdlInputHandler::setAdaptiveTriggers(uint16_t controllerNumber, DualSenseOu
#if SDL_VERSION_ATLEAST(2, 0, 16) #if SDL_VERSION_ATLEAST(2, 0, 16)
// Make sure the controller number is within our supported count // Make sure the controller number is within our supported count
if (controllerNumber <= MAX_GAMEPADS && if (controllerNumber < MAX_GAMEPADS &&
// and we have a valid controller // and we have a valid controller
m_GamepadState[controllerNumber].controller != nullptr && m_GamepadState[controllerNumber].controller != nullptr &&
// and it's a PS5 controller // and it's a PS5 controller
+39 -26
View File
@@ -840,9 +840,8 @@ bool Session::initialize(QQuickWindow* qtWindow)
// to allow decoding via VideoToolbox on macOS. Since we don't // to allow decoding via VideoToolbox on macOS. Since we don't
// have fixup code for HEVC, just avoid it if GFE is too old. // have fixup code for HEVC, just avoid it if GFE is too old.
QVector<int> gfeVersion = NvHTTP::parseQuad(m_Computer->gfeVersion); QVector<int> gfeVersion = NvHTTP::parseQuad(m_Computer->gfeVersion);
if (gfeVersion.isEmpty() || // Very old versions don't have GfeVersion at all if (gfeVersion.value(0) < 3 ||
gfeVersion[0] < 3 || (gfeVersion.value(0) == 3 && gfeVersion.value(1) < 11)) {
(gfeVersion[0] == 3 && gfeVersion[1] < 11)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Disabling HEVC on macOS due to old GFE version"); "Disabling HEVC on macOS due to old GFE version");
m_SupportedVideoFormats.removeByMask(VIDEO_FORMAT_MASK_H265); m_SupportedVideoFormats.removeByMask(VIDEO_FORMAT_MASK_H265);
@@ -1363,34 +1362,48 @@ void Session::getWindowDimensions(int& x, int& y,
} }
} }
// Get the usable display bounds
SDL_Rect usableBounds; SDL_Rect usableBounds;
if (SDL_GetDisplayUsableBounds(displayIndex, &usableBounds) == 0) { if (SDL_GetDisplayUsableBounds(displayIndex, &usableBounds) != 0) {
// If the stream resolution fits within the usable display area, use it directly
if (m_StreamConfig.width <= usableBounds.w &&
m_StreamConfig.height <= usableBounds.h) {
width = m_StreamConfig.width;
height = m_StreamConfig.height;
} else {
// Otherwise, use 80% of usable bounds and preserve aspect ratio
SDL_Rect src, dst;
src.x = src.y = dst.x = dst.y = 0;
src.w = m_StreamConfig.width;
src.h = m_StreamConfig.height;
dst.w = ((int)(usableBounds.w * 0.80f)) & ~0x1; // even width
dst.h = ((int)(usableBounds.h * 0.80f)) & ~0x1; // even height
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
width = dst.w;
height = dst.h;
}
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayUsableBounds() failed: %s", "SDL_GetDisplayUsableBounds() failed: %s",
SDL_GetError()); SDL_GetError());
SDL_zero(usableBounds);
}
// Use the usable display bounds if they are valid
if (usableBounds.w != 0 &&
WMUtils::hasDisplayUsableBounds() &&
m_StreamConfig.width <= usableBounds.w &&
m_StreamConfig.height <= usableBounds.h) {
// We fit pixel-perfect in the usable area, so don't scale at all
width = m_StreamConfig.width;
height = m_StreamConfig.height;
}
else if (usableBounds.w != 0 || SDL_GetDisplayBounds(displayIndex, &usableBounds) == 0) {
// Otherwise, use 80% of usable bounds and preserve aspect ratio
SDL_Rect src, dst;
src.x = src.y = dst.x = dst.y = 0;
src.w = m_StreamConfig.width;
src.h = m_StreamConfig.height;
dst.w = ((int)(usableBounds.w * 0.80f)) & ~0x1; // even width
dst.h = ((int)(usableBounds.h * 0.80f)) & ~0x1; // even height
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
// Only scale the size down, never up
if (m_StreamConfig.width >= dst.w || m_StreamConfig.height >= dst.h) {
width = dst.w;
height = dst.h;
}
else {
width = m_StreamConfig.width;
height = m_StreamConfig.height;
}
}
else {
// Oh well, hope for the best
width = m_StreamConfig.width; width = m_StreamConfig.width;
height = m_StreamConfig.height; height = m_StreamConfig.height;
} }
+4 -1
View File
@@ -38,7 +38,6 @@
#include <asm/hwprobe.h> #include <asm/hwprobe.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#else #else
#define __NR_riscv_hwprobe 258
struct riscv_hwprobe { struct riscv_hwprobe {
int64_t key; int64_t key;
uint64_t value; uint64_t value;
@@ -46,6 +45,10 @@ struct riscv_hwprobe {
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 #define RISCV_HWPROBE_KEY_IMA_EXT_0 4
#endif #endif
#ifndef __NR_riscv_hwprobe
#define __NR_riscv_hwprobe 258
#endif
// RISC-V Scalar AES [E]ncryption and [D]ecryption // RISC-V Scalar AES [E]ncryption and [D]ecryption
#ifndef RISCV_HWPROBE_EXT_ZKND #ifndef RISCV_HWPROBE_EXT_ZKND
#define RISCV_HWPROBE_EXT_ZKND (1 << 11) #define RISCV_HWPROBE_EXT_ZKND (1 << 11)
@@ -60,7 +60,7 @@ D3D11VARenderer::D3D11VARenderer(int decoderSelectionPass)
m_DecoderSelectionPass(decoderSelectionPass), m_DecoderSelectionPass(decoderSelectionPass),
m_DevicesWithFL11Support(0), m_DevicesWithFL11Support(0),
m_DevicesWithCodecSupport(0), m_DevicesWithCodecSupport(0),
m_LastColorTrc(AVCOL_TRC_UNSPECIFIED), m_SwapChainColorTrc(AVCOL_TRC_UNSPECIFIED),
m_AllowTearing(false), m_AllowTearing(false),
m_OverlayLock(0), m_OverlayLock(0),
m_HwDeviceContext(nullptr) m_HwDeviceContext(nullptr)
@@ -800,7 +800,7 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
HRESULT hr; HRESULT hr;
if (frame->color_trc != m_LastColorTrc) { if (frame->color_trc != m_SwapChainColorTrc) {
if (frame->color_trc == AVCOL_TRC_SMPTE2084) { if (frame->color_trc == AVCOL_TRC_SMPTE2084) {
// Switch to Rec 2020 PQ (SMPTE ST 2084) colorspace for HDR10 rendering // Switch to Rec 2020 PQ (SMPTE ST 2084) colorspace for HDR10 rendering
hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
@@ -820,7 +820,7 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
} }
} }
m_LastColorTrc = frame->color_trc; m_SwapChainColorTrc = frame->color_trc;
} }
// Present according to the decoder parameters // Present according to the decoder parameters
@@ -90,7 +90,7 @@ private:
DXGI_FORMAT m_TextureFormat; DXGI_FORMAT m_TextureFormat;
int m_DisplayWidth; int m_DisplayWidth;
int m_DisplayHeight; int m_DisplayHeight;
AVColorTransferCharacteristic m_LastColorTrc; AVColorTransferCharacteristic m_SwapChainColorTrc;
bool m_AllowTearing; bool m_AllowTearing;
+1 -1
View File
@@ -1307,7 +1307,7 @@ bool DrmRenderer::mapDumbBuffer(uint32_t handle, size_t size, void** mapping)
#else #else
*mapping = mmap(nullptr, size, PROT_WRITE, MAP_SHARED, m_DrmFd, mapBuf.offset); *mapping = mmap(nullptr, size, PROT_WRITE, MAP_SHARED, m_DrmFd, mapBuf.offset);
#endif #endif
if (mapping == MAP_FAILED) { if (*mapping == MAP_FAILED) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"mmap() failed for dumb buffer: %d", "mmap() failed for dumb buffer: %d",
errno); errno);
@@ -426,7 +426,13 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
D3DDISPLAYMODEEX currentMode; D3DDISPLAYMODEEX currentMode;
currentMode.Size = sizeof(currentMode); currentMode.Size = sizeof(currentMode);
d3d9ex->GetAdapterDisplayModeEx(adapterIndex, &currentMode, nullptr); hr = d3d9ex->GetAdapterDisplayModeEx(adapterIndex, &currentMode, nullptr);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"GetAdapterDisplayModeEx() failed: %x",
hr);
return false;
}
D3DPRESENT_PARAMETERS d3dpp = {}; D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.hDeviceWindow = info.info.win.window; d3dpp.hDeviceWindow = info.info.win.window;
@@ -987,6 +993,7 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"StretchRect() failed: %x", "StretchRect() failed: %x",
hr); hr);
m_Device->EndScene();
SDL_Event event; SDL_Event event;
event.type = SDL_RENDER_DEVICE_RESET; event.type = SDL_RENDER_DEVICE_RESET;
SDL_PushEvent(&event); SDL_PushEvent(&event);
@@ -240,7 +240,7 @@ ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImag
else { else {
// Cast the EGLAttrib array elements to EGLint for the KHR extension // Cast the EGLAttrib array elements to EGLint for the KHR extension
EGLint intAttribs[MAX_ATTRIB_COUNT]; EGLint intAttribs[MAX_ATTRIB_COUNT];
for (int i = 0; i < MAX_ATTRIB_COUNT; i++) { for (int i = 0; i < attribIndex; i++) {
intAttribs[i] = (EGLint)attribs[i]; intAttribs[i] = (EGLint)attribs[i];
} }
@@ -328,7 +328,7 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
attribs[attribIndex++] = layer.offset[0]; attribs[attribIndex++] = layer.offset[0];
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
attribs[attribIndex++] = layer.pitch[0]; attribs[attribIndex++] = layer.pitch[0];
if (m_EGLExtDmaBuf) { if (m_EGLExtDmaBuf && object.drm_format_modifier != DRM_FORMAT_MOD_INVALID) {
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF); attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
@@ -343,7 +343,7 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
attribs[attribIndex++] = layer.offset[1]; attribs[attribIndex++] = layer.offset[1];
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
attribs[attribIndex++] = layer.pitch[1]; attribs[attribIndex++] = layer.pitch[1];
if (m_EGLExtDmaBuf) { if (m_EGLExtDmaBuf && object.drm_format_modifier != DRM_FORMAT_MOD_INVALID) {
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF); attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
@@ -358,7 +358,7 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
attribs[attribIndex++] = layer.offset[2]; attribs[attribIndex++] = layer.offset[2];
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
attribs[attribIndex++] = layer.pitch[2]; attribs[attribIndex++] = layer.pitch[2];
if (m_EGLExtDmaBuf) { if (m_EGLExtDmaBuf && object.drm_format_modifier != DRM_FORMAT_MOD_INVALID) {
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF); attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
@@ -373,7 +373,7 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
attribs[attribIndex++] = layer.offset[3]; attribs[attribIndex++] = layer.offset[3];
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
attribs[attribIndex++] = layer.pitch[3]; attribs[attribIndex++] = layer.pitch[3];
if (m_EGLExtDmaBuf) { if (m_EGLExtDmaBuf && object.drm_format_modifier != DRM_FORMAT_MOD_INVALID) {
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF); attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
@@ -63,6 +63,7 @@ EGLRenderer::EGLRenderer(IFFmpegRenderer *backendRenderer)
m_Window(nullptr), m_Window(nullptr),
m_Backend(backendRenderer), m_Backend(backendRenderer),
m_VideoVAO(0), m_VideoVAO(0),
m_VideoVBO(0),
m_BlockingSwapBuffers(false), m_BlockingSwapBuffers(false),
m_LastRenderSync(EGL_NO_SYNC), m_LastRenderSync(EGL_NO_SYNC),
m_glEGLImageTargetTexture2DOES(nullptr), m_glEGLImageTargetTexture2DOES(nullptr),
@@ -100,6 +101,7 @@ EGLRenderer::~EGLRenderer()
SDL_assert(m_glDeleteVertexArraysOES != nullptr); SDL_assert(m_glDeleteVertexArraysOES != nullptr);
m_glDeleteVertexArraysOES(1, &m_VideoVAO); m_glDeleteVertexArraysOES(1, &m_VideoVAO);
} }
glDeleteBuffers(1, &m_VideoVBO);
glDeleteTextures(EGL_MAX_PLANES, m_Textures); glDeleteTextures(EGL_MAX_PLANES, m_Textures);
glDeleteTextures(Overlay::OverlayMax, m_OverlayTextures); glDeleteTextures(Overlay::OverlayMax, m_OverlayTextures);
@@ -266,10 +268,9 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type, int viewportWidth, in
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
int EGLRenderer::loadAndBuildShader(int shaderType, GLuint EGLRenderer::loadAndBuildShader(int shaderType, const char *file) {
const char *file) {
GLuint shader = glCreateShader(shaderType); GLuint shader = glCreateShader(shaderType);
if (!shader || shader == GL_INVALID_ENUM) { if (!shader) {
EGL_LOG(Error, "Can't create shader: %d", glGetError()); EGL_LOG(Error, "Can't create shader: %d", glGetError());
return 0; return 0;
} }
@@ -286,6 +287,7 @@ int EGLRenderer::loadAndBuildShader(int shaderType,
char shaderLog[512]; char shaderLog[512];
glGetShaderInfoLog(shader, sizeof (shaderLog), nullptr, shaderLog); glGetShaderInfoLog(shader, sizeof (shaderLog), nullptr, shaderLog);
EGL_LOG(Error, "Cannot load shader \"%s\": %s", file, shaderLog); EGL_LOG(Error, "Cannot load shader \"%s\": %s", file, shaderLog);
glDeleteShader(shader);
return 0; return 0;
} }
@@ -297,7 +299,7 @@ unsigned EGLRenderer::compileShader(const char* vertexShaderSrc, const char* fra
GLuint vertexShader = loadAndBuildShader(GL_VERTEX_SHADER, vertexShaderSrc); GLuint vertexShader = loadAndBuildShader(GL_VERTEX_SHADER, vertexShaderSrc);
if (!vertexShader) if (!vertexShader)
return false; return 0;
GLuint fragmentShader = loadAndBuildShader(GL_FRAGMENT_SHADER, fragmentShaderSrc); GLuint fragmentShader = loadAndBuildShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
if (!fragmentShader) if (!fragmentShader)
@@ -645,13 +647,12 @@ bool EGLRenderer::setupVideoRenderingState() {
}; };
// Setup the VAO and VBO // Setup the VAO and VBO
unsigned int VBO;
m_glGenVertexArraysOES(1, &m_VideoVAO); m_glGenVertexArraysOES(1, &m_VideoVAO);
glGenBuffers(1, &VBO); glGenBuffers(1, &m_VideoVBO);
m_glBindVertexArrayOES(m_VideoVAO); m_glBindVertexArrayOES(m_VideoVAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); glBindBuffer(GL_ARRAY_BUFFER, m_VideoVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// compileShader() ensures that aPosition and aTexCoord are indexes 0 and 1 respectively // compileShader() ensures that aPosition and aTexCoord are indexes 0 and 1 respectively
@@ -663,7 +664,8 @@ bool EGLRenderer::setupVideoRenderingState() {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
m_glBindVertexArrayOES(0); m_glBindVertexArrayOES(0);
glDeleteBuffers(1, &VBO); // The VBO is kept alive as a member since the VAO still references it.
// It is deleted in ~EGLRenderer() alongside the other GL resources.
GLenum err = glGetError(); GLenum err = glGetError();
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
@@ -787,7 +789,7 @@ void EGLRenderer::renderFrame(AVFrame* frame)
} }
ssize_t plane_count = m_Backend->exportEGLImages(frame, m_EGLDisplay, imgs); ssize_t plane_count = m_Backend->exportEGLImages(frame, m_EGLDisplay, imgs);
if (plane_count < 0) if (plane_count <= 0)
return; return;
for (ssize_t i = 0; i < plane_count; ++i) { for (ssize_t i = 0; i < plane_count; ++i) {
glActiveTexture(GL_TEXTURE0 + i); glActiveTexture(GL_TEXTURE0 + i);
@@ -29,7 +29,7 @@ private:
bool compileShaders(); bool compileShaders();
bool setupVideoRenderingState(); bool setupVideoRenderingState();
bool setupOverlayRenderingState(); bool setupOverlayRenderingState();
static int loadAndBuildShader(int shaderType, const char *filename); static GLuint loadAndBuildShader(int shaderType, const char *filename);
AVPixelFormat m_EGLImagePixelFormat; AVPixelFormat m_EGLImagePixelFormat;
void *m_EGLDisplay; void *m_EGLDisplay;
@@ -44,6 +44,7 @@ private:
SDL_Window *m_Window; SDL_Window *m_Window;
IFFmpegRenderer *m_Backend; IFFmpegRenderer *m_Backend;
unsigned int m_VideoVAO; unsigned int m_VideoVAO;
unsigned int m_VideoVBO;
bool m_BlockingSwapBuffers; bool m_BlockingSwapBuffers;
EGLSync m_LastRenderSync; EGLSync m_LastRenderSync;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
@@ -10,6 +10,8 @@ DxVsyncSource::DxVsyncSource(Pacer* pacer) :
m_LastMonitor(nullptr) m_LastMonitor(nullptr)
{ {
SDL_zero(m_WaitForVblankEventParams); SDL_zero(m_WaitForVblankEventParams);
m_WaitForVblankEventParams.VidPnSourceId = D3DDDI_ID_UNINITIALIZED;
SDL_zero(m_AdapterLuid);
} }
DxVsyncSource::~DxVsyncSource() DxVsyncSource::~DxVsyncSource()
@@ -72,41 +74,43 @@ bool DxVsyncSource::isAsync()
return false; return false;
} }
void DxVsyncSource::waitForVsync() bool DxVsyncSource::waitForVsync()
{ {
NTSTATUS status; NTSTATUS status;
// If the monitor has changed from last time, open the new adapter // If the monitor has changed from last time, open the new adapter
HMONITOR currentMonitor = MonitorFromWindow(m_Window, MONITOR_DEFAULTTONEAREST); HMONITOR currentMonitor = MonitorFromWindow(m_Window, MONITOR_DEFAULTTONEAREST);
if (currentMonitor != m_LastMonitor) { if (!currentMonitor || currentMonitor != m_LastMonitor) {
// Close the old adapter
if (m_WaitForVblankEventParams.hAdapter != 0) {
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
m_D3DKMTCloseAdapter(&closeAdapterParams);
m_WaitForVblankEventParams.hAdapter = 0;
}
m_LastMonitor = nullptr;
// We can't wait if we're not on any monitor
if (!currentMonitor) {
return false;
}
MONITORINFOEXA monitorInfo = {}; MONITORINFOEXA monitorInfo = {};
monitorInfo.cbSize = sizeof(monitorInfo); monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) { if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"GetMonitorInfo() failed: %d", "GetMonitorInfo() failed: %d",
GetLastError()); GetLastError());
return; return false;
} }
DEVMODEA monitorMode; DEVMODEA monitorMode = {};
monitorMode.dmSize = sizeof(monitorMode); monitorMode.dmSize = sizeof(monitorMode);
if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) { if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"EnumDisplaySettings() failed: %d", "EnumDisplaySettings() failed: %d",
GetLastError()); GetLastError());
return; return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Monitor changed: %s %d Hz",
monitorInfo.szDevice,
monitorMode.dmDisplayFrequency);
// Close the old adapter
if (m_WaitForVblankEventParams.hAdapter != 0) {
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
m_D3DKMTCloseAdapter(&closeAdapterParams);
} }
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {}; D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
@@ -115,7 +119,7 @@ void DxVsyncSource::waitForVsync()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"CreateDC() failed: %d", "CreateDC() failed: %d",
GetLastError()); GetLastError());
return; return false;
} }
// Open the new adapter // Open the new adapter
@@ -126,21 +130,38 @@ void DxVsyncSource::waitForVsync()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"D3DKMTOpenAdapterFromHdc() failed: %x", "D3DKMTOpenAdapterFromHdc() failed: %x",
status); status);
return; return false;
}
// VidPnSourceId is scoped to the adapter, so we must also check the adapter LUID
if (m_AdapterLuid.LowPart != openAdapterParams.AdapterLuid.LowPart ||
m_AdapterLuid.HighPart != openAdapterParams.AdapterLuid.HighPart ||
m_WaitForVblankEventParams.VidPnSourceId != openAdapterParams.VidPnSourceId) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Monitor changed: %s %d Hz",
monitorInfo.szDevice,
monitorMode.dmDisplayFrequency);
} }
m_WaitForVblankEventParams.hAdapter = openAdapterParams.hAdapter; m_WaitForVblankEventParams.hAdapter = openAdapterParams.hAdapter;
m_WaitForVblankEventParams.hDevice = 0; m_WaitForVblankEventParams.hDevice = 0;
m_WaitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId; m_WaitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
m_AdapterLuid = openAdapterParams.AdapterLuid;
m_LastMonitor = currentMonitor; m_LastMonitor = currentMonitor;
} }
status = m_D3DKMTWaitForVerticalBlankEvent(&m_WaitForVblankEventParams); status = m_D3DKMTWaitForVerticalBlankEvent(&m_WaitForVblankEventParams);
if (status != STATUS_SUCCESS) { if (status != STATUS_SUCCESS) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, if (status != STATUS_GRAPHICS_PRESENT_OCCLUDED) {
"D3DKMTWaitForVerticalBlankEvent() failed: %x", SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
status); "D3DKMTWaitForVerticalBlankEvent() failed: %x",
return; status);
}
m_LastMonitor = nullptr;
return false;
} }
return true;
} }
@@ -10,6 +10,7 @@ typedef UINT D3DKMT_HANDLE;
typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L) #define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L)
#define D3DDDI_ID_UNINITIALIZED (UINT)(~0)
typedef struct _D3DKMT_OPENADAPTERFROMHDC { typedef struct _D3DKMT_OPENADAPTERFROMHDC {
HDC hDc; HDC hDc;
D3DKMT_HANDLE hAdapter; D3DKMT_HANDLE hAdapter;
@@ -39,13 +40,14 @@ public:
virtual bool isAsync() override; virtual bool isAsync() override;
virtual void waitForVsync() override; virtual bool waitForVsync() override;
private: private:
Pacer* m_Pacer; Pacer* m_Pacer;
HMODULE m_Gdi32Handle; HMODULE m_Gdi32Handle;
HWND m_Window; HWND m_Window;
HMONITOR m_LastMonitor; HMONITOR m_LastMonitor;
LUID m_AdapterLuid;
D3DKMT_WAITFORVERTICALBLANKEVENT m_WaitForVblankEventParams; D3DKMT_WAITFORVERTICALBLANKEVENT m_WaitForVblankEventParams;
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc; PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
@@ -115,13 +115,20 @@ int Pacer::vsyncThread(void *context)
while (!me->m_Stopping) { while (!me->m_Stopping) {
if (async) { if (async) {
// Wait for the VSync source to invoke signalVsync() or 100ms to elapse // Wait for the VSync source to invoke signalVsync() or 100ms to elapse
me->m_FrameQueueLock.lock(); QMutexLocker locker(&me->m_FrameQueueLock);
me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 100); me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 100);
me->m_FrameQueueLock.unlock();
} }
else { else {
// Let the VSync source wait in the context of our thread // Let the VSync source wait in the context of our thread
me->m_VsyncSource->waitForVsync(); if (!me->m_VsyncSource->waitForVsync()) {
// Fall back to a fixed delay if the VSync source fails
//
// NB: We don't actually expect signalVsync() to be called, since
// this is a synchronous VSync source, but we wait on m_VsyncSignalled
// anyway so that the destructor can immediately wake us on shutdown.
QMutexLocker locker(&me->m_FrameQueueLock);
me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 1000 / me->m_DisplayFps);
}
} }
if (me->m_Stopping) { if (me->m_Stopping) {
@@ -148,24 +155,25 @@ int Pacer::renderThread(void* context)
// Wait for the renderer to be ready for the next frame // Wait for the renderer to be ready for the next frame
me->m_VsyncRenderer->waitToRender(); me->m_VsyncRenderer->waitToRender();
// Acquire the frame queue lock to protect the queue and AVFrame* frame;
// the not empty condition {
me->m_FrameQueueLock.lock(); // Acquire the frame queue lock to protect the queue and
// the not empty condition
QMutexLocker locker(&me->m_FrameQueueLock);
// Wait for a frame to be ready to render // Wait for a frame to be ready to render
while (!me->m_Stopping && me->m_RenderQueue.isEmpty()) { while (!me->m_Stopping && me->m_RenderQueue.isEmpty()) {
me->m_RenderQueueNotEmpty.wait(&me->m_FrameQueueLock); me->m_RenderQueueNotEmpty.wait(&me->m_FrameQueueLock);
}
if (me->m_Stopping) {
// Exit this thread
break;
}
frame = me->m_RenderQueue.dequeue();
} }
if (me->m_Stopping) {
// Exit this thread
me->m_FrameQueueLock.unlock();
break;
}
AVFrame* frame = me->m_RenderQueue.dequeue();
me->m_FrameQueueLock.unlock();
me->renderFrame(frame); me->renderFrame(frame);
} }
@@ -249,7 +257,7 @@ void Pacer::handleVsync(int timeUntilNextVsyncMillis)
return; return;
} }
if (m_Stopping) { if (m_Stopping || m_PacingQueue.isEmpty()) {
m_FrameQueueLock.unlock(); m_FrameQueueLock.unlock();
return; return;
} }
@@ -349,7 +357,7 @@ void Pacer::renderFrame(AVFrame* frame)
av_frame_free(&frame); av_frame_free(&frame);
// Drop frames if we have too many queued up for a while // Drop frames if we have too many queued up for a while
m_FrameQueueLock.lock(); QMutexLocker frameQueueLocker(&m_FrameQueueLock);
int frameDropTarget; int frameDropTarget;
@@ -382,13 +390,11 @@ void Pacer::renderFrame(AVFrame* frame)
AVFrame* frame = m_RenderQueue.dequeue(); AVFrame* frame = m_RenderQueue.dequeue();
// Drop the lock while we call av_frame_free() // Drop the lock while we call av_frame_free()
m_FrameQueueLock.unlock(); frameQueueLocker.unlock();
m_VideoStats->pacerDroppedFrames++; m_VideoStats->pacerDroppedFrames++;
av_frame_free(&frame); av_frame_free(&frame);
m_FrameQueueLock.lock(); frameQueueLocker.relock();
} }
m_FrameQueueLock.unlock();
} }
void Pacer::dropFrameForEnqueue(QQueue<AVFrame*>& queue) void Pacer::dropFrameForEnqueue(QQueue<AVFrame*>& queue)
@@ -22,9 +22,10 @@ public:
// sources require calls to waitForVsync(). // sources require calls to waitForVsync().
virtual bool isAsync() = 0; virtual bool isAsync() = 0;
virtual void waitForVsync() { virtual bool waitForVsync() {
// Synchronous sources must implement waitForVsync()! // Synchronous sources must implement waitForVsync()!
SDL_assert(false); SDL_assert(false);
return false;
} }
}; };
@@ -769,6 +769,7 @@ bool PlVkRenderer::populateQueues(int videoFormat)
vkDeviceContext->nb_comp_queues = m_Vulkan->queue_compute.count; vkDeviceContext->nb_comp_queues = m_Vulkan->queue_compute.count;
// Select a video decode queue that is capable of decoding our chosen format // Select a video decode queue that is capable of decoding our chosen format
vkDeviceContext->queue_family_decode_index = -1;
for (uint32_t i = 0; i < queueFamilyCount; i++) { for (uint32_t i = 0; i < queueFamilyCount; i++) {
if (queueFamilies[i].queueFamilyProperties.queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) { if (queueFamilies[i].queueFamilyProperties.queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
if (videoFormat & VIDEO_FORMAT_MASK_H264) { if (videoFormat & VIDEO_FORMAT_MASK_H264) {
@@ -897,6 +898,16 @@ void PlVkRenderer::waitToRender()
return; return;
} }
// If a swapchain (re)creation failed, m_Swapchain is NULL. We can't render
// or resize it, and a device reset has already been requested, so skip this
// frame and let the main thread recreate the renderer.
if (m_Swapchain == nullptr) {
SDL_Event event;
event.type = SDL_RENDER_DEVICE_RESET;
SDL_PushEvent(&event);
return;
}
#ifndef Q_OS_WIN32 #ifndef Q_OS_WIN32
// With libplacebo's Vulkan backend, all swap_buffers does is wait for queued // With libplacebo's Vulkan backend, all swap_buffers does is wait for queued
// presents to finish. This happens to be exactly what we want to do here, since // presents to finish. This happens to be exactly what we want to do here, since
@@ -533,7 +533,7 @@ public:
#endif #endif
protected: protected:
InitFailureReason m_InitFailureReason; InitFailureReason m_InitFailureReason = InitFailureReason::Unknown;
private: private:
RendererType m_Type; RendererType m_Type;
@@ -21,8 +21,10 @@
VAAPIRenderer::VAAPIRenderer(int decoderSelectionPass) VAAPIRenderer::VAAPIRenderer(int decoderSelectionPass)
: IFFmpegRenderer(RendererType::VAAPI), : IFFmpegRenderer(RendererType::VAAPI),
m_DecoderSelectionPass(decoderSelectionPass), m_DecoderSelectionPass(decoderSelectionPass),
m_WindowSystem(SDL_SYSWM_UNKNOWN),
m_HwContext(nullptr), m_HwContext(nullptr),
m_BlacklistedForDirectRendering(false), m_BlacklistedForDirectRendering(false),
m_HasRfiLatencyBug(false),
m_RequiresExplicitPixelFormat(false), m_RequiresExplicitPixelFormat(false),
m_OverlayMutex(nullptr) m_OverlayMutex(nullptr)
#ifdef HAVE_EGL #ifdef HAVE_EGL
@@ -159,6 +161,9 @@ VAAPIRenderer::openDisplay(SDL_Window* window)
errno); errno);
return nullptr; return nullptr;
} }
// This FD is owned by us (not shared with SDL), so it must be
// closed when we're done with it or adopted into m_DrmFd.
mustCloseFd = true;
} }
// If the KMSDRM FD is not a render node FD, open the render node for libva to use. // If the KMSDRM FD is not a render node FD, open the render node for libva to use.
@@ -541,11 +541,12 @@ void VDPAURenderer::renderFrame(AVFrame* frame)
// We need to create the mixer on the fly, because we don't know the dimensions // We need to create the mixer on the fly, because we don't know the dimensions
// of our video surfaces in advance of decoding. We also need to recreate it when // of our video surfaces in advance of decoding. We also need to recreate it when
// the frame format or size changes. // the frame format or size changes.
if (hasFrameFormatChanged(frame)) { if (hasFrameFormatChanged(frame) && m_VideoMixer != 0) {
if (m_VideoMixer != 0) { m_VdpVideoMixerDestroy(m_VideoMixer);
m_VdpVideoMixerDestroy(m_VideoMixer); m_VideoMixer = 0;
} }
if (m_VideoMixer == 0) {
VdpChromaType videoSurfaceChroma; VdpChromaType videoSurfaceChroma;
uint32_t videoSurfaceWidth, videoSurfaceHeight; uint32_t videoSurfaceWidth, videoSurfaceHeight;
status = m_VdpVideoSurfaceGetParameters(videoSurface, &videoSurfaceChroma, status = m_VdpVideoSurfaceGetParameters(videoSurface, &videoSurfaceChroma,
@@ -580,6 +581,7 @@ void VDPAURenderer::renderFrame(AVFrame* frame)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VdpVideoMixerCreate() failed: %s", "VdpVideoMixerCreate() failed: %s",
m_VdpGetErrorString(status)); m_VdpGetErrorString(status));
m_VideoMixer = 0;
return; return;
} }
} }
+28 -7
View File
@@ -1765,11 +1765,21 @@ void FFmpegVideoDecoder::writeBuffer(PLENTRY entry, int& offset)
int nalStart, nalEnd; int nalStart, nalEnd;
bool needsFixup; bool needsFixup;
// Read the old NALU // Find the old SPS NALU
find_nal_unit((uint8_t*)entry->data, entry->length, &nalStart, &nalEnd); if (find_nal_unit((uint8_t*)entry->data, entry->length, &nalStart, &nalEnd) <= 0) {
read_nal_unit(stream, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
(unsigned char *)&entry->data[nalStart], "Failed to find SPS");
nalEnd - nalStart); h264_free(stream);
goto WriteUnmodifiedBuffer;
}
// Read the SPS. Pass it through unmodified if we fail to parse it.
if (read_nal_unit(stream, (uint8_t*)&entry->data[nalStart], nalEnd - nalStart) < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to read SPS");
h264_free(stream);
goto WriteUnmodifiedBuffer;
}
SDL_assert(nalStart == 3 || nalStart == 4); // 3 or 4 byte Annex B start sequence SDL_assert(nalStart == 3 || nalStart == 4); // 3 or 4 byte Annex B start sequence
SDL_assert(nalEnd == entry->length); SDL_assert(nalEnd == entry->length);
@@ -1802,8 +1812,18 @@ void FFmpegVideoDecoder::writeBuffer(PLENTRY entry, int& offset)
// Copy the modified NALU data. This clobbers byte 0 and starts NALU data at byte 1. // Copy the modified NALU data. This clobbers byte 0 and starts NALU data at byte 1.
// Since it prepended one extra byte, subtract one from the returned length. // Since it prepended one extra byte, subtract one from the returned length.
offset += write_nal_unit(stream, (uint8_t*)&m_DecodeBuffer.data()[initialOffset + nalStart - 1], //
MAX_SPS_EXTRA_SIZE + entry->length - nalStart) - 1; // Pass the SPS through unmodified if the write fails.
int writeLength = write_nal_unit(stream, (uint8_t*)&m_DecodeBuffer.data()[initialOffset + nalStart - 1],
MAX_SPS_EXTRA_SIZE + entry->length - nalStart);
if (writeLength < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to write modified SPS");
h264_free(stream);
goto WriteUnmodifiedBuffer;
}
offset += writeLength - 1;
// Copy the NALU prefix over from the original SPS // Copy the NALU prefix over from the original SPS
memcpy(&m_DecodeBuffer.data()[initialOffset], entry->data, nalStart); memcpy(&m_DecodeBuffer.data()[initialOffset], entry->data, nalStart);
@@ -1834,6 +1854,7 @@ void FFmpegVideoDecoder::writeBuffer(PLENTRY entry, int& offset)
h264_free(stream); h264_free(stream);
} }
else { else {
WriteUnmodifiedBuffer:
// Write the buffer as-is // Write the buffer as-is
memcpy(&m_DecodeBuffer.data()[offset], memcpy(&m_DecodeBuffer.data()[offset],
entry->data, entry->data,
+1
View File
@@ -12,6 +12,7 @@ namespace WMUtils {
bool isRunningWayland(); bool isRunningWayland();
bool isRunningWindowManager(); bool isRunningWindowManager();
bool isRunningDesktopEnvironment(); bool isRunningDesktopEnvironment();
bool hasDisplayUsableBounds();
QString getDrmCardOverride(); QString getDrmCardOverride();
bool isGpuSlow(); bool isGpuSlow();
} }
+46
View File
@@ -205,6 +205,52 @@ bool WMUtils::isRunningDesktopEnvironment()
#endif #endif
} }
bool WMUtils::hasDisplayUsableBounds()
{
static SDL_atomic_t hasDisplayUsableBounds;
// If the value is not set yet, populate it now.
int val = SDL_AtomicGet(&hasDisplayUsableBounds);
if (!(val & VALUE_SET)) {
int numDisplays = SDL_GetNumVideoDisplays();
bool hasUsableBounds = false;
for (int i = 0; i < numDisplays; i++) {
SDL_Rect displayBounds;
SDL_Rect usableBounds;
if (SDL_GetDisplayBounds(i, &displayBounds) != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
continue;
}
if (SDL_GetDisplayUsableBounds(i, &usableBounds) != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayUsableBounds(%d) failed: %s",
i, SDL_GetError());
continue;
}
// If at least one display has usable bounds that differ from the
// complete bounds, we assume that usable bounds are accurate.
if (!SDL_RectEquals(&displayBounds, &usableBounds)) {
hasUsableBounds = true;
break;
}
}
// Populate the value to return and have for next time.
// This can race with another thread populating the same data,
// but that's no big deal.
val = VALUE_SET | (hasUsableBounds ? VALUE_TRUE : 0);
SDL_AtomicSet(&hasDisplayUsableBounds, val);
}
return !!(val & VALUE_TRUE);
}
bool WMUtils::isGpuSlow() bool WMUtils::isGpuSlow()
{ {
bool ret; bool ret;
+31 -7
View File
@@ -1,3 +1,25 @@
/*
* h264bitstream - a library for reading and writing H.264 video
* Copyright (C) 2005-2007 Auroras Entertainment, LLC
* Copyright (C) 2008-2011 Avail-TVN
*
* Written by Alex Izvorski <aizvorski@gmail.com> and Alex Giladi <alex.giladi@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _H264_BS_H #ifndef _H264_BS_H
#define _H264_BS_H 1 #define _H264_BS_H 1
@@ -79,7 +101,7 @@ static inline int bs_eof(bs_t* b) { if (b->p >= b->end) { return 1; } else { ret
static inline int bs_overrun(bs_t* b) { if (b->p > b->end) { return 1; } else { return 0; } } static inline int bs_overrun(bs_t* b) { if (b->p > b->end) { return 1; } else { return 0; } }
static inline int bs_pos(bs_t* b) { if (b->p > b->end) { return (b->end - b->start); } else { return (b->p - b->start); } } static inline int bs_pos(bs_t* b) { if (b->p > b->end) { return (int)(b->end - b->start); } else { return (int)(b->p - b->start); } }
static inline uint32_t bs_read_u1(bs_t* b) static inline uint32_t bs_read_u1(bs_t* b)
{ {
@@ -156,16 +178,18 @@ static inline uint32_t bs_read_ue(bs_t* b)
static inline int32_t bs_read_se(bs_t* b) static inline int32_t bs_read_se(bs_t* b)
{ {
int32_t r = bs_read_ue(b); // Do the math in unsigned space. bs_read_ue() can return values above
// INT32_MAX, and (r+1)/2 on a signed INT32_MAX is undefined behavior.
// bs_read_ue() returns at most 0xFFFFFFFE, so both results fit in int32_t.
uint32_t r = bs_read_ue(b);
if (r & 0x01) if (r & 0x01)
{ {
r = (r+1)/2; return (int32_t)((r >> 1) + 1);
} }
else else
{ {
r = -(r/2); return -(int32_t)(r >> 1);
} }
return r;
} }
static inline void bs_write_u1(bs_t* b, uint32_t v) static inline void bs_write_u1(bs_t* b, uint32_t v)
@@ -186,7 +210,7 @@ static inline void bs_write_u(bs_t* b, int n, uint32_t v)
int i; int i;
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
bs_write_u1(b, (v >> ( n - i - 1 ))&0x01 ); bs_write_u1(b, (n - i - 1 >= 32) ? 0 : ((v >> ( n - i - 1 ))&0x01) );
} }
} }
@@ -195,7 +219,7 @@ static inline void bs_write_u8(bs_t* b, uint32_t v)
#ifdef FAST_U8 #ifdef FAST_U8
if (b->bits_left == 8 && ! bs_eof(b)) // can do fast write if (b->bits_left == 8 && ! bs_eof(b)) // can do fast write
{ {
b->p[0] = v; b->p[0] = (uint8_t)v;
b->p++; b->p++;
return; return;
} }
+22
View File
@@ -1,3 +1,25 @@
/*
* h264bitstream - a library for reading and writing H.264 video
* Copyright (C) 2005-2007 Auroras Entertainment, LLC
* Copyright (C) 2008-2011 Avail-TVN
*
* Written by Alex Izvorski <aizvorski@gmail.com> and Alex Giladi <alex.giladi@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
+33
View File
@@ -1,3 +1,26 @@
/*
* h264bitstream - a library for reading and writing H.264 video
* Copyright (C) 2005-2007 Auroras Entertainment, LLC
* Copyright (C) 2008-2011 Avail-TVN
* Copyright (C) 2012 Alex Izvorski
*
* Written by Alex Izvorski <aizvorski@gmail.com> and Alex Giladi <alex.giladi@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@@ -361,6 +384,11 @@ void write_seq_parameter_set_rbsp(sps_t* sps, bs_t* b)
} }
else if( sps->pic_order_cnt_type == 1 ) else if( sps->pic_order_cnt_type == 1 )
{ {
if (sps->num_ref_frames_in_pic_order_cnt_cycle > 256)
{
sps->num_ref_frames_in_pic_order_cnt_cycle = 256;
}
bs_write_u1(b, sps->delta_pic_order_always_zero_flag); bs_write_u1(b, sps->delta_pic_order_always_zero_flag);
bs_write_se(b, sps->offset_for_non_ref_pic); bs_write_se(b, sps->offset_for_non_ref_pic);
bs_write_se(b, sps->offset_for_top_to_bottom_field); bs_write_se(b, sps->offset_for_top_to_bottom_field);
@@ -506,6 +534,11 @@ void write_vui_parameters(sps_t* sps, bs_t* b)
void write_hrd_parameters(hrd_t* hrd, bs_t* b) void write_hrd_parameters(hrd_t* hrd, bs_t* b)
{ {
if (hrd->cpb_cnt_minus1 > 31)
{
hrd->cpb_cnt_minus1 = 31;
}
bs_write_ue(b, hrd->cpb_cnt_minus1); bs_write_ue(b, hrd->cpb_cnt_minus1);
bs_write_u(b, 4, hrd->bit_rate_scale); bs_write_u(b, 4, hrd->bit_rate_scale);
bs_write_u(b, 4, hrd->cpb_size_scale); bs_write_u(b, 4, hrd->cpb_size_scale);
+22
View File
@@ -1,3 +1,25 @@
/*
* h264bitstream - a library for reading and writing H.264 video
* Copyright (C) 2005-2007 Auroras Entertainment, LLC
* Copyright (C) 2008-2011 Avail-TVN
*
* Written by Alex Izvorski <aizvorski@gmail.com> and Alex Giladi <alex.giladi@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _H264_STREAM_H #ifndef _H264_STREAM_H
#define _H264_STREAM_H 1 #define _H264_STREAM_H 1
-3
View File
@@ -13,9 +13,6 @@ TEMPLATE = lib
# Build a static library # Build a static library
CONFIG += staticlib CONFIG += staticlib
# Disable warnings
CONFIG += warn_off
# Include global qmake defs # Include global qmake defs
include(../globaldefs.pri) include(../globaldefs.pri)
+1 -1
View File
@@ -250,7 +250,7 @@ if not x%QT_PATH:\5.=%==x%QT_PATH% (
set WINDEPLOYQT_ARGS=--no-qmltooling --no-virtualkeyboard set WINDEPLOYQT_ARGS=--no-qmltooling --no-virtualkeyboard
) else ( ) else (
rem Qt 6.8+ rem Qt 6.8+
set WINDEPLOYQT_ARGS=--no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types qmltooling,generic --no-ffmpeg set WINDEPLOYQT_ARGS=--no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types qmltooling,generic --no-ffmpeg --force-openssl
set WINDEPLOYQT_ARGS=!WINDEPLOYQT_ARGS! --no-quickcontrols2fusion --no-quickcontrols2imagine --no-quickcontrols2universal set WINDEPLOYQT_ARGS=!WINDEPLOYQT_ARGS! --no-quickcontrols2fusion --no-quickcontrols2imagine --no-quickcontrols2universal
set WINDEPLOYQT_ARGS=!WINDEPLOYQT_ARGS! --no-quickcontrols2fusionstyleimpl --no-quickcontrols2imaginestyleimpl --no-quickcontrols2universalstyleimpl --no-quickcontrols2windowsstyleimpl --no-quickcontrols2fluentwinui3styleimpl set WINDEPLOYQT_ARGS=!WINDEPLOYQT_ARGS! --no-quickcontrols2fusionstyleimpl --no-quickcontrols2imaginestyleimpl --no-quickcontrols2universalstyleimpl --no-quickcontrols2windowsstyleimpl --no-quickcontrols2fluentwinui3styleimpl
) )