feat: add vaapi path for video wallpapers on generic gpus
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 32s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Failing after 1m23s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m2s

This commit is contained in:
2026-10-27 02:15:16 +01:00
parent e655ec5bc7
commit b0cb43f75c
39 changed files with 1670 additions and 599 deletions
@@ -0,0 +1,165 @@
#include "videotranscoder.hpp"
#include <QCryptographicHash>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QProcess>
#include <QStringList>
#include <QtLogging>
Q_LOGGING_CATEGORY(transcodeLog, "ZShell.Internal.wallpaper")
namespace ZShell::wallpaper {
VideoTranscoder::VideoTranscoder(QObject* parent) : QObject(parent) {}
bool VideoTranscoder::isHardwareCodec(const QString& codec) {
static const QStringList hardware{
"av1",
"hevc",
"h264",
"vp9",
"vp8",
"vc1",
"mpeg2",
"mpeg2video",
"mpeg4",
"mjpeg",
};
return hardware.contains(codec);
}
QString VideoTranscoder::codecName(const QString& path) const {
QProcess probe;
probe.start(
"ffprobe",
{
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=codec_name",
"-of",
"default=nw=1:nk=1",
path,
});
if (!probe.waitForFinished(2000) || probe.exitCode() != 0) {
qWarning(transcodeLog) << "WallpaperService: ffprobe failed for" << path
<< "(exit" << probe.exitCode() << ")";
return {};
}
return QString::fromUtf8(probe.readAllStandardOutput()).trimmed().toLower();
}
QString VideoTranscoder::cacheDir() const {
return QDir::homePath() + "/.cache/zshell/wallpaper-videos";
}
QString VideoTranscoder::transcodeTarget(const QString& path) const {
const QFileInfo info(path);
const QByteArray key =
path.toUtf8() + '|' + QByteArray::number(info.size()) + '|' +
QByteArray::number(info.lastModified().toMSecsSinceEpoch());
const QString hash = QString::fromUtf8(
QCryptographicHash::hash(key, QCryptographicHash::Sha1).toHex());
return cacheDir() + '/' + hash + ".av1.mkv";
}
bool VideoTranscoder::needsTranscode(const QString& path) const {
if (path.startsWith(cacheDir())) return false;
const QString codec = codecName(path);
if (codec.isEmpty()) return false;
return !isHardwareCodec(codec);
}
void VideoTranscoder::start(const QString& src, const QString& dst) {
QDir().mkpath(cacheDir());
if (m_process) {
m_process->kill();
m_process->deleteLater();
m_process = nullptr;
}
m_process = new QProcess(this);
auto* proc = m_process;
connect(proc, &QProcess::readyReadStandardError, proc, [proc]() {
qWarning(transcodeLog).noquote()
<< "ffmpeg:" << QString::fromUtf8(proc->readAllStandardError());
});
connect(
proc,
&QProcess::finished,
this,
[this, proc, dst](int code, QProcess::ExitStatus status) {
const bool ok = code == 0 && status == QProcess::NormalExit &&
QFile::exists(dst);
qWarning(transcodeLog)
<< "WallpaperService: transcode finished, exit" << code
<< "status" << int(status) << "exists" << QFile::exists(dst)
<< "->" << dst;
if (proc == m_process) m_process = nullptr;
proc->deleteLater();
emit transcoded(dst, ok);
});
qWarning(transcodeLog) << "WallpaperService: transcoding" << src << "->"
<< dst;
proc->setProgram("nice");
proc->setArguments({
"-n",
"19",
"ffmpeg",
"-hide_banner",
"-loglevel",
"error",
"-y",
"-threads",
"2",
"-i",
src,
"-map",
"0:v:0",
"-vf",
"scale=in_range=pc:out_range=tv:out_color_matrix=bt709",
"-colorspace",
"bt709",
"-color_primaries",
"bt709",
"-color_trc",
"bt709",
"-color_range",
"tv",
"-c:v",
"libsvtav1",
"-preset",
"6",
"-crf",
"20",
"-pix_fmt",
"yuv420p10le",
"-an",
dst,
});
proc->start();
}
} // namespace ZShell::wallpaper