proxy: per-footage presets, audio in proxies, configurable ffmpeg path
- Footage can store custom proxy parameters (width/height/crf/preset/ extension/audio) that override the global settings; they are serialized with the project and used by every generation entry point via Footage::GetEffectiveProxyParams() - Proxies now include the source audio streams (AAC) unless disabled; the proxy filename records the audio flag and offline audio rendering decodes from the proxy when present - ProxyTask resolves ffmpeg from the new FFmpegPath config key first, then PATH, then common install locations (e.g. Homebrew on macOS), instead of relying on PATH only; the error message points at the preferences when no executable is found - ProxyTask::BuildArguments() is extracted for testability - Add ProxyIncludeAudio and FFmpegPath config defaults - Add regression tests for the filename audio marker, config-backed params, ffmpeg resolution, argument building, and custom-param persistence
This commit is contained in:
@@ -18,11 +18,14 @@
|
||||
|
||||
#include "proxymanager.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QMutexLocker>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "task/proxy/proxy.h"
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
@@ -36,7 +39,8 @@ bool ProxyParamsEqual(const ProxyManager::ProxyParams &a,
|
||||
{
|
||||
return a.width == b.width && a.height == b.height &&
|
||||
a.version == b.version && a.extension == b.extension &&
|
||||
a.crf == b.crf && a.preset == b.preset;
|
||||
a.crf == b.crf && a.preset == b.preset &&
|
||||
a.include_audio == b.include_audio;
|
||||
}
|
||||
|
||||
QString ProxyManager::GetProxyDirectory(const QString &cache_path)
|
||||
@@ -53,11 +57,12 @@ QString ProxyManager::GetProxyFilename(const QString &cache_path,
|
||||
const QString extension =
|
||||
params.extension.isEmpty() ? QStringLiteral("mp4") : params.extension;
|
||||
const QString filename =
|
||||
QStringLiteral("%1-%2.%3x%4.v%5.%6")
|
||||
QStringLiteral("%1-%2.%3x%4.v%5.a%6.%7")
|
||||
.arg(FileFunctions::GetUniqueFileIdentifier(source_filename),
|
||||
QString::number(stream_index), QString::number(params.width),
|
||||
QString::number(params.height),
|
||||
QString::number(params.version), extension);
|
||||
QString::number(params.height), QString::number(params.version),
|
||||
params.include_audio ? QStringLiteral("1") : QStringLiteral("0"),
|
||||
extension);
|
||||
|
||||
return QDir(proxy_dir).filePath(filename);
|
||||
}
|
||||
@@ -117,6 +122,70 @@ ProxyManager::ProxyStateFromString(const QString &state)
|
||||
return kProxyMissing;
|
||||
}
|
||||
|
||||
bool ProxyManager::ProxyFilenameHasAudio(const QString &proxy_filename)
|
||||
{
|
||||
return QFileInfo(proxy_filename).fileName().contains(
|
||||
QStringLiteral(".a1."));
|
||||
}
|
||||
|
||||
ProxyManager::ProxyParams ProxyManager::ProxyParamsFromConfig()
|
||||
{
|
||||
ProxyParams params;
|
||||
params.width = OLIVE_CONFIG("ProxyWidth").value<int>();
|
||||
params.height = OLIVE_CONFIG("ProxyHeight").value<int>();
|
||||
params.crf = OLIVE_CONFIG("ProxyCRF").value<int>();
|
||||
params.preset = OLIVE_CONFIG("ProxyPreset").toString();
|
||||
params.include_audio = OLIVE_CONFIG("ProxyIncludeAudio").toBool();
|
||||
return params;
|
||||
}
|
||||
|
||||
QString ProxyManager::FindFFmpegExecutable(const QString &configured_path)
|
||||
{
|
||||
// An explicitly configured path takes precedence if it is usable
|
||||
if (!configured_path.isEmpty()) {
|
||||
const QFileInfo configured_info(configured_path);
|
||||
if (configured_info.exists() && configured_info.isFile() &&
|
||||
configured_info.isExecutable()) {
|
||||
return configured_info.absoluteFilePath();
|
||||
}
|
||||
|
||||
qWarning() << "Configured ffmpeg path is not a valid executable:"
|
||||
<< configured_path;
|
||||
}
|
||||
|
||||
// Fall back to searching the system PATH
|
||||
const QString from_path =
|
||||
QStandardPaths::findExecutable(QStringLiteral("ffmpeg"));
|
||||
if (!from_path.isEmpty()) {
|
||||
return from_path;
|
||||
}
|
||||
|
||||
// Finally, try common install locations (PATH on GUI-launched apps,
|
||||
// particularly on macOS, often lacks these)
|
||||
QStringList candidates;
|
||||
candidates.append(QCoreApplication::applicationDirPath() +
|
||||
QStringLiteral("/ffmpeg"));
|
||||
#ifdef Q_OS_MAC
|
||||
candidates.append(QStringLiteral("/opt/homebrew/bin/ffmpeg"));
|
||||
candidates.append(QStringLiteral("/usr/local/bin/ffmpeg"));
|
||||
#endif
|
||||
#ifdef Q_OS_WINDOWS
|
||||
candidates.append(QCoreApplication::applicationDirPath() +
|
||||
QStringLiteral("/ffmpeg.exe"));
|
||||
#endif
|
||||
candidates.append(QStringLiteral("/usr/bin/ffmpeg"));
|
||||
candidates.append(QStringLiteral("/usr/local/bin/ffmpeg"));
|
||||
|
||||
for (const QString &candidate : candidates) {
|
||||
const QFileInfo info(candidate);
|
||||
if (info.exists() && info.isFile() && info.isExecutable()) {
|
||||
return info.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
ProxyManager::Proxy
|
||||
ProxyManager::GetOrStartProxy(const QString &cache_path,
|
||||
const QString &source_filename, int stream_index,
|
||||
|
||||
Reference in New Issue
Block a user