/* * Olive Community Edition - Non-Linear Video Editor * Copyright (C) 2025 Olive CE Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "conformmanager.h" #include #include "task/taskmanager.h" namespace olive { ConformManager *ConformManager::instance_ = nullptr; ConformManager::Conform ConformManager::GetConformState( const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms, bool wait) { // Mutex because we'll need to check the status of a conform task QMutexLocker locker(&mutex_); // Return existing conform if exists QVector filenames = GetConformedFilename(cache_path, stream, params); if (AllConformsExist(filenames)) { return { kConformExists, filenames, nullptr }; } ConformTask *conforming_task = nullptr; foreach (const ConformData &data, conforming_) { if (data.stream == stream && data.params == params) { // Already creating conform in a task conforming_task = data.task; break; } } if (!conforming_task) { // Not conforming yet, create a task to do so // We conform to a different filename until it's done to make it clear even across sessions // whether this conform is ready or not QVector working_filenames = filenames; for (int i = 0; i < working_filenames.size(); i++) { working_filenames[i].append(QStringLiteral(".working")); } conforming_task = new ConformTask(decoder_id, stream, params, working_filenames); connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished); conforming_task->moveToThread(TaskManager::instance()->thread()); QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task)); conforming_.append( { stream, params, conforming_task, working_filenames, filenames }); } if (wait) { do { conform_done_condition_.wait(&mutex_); } while (!AllConformsExist(filenames)); return { kConformExists, filenames, nullptr }; } return { kConformGenerating, QVector(), conforming_task }; } QVector ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms) { QVector filenames(params.channel_count()); for (int i = 0; i < filenames.size(); i++) { QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm") .arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), QString::number(stream.stream()), QString::number(params.sample_rate()), QString::number(params.format()), QString::number(params.channel_layout().u.mask), QString::number(i)); filenames[i] = QDir(cache_path).filePath(index_fn); } return filenames; } bool ConformManager::AllConformsExist(const QVector &filenames) { foreach (const QString &fn, filenames) { if (!QFileInfo::exists(fn)) { return false; } } return true; } void ConformManager::ConformTaskFinished(Task *task, bool succeeded) { QMutexLocker locker(&mutex_); ConformData data; // Remove conform data from list for (int i = 0; i < conforming_.size(); i++) { const ConformData &c = conforming_.at(i); if (c.task == task) { data = c; conforming_.removeAt(i); break; } } if (succeeded) { // Move file to standard conform name, making it clear this conform is ready for use for (int i = 0; i < data.finished_filename.size(); i++) { const QString &finished = data.finished_filename.at(i); const QString &working = data.working_filename.at(i); QFile::remove(finished); QFile::rename(working, finished); } conform_done_condition_.wakeAll(); locker.unlock(); emit ConformReady(); } else { // Failed, just delete the working filename if exists for (int i = 0; i < data.working_filename.size(); i++) { QFile::remove(data.working_filename.at(i)); } } } }