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
166 lines
3.3 KiB
C++
166 lines
3.3 KiB
C++
#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
|