change: change code style to Linux style except indent.

This commit is contained in:
Mike Solar
2025-08-03 03:09:40 +08:00
parent 65ab76edc8
commit 74f73ab3be
789 changed files with 77113 additions and 68888 deletions
+98 -84
View File
@@ -4,123 +4,137 @@
#include "task/taskmanager.h"
namespace olive {
namespace olive
{
ConformManager *ConformManager::instance_ = nullptr;
ConformManager::Conform ConformManager::GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait)
ConformManager::Conform ConformManager::GetConformState(
const QString &decoder_id, const QString &cache_path,
const Decoder::CodecStream &stream, const AudioParams &params, bool wait)
{
// Mutex because we'll need to check the status of a conform task
QMutexLocker locker(&mutex_);
// Mutex because we'll need to check the status of a conform task
QMutexLocker locker(&mutex_);
// Return existing conform if exists
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) {
return {kConformExists, filenames, nullptr};
}
// Return existing conform if exists
QVector<QString> filenames =
GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) {
return { kConformExists, filenames, nullptr };
}
ConformTask *conforming_task = 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;
}
}
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
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<QString> working_filenames = filenames;
for (int i=0; i<working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working"));
}
// 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<QString> 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_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});
}
conforming_.append(
{ stream, params, conforming_task, working_filenames, filenames });
}
if (wait) {
do {
conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames));
return {kConformExists, filenames, nullptr};
}
if (wait) {
do {
conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames));
return { kConformExists, filenames, nullptr };
}
return {kConformGenerating, QVector<QString>(), conforming_task};
return { kConformGenerating, QVector<QString>(), conforming_task };
}
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params)
QVector<QString>
ConformManager::GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params)
{
QVector<QString> filenames(params.channel_count());
QVector<QString> 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));
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);
}
filenames[i] = QDir(cache_path).filePath(index_fn);
}
return filenames;
return filenames;
}
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
{
foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) {
return false;
}
}
foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) {
return false;
}
}
return true;
return true;
}
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
ConformData data;
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;
}
}
// 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);
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);
}
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));
}
}
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));
}
}
}
}
+57 -55
View File
@@ -7,82 +7,84 @@
#include "decoder.h"
#include "task/conform/conform.h"
namespace olive {
class ConformManager : public QObject
namespace olive
{
Q_OBJECT
class ConformManager : public QObject {
Q_OBJECT
public:
static void CreateInstance()
{
if (!instance_) {
instance_ = new ConformManager();
}
}
static void CreateInstance()
{
if (!instance_) {
instance_ = new ConformManager();
}
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static ConformManager *instance()
{
return instance_;
}
static ConformManager *instance()
{
return instance_;
}
enum ConformState {
kConformExists,
kConformGenerating
};
enum ConformState { kConformExists, kConformGenerating };
struct Conform {
ConformState state;
QVector<QString> filenames;
ConformTask *task;
};
struct Conform {
ConformState state;
QVector<QString> filenames;
ConformTask *task;
};
/**
* @brief Get conform state, and start conforming if no conform exists
*
* Thread-safe.
*/
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait);
/**
* @brief Get conform state, and start conforming if no conform exists
*
* Thread-safe.
*/
Conform GetConformState(const QString &decoder_id,
const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params, bool wait);
signals:
void ConformReady();
void ConformReady();
private:
ConformManager() = default;
ConformManager() = default;
static ConformManager *instance_;
static ConformManager *instance_;
QMutex mutex_;
QMutex mutex_;
QWaitCondition conform_done_condition_;
QWaitCondition conform_done_condition_;
struct ConformData {
Decoder::CodecStream stream;
AudioParams params;
ConformTask *task;
QVector<QString> working_filename;
QVector<QString> finished_filename;
};
struct ConformData {
Decoder::CodecStream stream;
AudioParams params;
ConformTask *task;
QVector<QString> working_filename;
QVector<QString> finished_filename;
};
QVector<ConformData> conforming_;
QVector<ConformData> conforming_;
/**
* @brief Get the destination filename of an audio stream conformed to a set of parameters
*/
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params);
/**
* @brief Get the destination filename of an audio stream conformed to a set of parameters
*/
static QVector<QString>
GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params);
static bool AllConformsExist(const QVector<QString> &filenames);
static bool AllConformsExist(const QVector<QString> &filenames);
private slots:
void ConformTaskFinished(Task *task, bool succeeded);
void ConformTaskFinished(Task *task, bool succeeded);
};
}
} // namespace olive
#endif // CONFORMMANAGER_H
+215 -185
View File
@@ -33,151 +33,162 @@
#include "node/project.h"
#include "task/taskmanager.h"
namespace olive {
namespace olive
{
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
Decoder::Decoder() :
cached_texture_(nullptr)
Decoder::Decoder()
: cached_texture_(nullptr)
{
UpdateLastAccessed();
UpdateLastAccessed();
}
void Decoder::IncrementAccessTime(qint64 t)
{
last_accessed_ += t;
last_accessed_ += t;
}
bool Decoder::Open(const CodecStream &stream)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == stream) {
return true;
} else {
qWarning() << "Tried to open a decoder that was already open with another stream";
return false;
}
} else {
// Stream was not open, try opening it now
if (!stream.IsValid()) {
// Cannot open null stream
qCritical() << "Decoder attempted to open null stream";
return false;
}
if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == stream) {
return true;
} else {
qWarning()
<< "Tried to open a decoder that was already open with another stream";
return false;
}
} else {
// Stream was not open, try opening it now
if (!stream.IsValid()) {
// Cannot open null stream
qCritical() << "Decoder attempted to open null stream";
return false;
}
if (!stream.Exists()) {
// Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist";
return false;
}
if (!stream.Exists()) {
// Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist";
return false;
}
// Set stream
stream_ = stream;
// Set stream
stream_ = stream;
// Try open internal
if (OpenInternal()) {
return true;
} else {
// Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream();
CloseInternal();
stream_.Reset();
return false;
}
}
// Try open internal
if (OpenInternal()) {
return true;
} else {
// Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream"
<< stream_.stream();
CloseInternal();
stream_.Reset();
return false;
}
}
}
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder";
return nullptr;
}
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder";
return nullptr;
}
if (!SupportsVideo()) {
qCritical() << "Decoder doesn't support video";
return nullptr;
}
if (!SupportsVideo()) {
qCritical() << "Decoder doesn't support video";
return nullptr;
}
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) {
return cached_texture_;
}
if (cached_texture_ && cached_time_ == p.time &&
cached_divider_ == p.divider) {
return cached_texture_;
}
cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time;
cached_divider_ = p.divider;
cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time;
cached_divider_ = p.divider;
return cached_texture_;
return cached_texture_;
}
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode)
Decoder::RetrieveAudioStatus
Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve audio on a closed decoder";
return kInvalid;
}
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve audio on a closed decoder";
return kInvalid;
}
if (!SupportsAudio()) {
qCritical() << "Decoder doesn't support audio";
return kInvalid;
}
if (!SupportsAudio()) {
qCritical() << "Decoder doesn't support audio";
return kInvalid;
}
// Get conform state from ConformManager
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
if (conform.state == ConformManager::kConformGenerating) {
// If we need the task, it's available in `conform.task`
return kWaitingForConform;
}
// Get conform state from ConformManager
ConformManager::Conform conform =
ConformManager::instance()->GetConformState(
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
if (conform.state == ConformManager::kConformGenerating) {
// If we need the task, it's available in `conform.task`
return kWaitingForConform;
}
// See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) {
return kOK;
} else {
return kUnknownError;
}
// See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
params)) {
return kOK;
} else {
return kUnknownError;
}
}
qint64 Decoder::GetLastAccessedTime()
{
return last_accessed_;
return last_accessed_;
}
void Decoder::Close()
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
cached_texture_ = nullptr;
cached_texture_ = nullptr;
if (stream_.IsValid()) {
CloseInternal();
stream_.Reset();
} else {
qWarning() << "Tried to close a decoder that wasn't open";
}
if (stream_.IsValid()) {
CloseInternal();
stream_.Reset();
} else {
qWarning() << "Tried to close a decoder that wasn't open";
}
}
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, CancelAtom *cancelled)
bool Decoder::ConformAudio(const QVector<QString> &output_filenames,
const AudioParams &params, CancelAtom *cancelled)
{
return ConformAudioInternal(output_filenames, params, cancelled);
return ConformAudioInternal(output_filenames, params, cancelled);
}
/*
@@ -186,159 +197,178 @@ bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const Audio
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
{
QVector<DecoderPtr> decoders;
QVector<DecoderPtr> decoders;
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
decoders.append(std::make_shared<OIIODecoder>());
decoders.append(std::make_shared<FFmpegDecoder>());
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
decoders.append(std::make_shared<OIIODecoder>());
decoders.append(std::make_shared<FFmpegDecoder>());
return decoders;
return decoders;
}
DecoderPtr Decoder::CreateFromID(const QString &id)
{
if (id.isEmpty()) {
return nullptr;
}
if (id.isEmpty()) {
return nullptr;
}
// Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
// Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
foreach (DecoderPtr d, decoder_list) {
if (d->id() == id) {
return d;
}
}
foreach (DecoderPtr d, decoder_list) {
if (d->id() == id) {
return d;
}
}
return nullptr;
return nullptr;
}
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
{
if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration));
}
if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) /
static_cast<double>(duration));
}
}
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number)
QString Decoder::TransformImageSequenceFileName(const QString &filename,
const int64_t &number)
{
int digit_count = GetImageSequenceDigitCount(filename);
int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename);
QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName();
QString original_basename = file_info.completeBaseName();
QString new_basename = original_basename.left(original_basename.size() - digit_count)
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
QString new_basename =
original_basename.left(original_basename.size() - digit_count)
.append(
QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
return file_info.dir().filePath(file_info.fileName().replace(original_basename, new_basename));
return file_info.dir().filePath(
file_info.fileName().replace(original_basename, new_basename));
}
int Decoder::GetImageSequenceDigitCount(const QString &filename)
{
QString basename = QFileInfo(filename).completeBaseName();
QString basename = QFileInfo(filename).completeBaseName();
// See if basename contains a number at the end
int digit_count = 0;
// See if basename contains a number at the end
int digit_count = 0;
for (int i=basename.size()-1;i>=0;i--) {
if (basename.at(i).isDigit()) {
digit_count++;
} else {
break;
}
}
for (int i = basename.size() - 1; i >= 0; i--) {
if (basename.at(i).isDigit()) {
digit_count++;
} else {
break;
}
}
return digit_count;
return digit_count;
}
int64_t Decoder::GetImageSequenceIndex(const QString &filename)
{
int digit_count = GetImageSequenceDigitCount(filename);
int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename);
QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName();
QString original_basename = file_info.completeBaseName();
QString number_only = original_basename.mid(original_basename.size() - digit_count);
QString number_only =
original_basename.mid(original_basename.size() - digit_count);
return number_only.toLongLong();
return number_only.toLongLong();
}
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
Q_UNUSED(p)
return nullptr;
Q_UNUSED(p)
return nullptr;
}
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled)
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled)
{
Q_UNUSED(filenames)
Q_UNUSED(cancelled)
Q_UNUSED(params)
return false;
Q_UNUSED(filenames)
Q_UNUSED(cancelled)
Q_UNUSED(params)
return false;
}
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
bool Decoder::RetrieveAudioFromConform(
SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
{
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset
range -= GetAudioStartOffset();
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset
range -= GetAudioStartOffset();
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
qint64 write_index = 0;
qint64 read_index = input_params.time_to_bytes(range.in()) /
input_params.channel_count();
qint64 write_index = 0;
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
const qint64 buffer_length_in_bytes =
sample_buffer.sample_count() *
input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
}
while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
}
while (read_index < 0) {
read_index += input.size();
}
}
while (read_index < 0) {
read_index += input.size();
}
}
qint64 write_count = 0;
qint64 write_count = 0;
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index;
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else {
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
}
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index;
sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else {
write_count = qMin(input.size() - read_index,
buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char **>(
sample_buffer.to_raw_ptrs().data()),
write_count, write_index);
}
read_index += write_count;
write_index += write_count;
}
read_index += write_count;
write_index += write_count;
}
input.close();
input.close();
return true;
}
return true;
}
return false;
return false;
}
void Decoder::UpdateLastAccessed()
{
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
}
uint qHash(Decoder::CodecStream stream, uint seed)
{
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
qHash(stream.block(), seed);
}
}
+143 -126
View File
@@ -36,12 +36,17 @@ extern "C" {
#include "render/cancelatom.h"
#include "render/rendermodes.h"
namespace olive {
namespace olive
{
class Decoder;
using DecoderPtr = std::shared_ptr<Decoder>;
#define DECODER_DEFAULT_DESTRUCTOR(x) virtual ~x() override {CloseInternal();}
#define DECODER_DEFAULT_DESTRUCTOR(x) \
virtual ~x() override \
{ \
CloseInternal(); \
}
/**
* @brief A decoder's is the main class for bringing external media into Olive
@@ -59,89 +64,88 @@ using DecoderPtr = std::shared_ptr<Decoder>;
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
* to be utilized in the rest of the rendering pipeline.
*/
class Decoder : public QObject
{
Q_OBJECT
class Decoder : public QObject {
Q_OBJECT
public:
enum RetrieveState {
kReady,
kFailedToOpen,
kIndexUnavailable
};
enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
Decoder();
Decoder();
/**
/**
* @brief Unique decoder ID
*/
virtual QString id() const = 0;
virtual QString id() const = 0;
virtual bool SupportsVideo(){return false;}
virtual bool SupportsAudio(){return false;}
virtual bool SupportsVideo()
{
return false;
}
virtual bool SupportsAudio()
{
return false;
}
void IncrementAccessTime(qint64 t);
void IncrementAccessTime(qint64 t);
class CodecStream
{
public:
CodecStream() :
stream_(-1),
block_(nullptr)
{
}
class CodecStream {
public:
CodecStream()
: stream_(-1)
, block_(nullptr)
{
}
CodecStream(const QString& filename, int stream, Block *block) :
filename_(filename),
stream_(stream),
block_(block)
{
}
CodecStream(const QString &filename, int stream, Block *block)
: filename_(filename)
, stream_(stream)
, block_(block)
{
}
bool IsValid() const
{
return !filename_.isEmpty() && stream_ >= 0;
}
bool IsValid() const
{
return !filename_.isEmpty() && stream_ >= 0;
}
bool Exists() const
{
return QFileInfo::exists(filename_);
}
bool Exists() const
{
return QFileInfo::exists(filename_);
}
void Reset()
{
*this = CodecStream();
}
void Reset()
{
*this = CodecStream();
}
bool operator==(const CodecStream& rhs) const
{
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
}
bool operator==(const CodecStream &rhs) const
{
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
}
const QString& filename() const
{
return filename_;
}
const QString &filename() const
{
return filename_;
}
int stream() const
{
return stream_;
}
int stream() const
{
return stream_;
}
Block *block() const
{
return block_;
}
Block *block() const
{
return block_;
}
private:
QString filename_;
private:
QString filename_;
int stream_;
int stream_;
Block *block_;
Block *block_;
};
};
/**
/**
* @brief Open stream for decoding
*
* This function is thread safe.
@@ -150,22 +154,21 @@ public:
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
* be opened OR if already open and the stream is NOT the same.
*/
bool Open(const CodecStream& stream);
bool Open(const CodecStream &stream);
static const rational kAnyTimecode;
static const rational kAnyTimecode;
struct RetrieveVideoParams
{
Renderer *renderer = nullptr;
rational time;
int divider = 1;
PixelFormat maximum_format = PixelFormat::INVALID;
CancelAtom *cancelled = nullptr;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
};
struct RetrieveVideoParams {
Renderer *renderer = nullptr;
rational time;
int divider = 1;
PixelFormat maximum_format = PixelFormat::INVALID;
CancelAtom *cancelled = nullptr;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
};
/**
/**
* @brief Retrieves a video frame from footage
*
* This function will always return a valid frame unless a fatal error occurs (in such case,
@@ -175,16 +178,16 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
enum RetrieveAudioStatus {
kInvalid = -1,
kOK,
kWaitingForConform,
kUnknownError
};
enum RetrieveAudioStatus {
kInvalid = -1,
kOK,
kWaitingForConform,
kUnknownError
};
/**
/**
* @brief Retrieve audio data from footage
*
* This function will always return a sample buffer unless a fatal error occurs (in such case,
@@ -192,14 +195,17 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode);
RetrieveAudioStatus
RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode);
/**
/**
* @brief Determine the last time this decoder instance was used in any way
*/
qint64 GetLastAccessedTime();
qint64 GetLastAccessedTime();
/**
/**
* @brief Generate a Footage object from a file
*
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
@@ -210,39 +216,43 @@ public:
*
* This function is re-entrant.
*/
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const = 0;
/**
/**
* @brief Closes media/deallocates memory
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
void Close();
void Close();
/**
/**
* @brief Conform audio stream
*/
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, CancelAtom *cancelled = nullptr);
bool ConformAudio(const QVector<QString> &output_filenames,
const AudioParams &params,
CancelAtom *cancelled = nullptr);
/**
/**
* @brief Create a Decoder instance using a Decoder ID
*
* @return
*
* A Decoder instance or nullptr if a Decoder with this ID does not exist
*/
static DecoderPtr CreateFromID(const QString& id);
static DecoderPtr CreateFromID(const QString &id);
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
static QString TransformImageSequenceFileName(const QString &filename,
const int64_t &number);
static int GetImageSequenceDigitCount(const QString& filename);
static int GetImageSequenceDigitCount(const QString &filename);
static int64_t GetImageSequenceIndex(const QString& filename);
static int64_t GetImageSequenceIndex(const QString &filename);
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
protected:
/**
/**
* @brief Internal open function
*
* Sub-classes must override this function. Function will already be mutexed, so there is no need
@@ -254,62 +264,69 @@ protected:
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
* memory allocated during OpenInternal.
*/
virtual bool OpenInternal() = 0;
virtual bool OpenInternal() = 0;
/**
/**
* @brief Internal close function
*
* Sub-classes must override this function. Function should be able to safely clear all allocated
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
*/
virtual void CloseInternal() = 0;
virtual void CloseInternal() = 0;
/**
/**
* @brief Internal frame retrieval function
*
* Sub-classes must override this function IF they support video. Function is already mutexed
* so sub-classes don't need to worry about thread safety.
*/
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled);
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled);
void SignalProcessingProgress(int64_t ts, int64_t duration);
void SignalProcessingProgress(int64_t ts, int64_t duration);
/**
/**
* @brief Return currently open stream
*
* This function is NOT thread safe and should therefore only be called by thread safe functions.
*/
const CodecStream& stream() const
{
return stream_;
}
const CodecStream &stream() const
{
return stream_;
}
virtual rational GetAudioStartOffset() const { return 0; }
virtual rational GetAudioStartOffset() const
{
return 0;
}
signals:
/**
/**
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
* available
*/
void IndexProgress(double);
void IndexProgress(double);
private:
void UpdateLastAccessed();
void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &params);
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer,
const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode,
const AudioParams &params);
CodecStream stream_;
CodecStream stream_;
QMutex mutex_;
QMutex mutex_;
std::atomic_int64_t last_accessed_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
std::atomic_int64_t last_accessed_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
};
uint qHash(Decoder::CodecStream stream, uint seed = 0);
+415 -339
View File
@@ -26,492 +26,568 @@
#include "ffmpeg/ffmpegencoder.h"
#include "oiio/oiioencoder.h"
namespace olive {
namespace olive
{
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceContainsDigits =
QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
Encoder::Encoder(const EncodingParams &params) :
params_(params)
Encoder::Encoder(const EncodingParams &params)
: params_(params)
{
}
const EncodingParams &Encoder::params() const
{
return params_;
return params_;
}
QString Encoder::GetFilenameForFrame(const rational &frame)
{
if (params().video_is_image_sequence()) {
// Transform!
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base());
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
if (params().video_is_image_sequence()) {
// Transform!
int64_t frame_index = Timecode::time_to_timestamp(
frame, params().video_params().frame_rate_as_time_base());
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str =
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str);
return f;
} else {
// Keep filename
return params_.filename();
}
QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str);
return f;
} else {
// Keep filename
return params_.filename();
}
}
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
{
int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0;
for (int i=start+1; i<filename.size(); i++) {
if (filename.at(i) == '#') {
digit_count++;
} else {
break;
}
}
return digit_count;
int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0;
for (int i = start + 1; i < filename.size(); i++) {
if (filename.at(i) == '#') {
digit_count++;
} else {
break;
}
}
return digit_count;
}
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename)
bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
{
return filename.contains(kImageSequenceContainsDigits);
return filename.contains(kImageSequenceContainsDigits);
}
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
{
return filename.remove(kImageSequenceRemoveDigits);
return filename.remove(kImageSequenceRemoveDigits);
}
EncodingParams::EncodingParams() :
video_enabled_(false),
video_bit_rate_(0),
video_min_bit_rate_(0),
video_max_bit_rate_(0),
video_buffer_size_(0),
video_threads_(0),
video_is_image_sequence_(false),
audio_enabled_(false),
audio_bit_rate_(0),
subtitles_enabled_(false),
subtitles_are_sidecar_(false),
video_scaling_method_(kStretch),
has_custom_range_(false)
EncodingParams::EncodingParams()
: video_enabled_(false)
, video_bit_rate_(0)
, video_min_bit_rate_(0)
, video_max_bit_rate_(0)
, video_buffer_size_(0)
, video_threads_(0)
, video_is_image_sequence_(false)
, audio_enabled_(false)
, audio_bit_rate_(0)
, subtitles_enabled_(false)
, subtitles_are_sidecar_(false)
, video_scaling_method_(kStretch)
, has_custom_range_(false)
{
}
QDir EncodingParams::GetPresetPath()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("exportpresets"));
}
QStringList EncodingParams::GetListOfPresets()
{
QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files);
QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files);
}
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec)
void EncodingParams::EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec)
{
video_enabled_ = true;
video_params_ = video_params;
video_codec_ = vcodec;
video_enabled_ = true;
video_params_ = video_params;
video_codec_ = vcodec;
}
void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCodec::Codec &acodec)
void EncodingParams::EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec)
{
audio_enabled_ = true;
audio_params_ = audio_params;
audio_codec_ = acodec;
audio_enabled_ = true;
audio_params_ = audio_params;
audio_codec_ = acodec;
}
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
{
subtitles_enabled_ = true;
subtitles_codec_ = scodec;
subtitles_enabled_ = true;
subtitles_codec_ = scodec;
}
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec)
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec)
{
subtitles_enabled_ = true;
subtitles_are_sidecar_ = true;
subtitle_sidecar_fmt_ = sfmt;
subtitles_codec_ = scodec;
subtitles_enabled_ = true;
subtitles_are_sidecar_ = true;
subtitle_sidecar_fmt_ = sfmt;
subtitles_codec_ = scodec;
}
void EncodingParams::DisableVideo()
{
video_enabled_ = false;
video_enabled_ = false;
}
void EncodingParams::DisableAudio()
{
audio_enabled_ = false;
audio_enabled_ = false;
}
void EncodingParams::DisableSubtitles()
{
subtitles_enabled_ = false;
subtitles_enabled_ = false;
}
bool EncodingParams::Load(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) {
int version = 0;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) {
int version = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt();
}
}
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt();
}
}
switch (version) {
case 1:
return LoadV1(reader);
}
} else {
reader->skipCurrentElement();
}
}
switch (version) {
case 1:
return LoadV1(reader);
}
} else {
reader->skipCurrentElement();
}
}
return false;
return false;
}
bool EncodingParams::Load(QIODevice *device)
{
QXmlStreamReader reader(device);
return Load(&reader);
QXmlStreamReader reader(device);
return Load(&reader);
}
void EncodingParams::Save(QIODevice *device) const
{
QXmlStreamWriter writer(device);
Save(&writer);
QXmlStreamWriter writer(device);
Save(&writer);
}
void EncodingParams::Save(QXmlStreamWriter *writer) const
{
writer->writeStartDocument();
writer->writeStartDocument();
writer->writeStartElement(QStringLiteral("export"));
writer->writeStartElement(QStringLiteral("export"));
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
writer->writeAttribute(QStringLiteral("version"),
QString::number(kEncoderParamsVersion));
writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"),
QString::number(format_));
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString()));
writer->writeTextElement(QStringLiteral("range"),
QString::number(has_custom_range_));
writer->writeTextElement(
QStringLiteral("customrangein"),
QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(
QStringLiteral("customrangeout"),
QString::fromStdString(custom_range_.out().toString()));
writer->writeStartElement(QStringLiteral("video"));
writer->writeStartElement(QStringLiteral("video"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(video_enabled_));
if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_));
if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"),
QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("width"),
QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("height"),
QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("format"),
QString::number(video_params_.format()));
writer->writeTextElement(
QStringLiteral("pixelaspect"),
QString::fromStdString(
video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(
QStringLiteral("timebase"),
QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"),
QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"),
QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"),
QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"),
QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"),
QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"),
QString::number(video_is_image_sequence_));
writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"), color_transform_.output());
writer->writeEndElement(); // colortransform
writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"),
color_transform_.output());
writer->writeEndElement(); // colortransform
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
writer->writeTextElement(QStringLiteral("vscale"),
QString::number(video_scaling_method_));
if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts"));
if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts"));
QHash<QString, QString>::const_iterator i;
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
writer->writeStartElement(QStringLiteral("entry"));
QHash<QString, QString>::const_iterator i;
for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
i++) {
writer->writeStartElement(QStringLiteral("entry"));
writer->writeTextElement(QStringLiteral("key"), i.key());
writer->writeTextElement(QStringLiteral("value"), i.value());
writer->writeTextElement(QStringLiteral("key"), i.key());
writer->writeTextElement(QStringLiteral("value"), i.value());
writer->writeEndElement(); // entry
}
writer->writeEndElement(); // entry
}
writer->writeEndElement(); // opts
}
}
writer->writeEndElement(); // opts
}
}
writer->writeEndElement(); // video
writer->writeEndElement(); // video
writer->writeStartElement(QStringLiteral("audio"));
writer->writeStartElement(QStringLiteral("audio"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(audio_enabled_));
if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"),
QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"),
QString::number(audio_params_.sample_rate()));
writer->writeTextElement(QStringLiteral("channellayout"),
QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_));
}
writer->writeTextElement(
QStringLiteral("channellayout"),
QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(
QStringLiteral("format"),
QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(audio_bit_rate_));
}
writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(subtitles_enabled_));
if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_));
if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"),
QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"),
QString::number(subtitle_sidecar_fmt_));
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_));
}
writer->writeTextElement(QStringLiteral("codec"),
QString::number(subtitles_codec_));
}
writer->writeEndElement(); // subtitles
writer->writeEndElement(); // subtitles
writer->writeEndElement(); // audio
writer->writeEndElement(); // audio
writer->writeEndElement(); // export
writer->writeEndElement(); // export
writer->writeEndDocument();
writer->writeEndDocument();
}
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
Encoder *Encoder::CreateFromID(Type id, const EncodingParams &params)
{
switch (id) {
case kEncoderTypeNone:
break;
case kEncoderTypeFFmpeg:
return new FFmpegEncoder(params);
case kEncoderTypeOIIO:
return new OIIOEncoder(params);
}
switch (id) {
case kEncoderTypeNone:
break;
case kEncoderTypeFFmpeg:
return new FFmpegEncoder(params);
case kEncoderTypeOIIO:
return new OIIOEncoder(params);
}
return nullptr;
return nullptr;
}
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
{
switch (f) {
case ExportFormat::kFormatDNxHD:
case ExportFormat::kFormatMatroska:
case ExportFormat::kFormatQuickTime:
case ExportFormat::kFormatMPEG4Video:
case ExportFormat::kFormatMPEG4Audio:
case ExportFormat::kFormatWAV:
case ExportFormat::kFormatAIFF:
case ExportFormat::kFormatMP3:
case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG:
case ExportFormat::kFormatTIFF:
return kEncoderTypeOIIO;
case ExportFormat::kFormatCount:
break;
}
switch (f) {
case ExportFormat::kFormatDNxHD:
case ExportFormat::kFormatMatroska:
case ExportFormat::kFormatQuickTime:
case ExportFormat::kFormatMPEG4Video:
case ExportFormat::kFormatMPEG4Audio:
case ExportFormat::kFormatWAV:
case ExportFormat::kFormatAIFF:
case ExportFormat::kFormatMP3:
case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG:
case ExportFormat::kFormatTIFF:
return kEncoderTypeOIIO;
case ExportFormat::kFormatCount:
break;
}
return kEncoderTypeNone;
return kEncoderTypeNone;
}
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams &params)
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params)
{
return CreateFromID(GetTypeFromFormat(f), params);
return CreateFromID(GetTypeFromFormat(f), params);
}
Encoder *Encoder::CreateFromParams(const EncodingParams &params)
{
return CreateFromFormat(params.format(), params);
return CreateFromFormat(params.format(), params);
}
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
{
return QStringList();
return QStringList();
}
std::vector<SampleFormat> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
std::vector<SampleFormat>
Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
{
return std::vector<SampleFormat>();
return std::vector<SampleFormat>();
}
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height)
QMatrix4x4
EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height)
{
QMatrix4x4 preview_matrix;
QMatrix4x4 preview_matrix;
if (method == EncodingParams::kStretch) {
return preview_matrix;
}
if (method == EncodingParams::kStretch) {
return preview_matrix;
}
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
float export_ar =
static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar =
static_cast<float>(source_width) / static_cast<float>(source_height);
if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix;
}
if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix;
}
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F);
} else {
preview_matrix.scale(1.0F, export_ar / source_ar);
}
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F);
} else {
preview_matrix.scale(1.0F, export_ar / source_ar);
}
return preview_matrix;
return preview_matrix;
}
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
{
rational custom_range_in, custom_range_out;
rational custom_range_in, custom_range_out;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in = rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out = rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) {
video_threads_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) {
color_transform_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) {
QString key, value;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText();
} else if (reader->name() == QStringLiteral("value")) {
value = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<PixelFormat::Format>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_pixel_aspect_ratio(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) {
video_threads_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ =
reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) {
color_transform_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) {
QString key, value;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText();
} else if (reader->name() ==
QStringLiteral("value")) {
value = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
if (video_params_.pixel_aspect_ratio().isNull()) {
video_params_.set_pixel_aspect_ratio(1);
}
} else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt();
}
}
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
if (video_params_.pixel_aspect_ratio().isNull()) {
video_params_.set_pixel_aspect_ratio(1);
}
} else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) {
AVChannelLayout av_channel_layout;
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong());
audio_params_.set_channel_layout(av_channel_layout);
av_channel_layout_uninit(&av_channel_layout);
} else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong();
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) {
AVChannelLayout av_channel_layout;
av_channel_layout_from_mask(
&av_channel_layout,
reader->readElementText().toLongLong());
audio_params_.set_channel_layout(av_channel_layout);
av_channel_layout_uninit(&av_channel_layout);
} else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(SampleFormat::from_string(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong();
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize the audio bit rate
if (!audio_bit_rate_) {
audio_bit_rate_ = 320000;
}
} else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt();
}
}
// HACK: Resolve bug where I forgot to serialize the audio bit rate
if (!audio_bit_rate_) {
audio_bit_rate_ = 320000;
}
} else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
return true;
return true;
}
}
+270 -148
View File
@@ -34,218 +34,340 @@
#include "render/subtitleparams.h"
#include "render/videoparams.h"
//这个代码也许是导出编码视频用的?
namespace olive {
namespace olive
{
class Encoder;
using EncoderPtr = std::shared_ptr<Encoder>;
class EncodingParams
{
class EncodingParams {
public:
enum VideoScalingMethod {
kFit,
kStretch,
kCrop
};
enum VideoScalingMethod { kFit, kStretch, kCrop };
EncodingParams();
EncodingParams();
static QDir GetPresetPath();
static QStringList GetListOfPresets();
static QDir GetPresetPath();
static QStringList GetListOfPresets();
bool IsValid() const
{
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
}
bool IsValid() const
{
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
}
void SetFilename(const QString& filename) { filename_ = filename; }
void SetFilename(const QString &filename)
{
filename_ = filename;
}
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec);
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec);
void EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec);
void EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec);
void DisableVideo();
void DisableAudio();
void DisableSubtitles();
void DisableVideo();
void DisableAudio();
void DisableSubtitles();
const ExportFormat::Format &format() const { return format_; }
void set_format(const ExportFormat::Format &format) { format_ = format; }
const ExportFormat::Format &format() const
{
return format_;
}
void set_format(const ExportFormat::Format &format)
{
format_ = format;
}
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); }
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; }
void set_video_min_bit_rate(const int64_t& rate) { video_min_bit_rate_ = rate; }
void set_video_max_bit_rate(const int64_t& rate) { video_max_bit_rate_ = rate; }
void set_video_buffer_size(const int64_t& sz) { video_buffer_size_ = sz; }
void set_video_threads(const int& threads) { video_threads_ = threads; }
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; }
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; }
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; }
void set_video_option(const QString &key, const QString &value)
{
video_opts_.insert(key, value);
}
void set_video_bit_rate(const int64_t &rate)
{
video_bit_rate_ = rate;
}
void set_video_min_bit_rate(const int64_t &rate)
{
video_min_bit_rate_ = rate;
}
void set_video_max_bit_rate(const int64_t &rate)
{
video_max_bit_rate_ = rate;
}
void set_video_buffer_size(const int64_t &sz)
{
video_buffer_size_ = sz;
}
void set_video_threads(const int &threads)
{
video_threads_ = threads;
}
void set_video_pix_fmt(const QString &s)
{
video_pix_fmt_ = s;
}
void set_video_is_image_sequence(bool s)
{
video_is_image_sequence_ = s;
}
void set_color_transform(const ColorTransform &color_transform)
{
color_transform_ = color_transform;
}
const QString& filename() const { return filename_; }
const QString &filename() const
{
return filename_;
}
bool video_enabled() const { return video_enabled_; }
const ExportCodec::Codec& video_codec() const { return video_codec_; }
const VideoParams& video_params() const { return video_params_; }
const QHash<QString, QString>& video_opts() const { return video_opts_; }
QString video_option(const QString &key) const { return video_opts_.value(key); }
bool has_video_opt(const QString &key) const { return video_opts_.contains(key); }
const int64_t& video_bit_rate() const { return video_bit_rate_; }
const int64_t& video_min_bit_rate() const { return video_min_bit_rate_; }
const int64_t& video_max_bit_rate() const { return video_max_bit_rate_; }
const int64_t& video_buffer_size() const { return video_buffer_size_; }
const int& video_threads() const { return video_threads_; }
const QString& video_pix_fmt() const { return video_pix_fmt_; }
bool video_is_image_sequence() const { return video_is_image_sequence_; }
const ColorTransform& color_transform() const { return color_transform_; }
bool video_enabled() const
{
return video_enabled_;
}
const ExportCodec::Codec &video_codec() const
{
return video_codec_;
}
const VideoParams &video_params() const
{
return video_params_;
}
const QHash<QString, QString> &video_opts() const
{
return video_opts_;
}
QString video_option(const QString &key) const
{
return video_opts_.value(key);
}
bool has_video_opt(const QString &key) const
{
return video_opts_.contains(key);
}
const int64_t &video_bit_rate() const
{
return video_bit_rate_;
}
const int64_t &video_min_bit_rate() const
{
return video_min_bit_rate_;
}
const int64_t &video_max_bit_rate() const
{
return video_max_bit_rate_;
}
const int64_t &video_buffer_size() const
{
return video_buffer_size_;
}
const int &video_threads() const
{
return video_threads_;
}
const QString &video_pix_fmt() const
{
return video_pix_fmt_;
}
bool video_is_image_sequence() const
{
return video_is_image_sequence_;
}
const ColorTransform &color_transform() const
{
return color_transform_;
}
bool audio_enabled() const { return audio_enabled_; }
const ExportCodec::Codec &audio_codec() const { return audio_codec_; }
const AudioParams& audio_params() const { return audio_params_; }
const int64_t& audio_bit_rate() const { return audio_bit_rate_; }
bool audio_enabled() const
{
return audio_enabled_;
}
const ExportCodec::Codec &audio_codec() const
{
return audio_codec_;
}
const AudioParams &audio_params() const
{
return audio_params_;
}
const int64_t &audio_bit_rate() const
{
return audio_bit_rate_;
}
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; }
void set_audio_bit_rate(const int64_t &b)
{
audio_bit_rate_ = b;
}
bool subtitles_enabled() const { return subtitles_enabled_; }
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; }
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; }
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; }
bool subtitles_enabled() const
{
return subtitles_enabled_;
}
bool subtitles_are_sidecar() const
{
return subtitles_are_sidecar_;
}
ExportFormat::Format subtitle_sidecar_fmt() const
{
return subtitle_sidecar_fmt_;
}
ExportCodec::Codec subtitles_codec() const
{
return subtitles_codec_;
}
const rational& GetExportLength() const { return export_length_; }
void SetExportLength(const rational& export_length) { export_length_ = export_length; }
const rational &GetExportLength() const
{
return export_length_;
}
void SetExportLength(const rational &export_length)
{
export_length_ = export_length;
}
bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader);
bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader);
void Save(QIODevice *device) const;
void Save(QXmlStreamWriter* writer) const;
void Save(QIODevice *device) const;
void Save(QXmlStreamWriter *writer) const;
bool has_custom_range() const { return has_custom_range_; }
const TimeRange& custom_range() const { return custom_range_; }
void set_custom_range(const TimeRange& custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
bool has_custom_range() const
{
return has_custom_range_;
}
const TimeRange &custom_range() const
{
return custom_range_;
}
void set_custom_range(const TimeRange &custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; }
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; }
const VideoScalingMethod &video_scaling_method() const
{
return video_scaling_method_;
}
void
set_video_scaling_method(const VideoScalingMethod &video_scaling_method)
{
video_scaling_method_ = video_scaling_method;
}
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height);
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height);
private:
static const int kEncoderParamsVersion = 1;
static const int kEncoderParamsVersion = 1;
bool LoadV1(QXmlStreamReader *reader);
bool LoadV1(QXmlStreamReader *reader);
QString filename_;
ExportFormat::Format format_;
QString filename_;
ExportFormat::Format format_;
bool video_enabled_;
ExportCodec::Codec video_codec_;
VideoParams video_params_;
QHash<QString, QString> video_opts_;
int64_t video_bit_rate_;
int64_t video_min_bit_rate_;
int64_t video_max_bit_rate_;
int64_t video_buffer_size_;
int video_threads_;
QString video_pix_fmt_;
bool video_is_image_sequence_;
ColorTransform color_transform_;
bool video_enabled_;
ExportCodec::Codec video_codec_;
VideoParams video_params_;
QHash<QString, QString> video_opts_;
int64_t video_bit_rate_;
int64_t video_min_bit_rate_;
int64_t video_max_bit_rate_;
int64_t video_buffer_size_;
int video_threads_;
QString video_pix_fmt_;
bool video_is_image_sequence_;
ColorTransform color_transform_;
bool audio_enabled_;
ExportCodec::Codec audio_codec_;
AudioParams audio_params_;
int64_t audio_bit_rate_;
bool audio_enabled_;
ExportCodec::Codec audio_codec_;
AudioParams audio_params_;
int64_t audio_bit_rate_;
bool subtitles_enabled_;
bool subtitles_are_sidecar_;
ExportFormat::Format subtitle_sidecar_fmt_;
ExportCodec::Codec subtitles_codec_;
bool subtitles_enabled_;
bool subtitles_are_sidecar_;
ExportFormat::Format subtitle_sidecar_fmt_;
ExportCodec::Codec subtitles_codec_;
rational export_length_;
VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
rational export_length_;
VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
};
class Encoder : public QObject
{
Q_OBJECT
class Encoder : public QObject {
Q_OBJECT
public:
Encoder(const EncodingParams& params);
Encoder(const EncodingParams &params);
enum Type {
kEncoderTypeNone = -1,
kEncoderTypeFFmpeg,
kEncoderTypeOIIO
};
enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
/**
/**
* @brief Create a Encoder instance using a Encoder ID
*
* @return
*
* A Encoder instance or nullptr if a Decoder with this ID does not exist
*/
static Encoder *CreateFromID(Type id, const EncodingParams &params);
static Encoder *CreateFromID(Type id, const EncodingParams &params);
static Type GetTypeFromFormat(ExportFormat::Format f);
static Type GetTypeFromFormat(ExportFormat::Format f);
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams &params);
static Encoder *CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params);
static Encoder *CreateFromParams(const EncodingParams &params);
static Encoder *CreateFromParams(const EncodingParams &params);
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
const EncodingParams& params() const;
const EncodingParams &params() const;
virtual PixelFormat GetDesiredPixelFormat() const
{
return PixelFormat::INVALID;
}
virtual PixelFormat GetDesiredPixelFormat() const
{
return PixelFormat::INVALID;
}
const QString& GetError() const
{
return error_;
}
const QString &GetError() const
{
return error_;
}
QString GetFilenameForFrame(const rational& frame);
QString GetFilenameForFrame(const rational &frame);
static int GetImageSequencePlaceholderDigitCount(const QString& filename);
static int GetImageSequencePlaceholderDigitCount(const QString &filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename);
static const QRegularExpression kImageSequenceContainsDigits;
static const QRegularExpression kImageSequenceRemoveDigits;
static const QRegularExpression kImageSequenceContainsDigits;
static const QRegularExpression kImageSequenceRemoveDigits;
public slots:
virtual bool Open() = 0;
virtual bool Open() = 0;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual void Close() = 0;
virtual void Close() = 0;
protected:
void SetError(const QString& err)
{
error_ = err;
}
void SetError(const QString &err)
{
error_ = err;
}
private:
EncodingParams params_;
QString error_;
EncodingParams params_;
QString error_;
};
}
+96 -95
View File
@@ -25,115 +25,116 @@ extern "C" {
#include <libavutil/pixdesc.h>
}
namespace olive {
namespace olive
{
QString ExportCodec::GetCodecName(ExportCodec::Codec c)
{
switch (c) {
case kCodecDNxHD:
return tr("DNxHD");
case kCodecH264:
return tr("H.264");
case kCodecH264rgb:
return tr("H.264 RGB");
case kCodecH265:
return tr("H.265");
case kCodecOpenEXR:
return tr("OpenEXR");
case kCodecPNG:
return tr("PNG");
case kCodecProRes:
return tr("ProRes");
case kCodecCineform:
return tr("Cineform");
case kCodecTIFF:
return tr("TIFF");
case kCodecMP2:
return tr("MP2");
case kCodecMP3:
return tr("MP3");
case kCodecAAC:
return tr("AAC");
case kCodecPCM:
return tr("PCM (Uncompressed)");
case kCodecFLAC:
return tr("FLAC");
case kCodecOpus:
return tr("Opus");
case kCodecVorbis:
return tr("Vorbis");
case kCodecVP9:
return tr("VP9");
case kCodecAV1:
return tr("AV1");
case kCodecSRT:
return tr("SubRip SRT");
case kCodecCount:
break;
}
switch (c) {
case kCodecDNxHD:
return tr("DNxHD");
case kCodecH264:
return tr("H.264");
case kCodecH264rgb:
return tr("H.264 RGB");
case kCodecH265:
return tr("H.265");
case kCodecOpenEXR:
return tr("OpenEXR");
case kCodecPNG:
return tr("PNG");
case kCodecProRes:
return tr("ProRes");
case kCodecCineform:
return tr("Cineform");
case kCodecTIFF:
return tr("TIFF");
case kCodecMP2:
return tr("MP2");
case kCodecMP3:
return tr("MP3");
case kCodecAAC:
return tr("AAC");
case kCodecPCM:
return tr("PCM (Uncompressed)");
case kCodecFLAC:
return tr("FLAC");
case kCodecOpus:
return tr("Opus");
case kCodecVorbis:
return tr("Vorbis");
case kCodecVP9:
return tr("VP9");
case kCodecAV1:
return tr("AV1");
case kCodecSRT:
return tr("SubRip SRT");
case kCodecCount:
break;
}
return tr("Unknown");
return tr("Unknown");
}
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
{
switch (c) {
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecPCM:
case kCodecVorbis:
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
return false;
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
return true;
case kCodecCount:
break;
}
switch (c) {
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecPCM:
case kCodecVorbis:
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
return false;
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
return true;
case kCodecCount:
break;
}
return false;
return false;
}
bool ExportCodec::IsCodecLossless(Codec c)
{
switch (c) {
case kCodecPCM:
case kCodecFLAC:
return true;
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecVorbis:
case kCodecOpus:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
case kCodecCount:
break;
}
switch (c) {
case kCodecPCM:
case kCodecFLAC:
return true;
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecVorbis:
case kCodecOpus:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
case kCodecCount:
break;
}
return false;
return false;
}
}
+30 -31
View File
@@ -27,43 +27,42 @@
#include "common/define.h"
#include "render/subtitleparams.h"
namespace olive {
class ExportCodec : public QObject
namespace olive
{
Q_OBJECT
class ExportCodec : public QObject {
Q_OBJECT
public:
// Only append to this list (never insert) because indexes are used in serialized files
enum Codec {
kCodecDNxHD,
kCodecH264,
kCodecH264rgb,
kCodecH265,
kCodecOpenEXR,
kCodecPNG,
kCodecProRes,
kCodecCineform,
kCodecTIFF,
kCodecVP9,
kCodecMP2,
kCodecMP3,
kCodecAAC,
kCodecPCM,
kCodecOpus,
kCodecVorbis,
kCodecFLAC,
kCodecSRT,
kCodecAV1,
// Only append to this list (never insert) because indexes are used in serialized files
enum Codec {
kCodecDNxHD,
kCodecH264,
kCodecH264rgb,
kCodecH265,
kCodecOpenEXR,
kCodecPNG,
kCodecProRes,
kCodecCineform,
kCodecTIFF,
kCodecVP9,
kCodecMP2,
kCodecMP3,
kCodecAAC,
kCodecPCM,
kCodecOpus,
kCodecVorbis,
kCodecFLAC,
kCodecSRT,
kCodecAV1,
kCodecCount
};
kCodecCount
};
static QString GetCodecName(Codec c);
static QString GetCodecName(Codec c);
static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c);
static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c);
};
}
+184 -171
View File
@@ -22,214 +22,227 @@
#include "encoder.h"
namespace olive {
namespace olive
{
QString ExportFormat::GetName(olive::ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return tr("DNxHD");
case kFormatMatroska:
return tr("Matroska Video");
case kFormatMPEG4Video:
return tr("MPEG-4 Video");
case kFormatMPEG4Audio:
return tr("MPEG-4 Audio");
case kFormatOpenEXR:
return tr("OpenEXR");
case kFormatPNG:
return tr("PNG");
case kFormatTIFF:
return tr("TIFF");
case kFormatQuickTime:
return tr("QuickTime");
case kFormatWAV:
return tr("Wave Audio");
case kFormatAIFF:
return tr("AIFF");
case kFormatMP3:
return tr("MP3");
case kFormatFLAC:
return tr("FLAC");
case kFormatOgg:
return tr("Ogg");
case kFormatWebM:
return tr("WebM");
case kFormatSRT:
return tr("SubRip SRT");
switch (f) {
case kFormatDNxHD:
return tr("DNxHD");
case kFormatMatroska:
return tr("Matroska Video");
case kFormatMPEG4Video:
return tr("MPEG-4 Video");
case kFormatMPEG4Audio:
return tr("MPEG-4 Audio");
case kFormatOpenEXR:
return tr("OpenEXR");
case kFormatPNG:
return tr("PNG");
case kFormatTIFF:
return tr("TIFF");
case kFormatQuickTime:
return tr("QuickTime");
case kFormatWAV:
return tr("Wave Audio");
case kFormatAIFF:
return tr("AIFF");
case kFormatMP3:
return tr("MP3");
case kFormatFLAC:
return tr("FLAC");
case kFormatOgg:
return tr("Ogg");
case kFormatWebM:
return tr("WebM");
case kFormatSRT:
return tr("SubRip SRT");
case kFormatCount:
break;
}
case kFormatCount:
break;
}
return tr("Unknown");
return tr("Unknown");
}
QString ExportFormat::GetExtension(ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return QStringLiteral("mxf");
case kFormatMatroska:
return QStringLiteral("mkv");
case kFormatMPEG4Video:
return QStringLiteral("mp4");
case kFormatMPEG4Audio:
return QStringLiteral("m4a");
case kFormatOpenEXR:
return QStringLiteral("exr");
case kFormatPNG:
return QStringLiteral("png");
case kFormatTIFF:
return QStringLiteral("tiff");
case kFormatQuickTime:
return QStringLiteral("mov");
case kFormatWAV:
return QStringLiteral("wav");
case kFormatAIFF:
return QStringLiteral("aiff");
case kFormatMP3:
return QStringLiteral("mp3");
case kFormatFLAC:
return QStringLiteral("flac");
case kFormatOgg:
return QStringLiteral("ogg");
case kFormatWebM:
return QStringLiteral("webm");
case kFormatSRT:
return QStringLiteral("srt");
case kFormatCount:
break;
}
switch (f) {
case kFormatDNxHD:
return QStringLiteral("mxf");
case kFormatMatroska:
return QStringLiteral("mkv");
case kFormatMPEG4Video:
return QStringLiteral("mp4");
case kFormatMPEG4Audio:
return QStringLiteral("m4a");
case kFormatOpenEXR:
return QStringLiteral("exr");
case kFormatPNG:
return QStringLiteral("png");
case kFormatTIFF:
return QStringLiteral("tiff");
case kFormatQuickTime:
return QStringLiteral("mov");
case kFormatWAV:
return QStringLiteral("wav");
case kFormatAIFF:
return QStringLiteral("aiff");
case kFormatMP3:
return QStringLiteral("mp3");
case kFormatFLAC:
return QStringLiteral("flac");
case kFormatOgg:
return QStringLiteral("ogg");
case kFormatWebM:
return QStringLiteral("webm");
case kFormatSRT:
return QStringLiteral("srt");
case kFormatCount:
break;
}
return QString();
return QString();
}
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return {ExportCodec::kCodecDNxHD};
case kFormatMatroska:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9};
case kFormatMPEG4Video:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
case kFormatOpenEXR:
return {ExportCodec::kCodecOpenEXR};
case kFormatPNG:
return {ExportCodec::kCodecPNG};
case kFormatTIFF:
return {ExportCodec::kCodecTIFF};
case kFormatQuickTime:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
case kFormatWebM:
return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9};
case kFormatOgg:
case kFormatWAV:
case kFormatMPEG4Audio:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatSRT:
case kFormatCount:
break;
}
switch (f) {
case kFormatDNxHD:
return { ExportCodec::kCodecDNxHD };
case kFormatMatroska:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
case kFormatMPEG4Video:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265 };
case kFormatOpenEXR:
return { ExportCodec::kCodecOpenEXR };
case kFormatPNG:
return { ExportCodec::kCodecPNG };
case kFormatTIFF:
return { ExportCodec::kCodecTIFF };
case kFormatQuickTime:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
ExportCodec::kCodecCineform };
case kFormatWebM:
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
case kFormatOgg:
case kFormatWAV:
case kFormatMPEG4Audio:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatSRT:
case kFormatCount:
break;
}
return {};
return {};
}
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
{
switch (f) {
// Video/audio formats
case kFormatDNxHD:
return {ExportCodec::kCodecPCM};
case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC};
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
case kFormatQuickTime:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
case kFormatWebM:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis};
switch (f) {
// Video/audio formats
case kFormatDNxHD:
return { ExportCodec::kCodecPCM };
case kFormatMatroska:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
ExportCodec::kCodecFLAC };
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3 };
case kFormatQuickTime:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
case kFormatWebM:
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
// Audio only formats
case kFormatWAV:
return {ExportCodec::kCodecPCM};
case kFormatAIFF:
return {ExportCodec::kCodecPCM};
case kFormatMP3:
return {ExportCodec::kCodecMP3};
case kFormatFLAC:
return {ExportCodec::kCodecFLAC};
case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
// Audio only formats
case kFormatWAV:
return { ExportCodec::kCodecPCM };
case kFormatAIFF:
return { ExportCodec::kCodecPCM };
case kFormatMP3:
return { ExportCodec::kCodecMP3 };
case kFormatFLAC:
return { ExportCodec::kCodecFLAC };
case kFormatOgg:
return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
ExportCodec::kCodecPCM };
// Video only formats
case kFormatOpenEXR:
case kFormatPNG:
case kFormatTIFF:
case kFormatSRT:
case kFormatCount:
break;
// Video only formats
case kFormatOpenEXR:
case kFormatPNG:
case kFormatTIFF:
case kFormatSRT:
case kFormatCount:
break;
}
}
return {};
return {};
}
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
{
switch (f) {
case kFormatDNxHD:
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
case kFormatOpenEXR:
case kFormatQuickTime:
case kFormatPNG:
case kFormatTIFF:
case kFormatWAV:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatOgg:
case kFormatWebM:
case kFormatCount:
break;
case kFormatMatroska:
case kFormatSRT:
return {ExportCodec::kCodecSRT};
}
switch (f) {
case kFormatDNxHD:
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
case kFormatOpenEXR:
case kFormatQuickTime:
case kFormatPNG:
case kFormatTIFF:
case kFormatWAV:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatOgg:
case kFormatWebM:
case kFormatCount:
break;
case kFormatMatroska:
case kFormatSRT:
return { ExportCodec::kCodecSRT };
}
return {};
return {};
}
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c)
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
ExportCodec::Codec c)
{
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list;
Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list;
if (e) {
list = e->GetPixelFormatsForCodec(c);
delete e;
}
if (e) {
list = e->GetPixelFormatsForCodec(c);
delete e;
}
return list;
return list;
}
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
std::vector<SampleFormat>
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
{
std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
if (e) {
f = e->GetSampleFormatsForCodec(c);
delete e;
}
if (e) {
f = e->GetSampleFormatsForCodec(c);
delete e;
}
return f;
return f;
}
}
+31 -31
View File
@@ -27,42 +27,42 @@
#include "common/define.h"
#include "exportcodec.h"
namespace olive {
class ExportFormat : public QObject
namespace olive
{
Q_OBJECT
class ExportFormat : public QObject {
Q_OBJECT
public:
// Only append to this list (never insert) because indexes are used in serialized files
enum Format {
kFormatDNxHD,
kFormatMatroska,
kFormatMPEG4Video,
kFormatOpenEXR,
kFormatQuickTime,
kFormatPNG,
kFormatTIFF,
kFormatWAV,
kFormatAIFF,
kFormatMP3,
kFormatFLAC,
kFormatOgg,
kFormatWebM,
kFormatSRT,
kFormatMPEG4Audio,
// Only append to this list (never insert) because indexes are used in serialized files
enum Format {
kFormatDNxHD,
kFormatMatroska,
kFormatMPEG4Video,
kFormatOpenEXR,
kFormatQuickTime,
kFormatPNG,
kFormatTIFF,
kFormatWAV,
kFormatAIFF,
kFormatMP3,
kFormatFLAC,
kFormatOgg,
kFormatWebM,
kFormatSRT,
kFormatMPEG4Audio,
kFormatCount
};
kFormatCount
};
static QString GetName(Format f);
static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
static QString GetName(Format f);
static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat>
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
};
}
File diff suppressed because it is too large Load Diff
+93 -83
View File
@@ -39,92 +39,100 @@ extern "C" {
#include "codec/decoder.h"
#include "common/ffmpegutils.h"
namespace olive {
namespace olive
{
/**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/
class FFmpegDecoder : public Decoder
{
Q_OBJECT
class FFmpegDecoder : public Decoder {
Q_OBJECT
public:
// Constructor
FFmpegDecoder();
// Constructor
FFmpegDecoder();
// Destructor
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
// Destructor
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
virtual QString id() const override;
virtual QString id() const override;
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsAudio() override{return true;}
virtual bool SupportsVideo() override
{
return true;
}
virtual bool SupportsAudio() override
{
return true;
}
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override;
virtual void CloseInternal() override;
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled) override;
virtual void CloseInternal() override;
virtual rational GetAudioStartOffset() const override;
virtual rational GetAudioStartOffset() const override;
private:
class Instance
{
public:
Instance();
class Instance {
public:
Instance();
~Instance()
{
Close();
}
~Instance()
{
Close();
}
bool Open(const char* filename, int stream_index);
bool Open(const char *filename, int stream_index);
bool IsOpen() const
{
return fmt_ctx_;
}
bool IsOpen() const
{
return fmt_ctx_;
}
void Close();
void Close();
/**
/**
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
*
* @return
*
* An FFmpeg error code, or >= 0 on success
*/
int GetFrame(AVPacket* pkt, AVFrame* frame);
int GetFrame(AVPacket *pkt, AVFrame *frame);
const char *GetSubtitleHeader() const;
const char *GetSubtitleHeader() const;
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub);
int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
int GetPacket(AVPacket *pkt);
int GetPacket(AVPacket *pkt);
void Seek(int64_t timestamp);
void Seek(int64_t timestamp);
AVFormatContext* fmt_ctx() const
{
return fmt_ctx_;
}
AVFormatContext *fmt_ctx() const
{
return fmt_ctx_;
}
AVStream* avstream() const
{
return avstream_;
}
AVStream *avstream() const
{
return avstream_;
}
private:
AVFormatContext* fmt_ctx_;
AVCodecContext* codec_ctx_;
AVStream* avstream_;
AVDictionary* opts_;
private:
AVFormatContext *fmt_ctx_;
AVCodecContext *codec_ctx_;
AVStream *avstream_;
AVDictionary *opts_;
};
};
/**
/**
* @brief Handle an FFmpeg error code
*
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -132,54 +140,56 @@ private:
*
* @param error_code
*/
static QString FFmpegError(int error_code);
static QString FFmpegError(int error_code);
void FreeScaler();
void FreeScaler();
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static const char *
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
AVFramePtr GetFrameFromCache(const int64_t &t) const;
AVFramePtr GetFrameFromCache(const int64_t &t) const;
void ClearFrameCache();
void ClearFrameCache();
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
const RetrieveVideoParams &p,
const AVFramePtr original);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
void RemoveFirstFrame();
void RemoveFirstFrame();
static int MaximumQueueSize();
static int MaximumQueueSize();
SwsContext *sws_ctx_;
int sws_src_width_;
int sws_src_height_;
AVPixelFormat sws_src_format_;
int sws_dst_width_;
int sws_dst_height_;
AVPixelFormat sws_dst_format_;
AVColorRange sws_colrange_;
AVColorSpace sws_colspace_;
SwsContext *sws_ctx_;
int sws_src_width_;
int sws_src_height_;
AVPixelFormat sws_src_format_;
int sws_dst_width_;
int sws_dst_height_;
AVPixelFormat sws_dst_format_;
AVColorRange sws_colrange_;
AVColorSpace sws_colspace_;
AVPacket *working_packet_;
AVPacket *working_packet_;
int64_t second_ts_;
int64_t second_ts_;
std::list<AVFramePtr> cached_frames_;
std::list<AVFramePtr> cached_frames_;
bool cache_at_zero_;
bool cache_at_eof_;
Instance instance_;
bool cache_at_zero_;
bool cache_at_eof_;
Instance instance_;
};
}
File diff suppressed because it is too large Load Diff
+54 -45
View File
@@ -31,37 +31,41 @@ extern "C" {
#include "codec/encoder.h"
namespace olive {
class FFmpegEncoder : public Encoder
namespace olive
{
Q_OBJECT
class FFmpegEncoder : public Encoder {
Q_OBJECT
public:
FFmpegEncoder(const EncodingParams &params);
FFmpegEncoder(const EncodingParams &params);
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
virtual QStringList
GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
virtual bool Open() override;
virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) override;
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data,
int input_sample_count);
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
virtual void Close() override;
virtual PixelFormat GetDesiredPixelFormat() const override
{
return video_conversion_fmt_;
}
virtual PixelFormat GetDesiredPixelFormat() const override
{
return video_conversion_fmt_;
}
private:
/**
/**
* @brief Handle an FFmpeg error code
*
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -69,43 +73,48 @@ private:
*
* @param error_code
*/
void FFmpegError(const QString &context, int error_code);
void FFmpegError(const QString &context, int error_code);
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
AVStream *stream);
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec);
bool InitializeStream(enum AVMediaType type, AVStream **stream,
AVCodecContext **codec_ctx,
const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx,
const AVCodec *codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx,
const AVCodec *codec);
void FlushEncoders();
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
void FlushEncoders();
void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
bool InitializeResampleContext(const AudioParams &audio);
bool InitializeResampleContext(const AudioParams &audio);
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat);
static const AVCodec *GetEncoder(ExportCodec::Codec c,
SampleFormat aformat);
AVFormatContext* fmt_ctx_;
AVFormatContext *fmt_ctx_;
AVStream* video_stream_;
AVCodecContext* video_codec_ctx_;
AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_;
AVStream *video_stream_;
AVCodecContext *video_codec_ctx_;
AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_;
AVStream* audio_stream_;
AVCodecContext* audio_codec_ctx_;
SwrContext* audio_resample_ctx_;
AVFrame* audio_frame_;
int audio_max_samples_;
int audio_frame_offset_;
int audio_write_count_;
AVStream *audio_stream_;
AVCodecContext *audio_codec_ctx_;
SwrContext *audio_resample_ctx_;
AVFrame *audio_frame_;
int audio_max_samples_;
int audio_frame_offset_;
int audio_write_count_;
AVStream* subtitle_stream_;
AVCodecContext* subtitle_codec_ctx_;
bool open_;
AVStream *subtitle_stream_;
AVCodecContext *subtitle_codec_ctx_;
bool open_;
};
}
+87 -79
View File
@@ -28,153 +28,161 @@
#include "common/oiioutils.h"
#include "render/framemanager.h"
namespace olive {
namespace olive
{
Frame::Frame() :
data_(nullptr),
data_size_(0),
timestamp_(0)
Frame::Frame()
: data_(nullptr)
, data_size_(0)
, timestamp_(0)
{
}
Frame::~Frame()
{
destroy();
destroy();
}
FramePtr Frame::Create()
{
return std::make_shared<Frame>();
return std::make_shared<Frame>();
}
const VideoParams &Frame::video_params() const
{
return params_;
return params_;
}
void Frame::set_video_params(const VideoParams &params)
{
params_ = params;
params_ = params;
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
linesize_ = generate_linesize_bytes(width(), params_.format(),
params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
}
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
{
if (top->video_params() != bottom->video_params()) {
qCritical() << "Tried to interlace two frames that had incompatible parameters";
return nullptr;
}
if (top->video_params() != bottom->video_params()) {
qCritical()
<< "Tried to interlace two frames that had incompatible parameters";
return nullptr;
}
FramePtr interlaced = Frame::Create();
interlaced->set_video_params(top->video_params());
interlaced->allocate();
FramePtr interlaced = Frame::Create();
interlaced->set_video_params(top->video_params());
interlaced->allocate();
int linesize = interlaced->linesize_bytes();
int linesize = interlaced->linesize_bytes();
for (int i=0; i<interlaced->height(); i++) {
FramePtr which = (i%2 == 0) ? top : bottom;
for (int i = 0; i < interlaced->height(); i++) {
FramePtr which = (i % 2 == 0) ? top : bottom;
memcpy(interlaced->data() + i*linesize,
which->const_data() + i*linesize,
linesize);
}
memcpy(interlaced->data() + i * linesize,
which->const_data() + i * linesize, linesize);
}
return interlaced;
return interlaced;
}
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count)
int Frame::generate_linesize_bytes(int width, PixelFormat format,
int channel_count)
{
// Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31);
// Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) *
((width + 31) & ~31);
}
Color Frame::get_pixel(int x, int y) const
{
if (!contains_pixel(x, y)) {
return Color();
}
if (!contains_pixel(x, y)) {
return Color();
}
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
return Color(reinterpret_cast<const char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
return Color(reinterpret_cast<const char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
}
bool Frame::contains_pixel(int x, int y) const
{
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
}
void Frame::set_pixel(int x, int y, const Color &c)
{
if (!contains_pixel(x, y)) {
return;
}
if (!contains_pixel(x, y)) {
return;
}
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
c.toData(reinterpret_cast<char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
c.toData(reinterpret_cast<char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
}
bool Frame::allocate()
{
// Assume this frame is intended to be a video frame
if (!params_.is_valid()) {
qWarning() << "Tried to allocate a frame with invalid parameters";
return false;
}
// Assume this frame is intended to be a video frame
if (!params_.is_valid()) {
qWarning() << "Tried to allocate a frame with invalid parameters";
return false;
}
if (is_allocated()) {
// Already allocated
return true;
}
if (is_allocated()) {
// Already allocated
return true;
}
data_size_ = linesize_ * height();
data_ = FrameManager::Allocate(data_size_);
data_size_ = linesize_ * height();
data_ = FrameManager::Allocate(data_size_);
return true;
return true;
}
void Frame::destroy()
{
if (is_allocated()) {
FrameManager::Deallocate(data_size_, data_);
if (is_allocated()) {
FrameManager::Deallocate(data_size_, data_);
data_size_ = 0;
data_ = nullptr;
}
data_size_ = 0;
data_ = nullptr;
}
}
FramePtr Frame::convert(PixelFormat format) const
{
// Create new params with destination format
VideoParams params = params_;
params.set_format(format);
// Create new params with destination format
VideoParams params = params_;
params.set_format(format);
// Create new frame
FramePtr converted = Frame::Create();
converted->set_video_params(params);
converted->set_timestamp(timestamp_);
converted->allocate();
// Create new frame
FramePtr converted = Frame::Create();
converted->set_video_params(params);
converted->set_timestamp(timestamp_);
converted->allocate();
// Do the conversion through OIIO for convenience
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(),
channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
// Do the conversion through OIIO for convenience
OIIO::ImageBuf src(
OIIO::ImageSpec(width(), height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
OIIOUtils::FrameToBuffer(this, &src);
OIIOUtils::FrameToBuffer(this, &src);
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(),
channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
OIIO::ImageBuf dst(OIIO::ImageSpec(
converted->width(), converted->height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
if (dst.copy_pixels(src)) {
OIIOUtils::BufferToFrame(&dst, converted.get());
return converted;
} else {
return nullptr;
}
if (dst.copy_pixels(src)) {
OIIOUtils::BufferToFrame(&dst, converted.get());
return converted;
} else {
return nullptr;
}
}
}
+79 -79
View File
@@ -28,7 +28,8 @@
#include "common/define.h"
#include "render/videoparams.h"
namespace olive {
namespace olive
{
class Frame;
using FramePtr = std::shared_ptr<Frame>;
@@ -36,135 +37,134 @@ using FramePtr = std::shared_ptr<Frame>;
/**
* @brief Video frame data or audio sample data from a Decoder
*/
class Frame
{
class Frame {
public:
Frame();
Frame();
~Frame();
~Frame();
DISABLE_COPY_MOVE(Frame)
DISABLE_COPY_MOVE(Frame)
static FramePtr Create();
static FramePtr Create();
const VideoParams& video_params() const;
void set_video_params(const VideoParams& params);
const VideoParams &video_params() const;
void set_video_params(const VideoParams &params);
static FramePtr Interlace(FramePtr top, FramePtr bottom);
static FramePtr Interlace(FramePtr top, FramePtr bottom);
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count);
static int generate_linesize_bytes(int width, PixelFormat format,
int channel_count);
int linesize_pixels() const
{
return linesize_pixels_;
}
int linesize_pixels() const
{
return linesize_pixels_;
}
int linesize_bytes() const
{
return linesize_;
}
int linesize_bytes() const
{
return linesize_;
}
int width() const
{
return params_.effective_width();
}
int width() const
{
return params_.effective_width();
}
int height() const
{
return params_.effective_height();
}
int height() const
{
return params_.effective_height();
}
PixelFormat format() const
{
return params_.format();
}
PixelFormat format() const
{
return params_.format();
}
int channel_count() const
{
return params_.channel_count();
}
int channel_count() const
{
return params_.channel_count();
}
Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color& c);
Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color &c);
/**
/**
* @brief Get frame's timestamp.
*
* This timestamp is always a rational that will equate to the time in seconds.
*/
const rational& timestamp() const
{
return timestamp_;
}
const rational &timestamp() const
{
return timestamp_;
}
void set_timestamp(const rational& timestamp)
{
timestamp_ = timestamp;
}
void set_timestamp(const rational &timestamp)
{
timestamp_ = timestamp;
}
/**
/**
* @brief Get the data buffer of this frame
*/
char* data()
{
return data_;
}
char *data()
{
return data_;
}
/**
/**
* @brief Get the const data buffer of this frame
*/
const char* const_data() const
{
return data_;
}
const char *const_data() const
{
return data_;
}
/**
/**
* @brief Allocate memory buffer to store data based on parameters
*
* For video frames, the width(), height(), and format() must be set for this function to work.
*
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
*/
bool allocate();
bool allocate();
/**
/**
* @brief Return whether the frame is allocated or not
*/
bool is_allocated() const
{
return data_;
}
bool is_allocated() const
{
return data_;
}
/**
/**
* @brief Destroy a memory buffer allocated with allocate()
*/
void destroy();
void destroy();
/**
/**
* @brief Returns the size of the array returned in data() in bytes
*
* Returns 0 if nothing is allocated.
*/
int allocated_size() const
{
return data_size_;
}
int allocated_size() const
{
return data_size_;
}
FramePtr convert(PixelFormat format) const;
FramePtr convert(PixelFormat format) const;
private:
VideoParams params_;
VideoParams params_;
char* data_;
int data_size_;
char *data_;
int data_size_;
rational timestamp_;
rational timestamp_;
int linesize_;
int linesize_pixels_;
int linesize_;
int linesize_pixels_;
};
}
+150 -133
View File
@@ -31,217 +31,234 @@
#include "core.h"
#include "render/renderer.h"
namespace olive {
namespace olive
{
QStringList OIIODecoder::supported_formats_;
OIIODecoder::OIIODecoder() :
image_(nullptr)
OIIODecoder::OIIODecoder()
: image_(nullptr)
{
}
QString OIIODecoder::id() const
{
return QStringLiteral("oiio");
return QStringLiteral("oiio");
}
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const
FootageDescription OIIODecoder::Probe(const QString &filename,
CancelAtom *cancelled) const
{
Q_UNUSED(cancelled)
Q_UNUSED(cancelled)
FootageDescription desc(id());
FootageDescription desc(id());
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) {
return desc;
}
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) {
return desc;
}
std::string std_filename = filename.toStdString();
std::string std_filename = filename.toStdString();
auto in = OIIO::ImageInput::open(std_filename);
auto in = OIIO::ImageInput::open(std_filename);
if (!in) {
return desc;
}
if (!in) {
return desc;
}
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better
if (!strcmp(in->format_name(), "FFmpeg movie")) {
return desc;
}
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better
if (!strcmp(in->format_name(), "FFmpeg movie")) {
return desc;
}
bool stream_enabled = true;
bool stream_enabled = true;
int i;
for (i=0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec();
int i;
for (i = 0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
video_params.set_stream_index(i);
video_params.set_stream_index(i);
if (i > 1) {
// This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0);
if (i > 1) {
// This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0);
float norm_x = spec.x + float(spec.width)*0.5f - float(root_spec.width)*0.5f;
float norm_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f;
float norm_x = spec.x + float(spec.width) * 0.5f -
float(root_spec.width) * 0.5f;
float norm_y = spec.y + float(spec.height) * 0.5f -
float(root_spec.height) * 0.5f;
video_params.set_x(norm_x);
video_params.set_y(norm_y);
}
video_params.set_x(norm_x);
video_params.set_y(norm_y);
}
// By default, only enable the first subimage (presumably the combined image). Later we will
// ask the user if they want to enable the layers instead.
video_params.set_enabled(stream_enabled);
stream_enabled = false;
// By default, only enable the first subimage (presumably the combined image). Later we will
// ask the user if they want to enable the layers instead.
video_params.set_enabled(stream_enabled);
stream_enabled = false;
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
video_params.set_premultiplied_alpha(true);
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
video_params.set_premultiplied_alpha(true);
desc.AddVideoStream(video_params);
}
desc.AddVideoStream(video_params);
}
desc.SetStreamCount(i);
desc.SetStreamCount(i);
// If we're here, we have a successful image open
in->close();
// If we're here, we have a successful image open
in->close();
return desc;
return desc;
}
bool OIIODecoder::OpenInternal()
{
// If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename(), stream().stream());
// If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename(), stream().stream());
}
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider);
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider);
if (!buffer_.is_allocated()
|| last_params_.divider != p.divider) {
last_params_ = p;
if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
last_params_ = p;
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
if (p.divider == 1) {
// Just upload straight to the buffer
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else {
OIIO::ImageBuf buf(image_->spec());
image_->read_image(0,0,0,-1,image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride());
if (p.divider == 1) {
// Just upload straight to the buffer
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else {
OIIO::ImageBuf buf(image_->spec());
image_->read_image(0, 0, 0, -1, image_->spec().format,
buf.localpixels(), buf.pixel_stride(),
buf.scanline_stride(), buf.z_stride());
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel();
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height();
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel();
for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height();
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x,
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x,
px_sz);
}
}
}
}
for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
px_sz * dst_x,
static_cast<uint8_t *>(buf.localpixels()) +
buf.scanline_stride() * src_y + px_sz * src_x,
px_sz);
}
}
}
}
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
return p.renderer->CreateTexture(vp, buffer_.data(),
buffer_.linesize_pixels());
}
void OIIODecoder::CloseInternal()
{
CloseImageHandle();
CloseImageHandle();
}
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
bool OIIODecoder::FileTypeIsSupported(const QString &fn)
{
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
// "extension_list" attribute and match it with the extension of the file.
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
// "extension_list" attribute and match it with the extension of the file.
// Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) {
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';');
// Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) {
QStringList extension_list =
QString::fromStdString(OIIO::get_string_attribute("extension_list"))
.split(';');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString& ext, extension_list) {
QStringList format_and_ext = ext.split(':');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString &ext, extension_list) {
QStringList format_and_ext = ext.split(':');
supported_formats_.append(format_and_ext.at(1).split(','));
}
}
supported_formats_.append(format_and_ext.at(1).split(','));
}
}
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) {
return false;
}
if (!supported_formats_.contains(QFileInfo(fn).suffix(),
Qt::CaseInsensitive)) {
return false;
}
return true;
return true;
}
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
{
image_ = OIIO::ImageInput::open(fn.toStdString());
image_ = OIIO::ImageInput::open(fn.toStdString());
if (!image_) {
return false;
}
if (!image_) {
return false;
}
if (!image_->seek_subimage(subimage, 0)) {
return false;
}
if (!image_->seek_subimage(subimage, 0)) {
return false;
}
// Check if we can work with this pixel format
const OIIO::ImageSpec& spec = image_->spec();
// Check if we can work with this pixel format
const OIIO::ImageSpec &spec = image_->spec();
// We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
// We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
if (pix_fmt_ == PixelFormat::INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
return false;
}
if (pix_fmt_ == PixelFormat::INVALID) {
qWarning()
<< "Failed to convert OIIO::ImageDesc to native pixel format";
return false;
}
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
return false;
}
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical()
<< "Failed to determine appropriate OIIO basetype from native format";
return false;
}
return true;
return true;
}
void OIIODecoder::CloseImageHandle()
{
if (image_) {
image_->close();
image_ = nullptr;
}
if (image_) {
image_->close();
image_ = nullptr;
}
buffer_.destroy();
buffer_.destroy();
}
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
VideoParams
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
{
VideoParams video_params;
VideoParams video_params;
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
return video_params;
return video_params;
}
}
+27 -23
View File
@@ -26,46 +26,50 @@
#include "codec/decoder.h"
namespace olive {
class OIIODecoder : public Decoder
namespace olive
{
Q_OBJECT
class OIIODecoder : public Decoder {
Q_OBJECT
public:
OIIODecoder();
OIIODecoder();
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
virtual QString id() const override;
virtual QString id() const override;
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsVideo() override
{
return true;
}
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual void CloseInternal() override;
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override;
private:
std::unique_ptr<OIIO::ImageInput> image_;
std::unique_ptr<OIIO::ImageInput> image_;
static bool FileTypeIsSupported(const QString& fn);
static bool FileTypeIsSupported(const QString &fn);
bool OpenImageHandler(const QString& fn, int subimage);
bool OpenImageHandler(const QString &fn, int subimage);
void CloseImageHandle();
void CloseImageHandle();
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
PixelFormat pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
PixelFormat pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
Frame buffer_;
RetrieveVideoParams last_params_;
static QStringList supported_formats_;
Frame buffer_;
RetrieveVideoParams last_params_;
static QStringList supported_formats_;
};
}
+28 -26
View File
@@ -22,60 +22,62 @@
#include "common/oiioutils.h"
namespace olive {
OIIOEncoder::OIIOEncoder(const EncodingParams &params) :
Encoder(params)
namespace olive
{
OIIOEncoder::OIIOEncoder(const EncodingParams &params)
: Encoder(params)
{
}
bool OIIOEncoder::Open()
{
return true;
return true;
}
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
{
std::string filename = GetFilenameForFrame(time).toStdString();
std::string filename = GetFilenameForFrame(time).toStdString();
auto output = OIIO::ImageOutput::create(filename);
if (!output) {
return false;
}
auto output = OIIO::ImageOutput::create(filename);
if (!output) {
return false;
}
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type);
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(),
frame->channel_count(), type);
if (!output->open(filename, spec)) {
return false;
}
if (!output->open(filename, spec)) {
return false;
}
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) {
return false;
}
if (!output->write_image(type, frame->data(), OIIO::AutoStride,
frame->linesize_bytes())) {
return false;
}
if (!output->close()) {
return false;
}
if (!output->close()) {
return false;
}
return true;
return true;
}
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
{
// Do nothing
return false;
// Do nothing
return false;
}
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
{
return false;
return false;
}
void OIIOEncoder::Close()
{
// Do nothing
// Do nothing
}
}
+11 -11
View File
@@ -23,23 +23,23 @@
#include "codec/encoder.h"
namespace olive {
class OIIOEncoder : public Encoder
namespace olive
{
Q_OBJECT
class OIIOEncoder : public Encoder {
Q_OBJECT
public:
OIIOEncoder(const EncodingParams &params);
OIIOEncoder(const EncodingParams &params);
public slots:
virtual bool Open() override;
virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) override;
virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
};
}
+61 -58
View File
@@ -20,100 +20,103 @@
#include "planarfiledevice.h"
namespace olive {
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
QObject(parent)
namespace olive
{
PlanarFileDevice::PlanarFileDevice(QObject *parent)
: QObject(parent)
{
}
PlanarFileDevice::~PlanarFileDevice()
{
close();
close();
}
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode)
bool PlanarFileDevice::open(const QVector<QString> &filenames,
QIODevice::OpenMode mode)
{
if (isOpen()) {
// Already open
return false;
}
if (isOpen()) {
// Already open
return false;
}
files_.resize(filenames.size());
files_.fill(nullptr);
files_.resize(filenames.size());
files_.fill(nullptr);
for (int i=0; i<files_.size(); i++) {
files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) {
close();
return false;
}
}
for (int i = 0; i < files_.size(); i++) {
files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) {
close();
return false;
}
}
return true;
return true;
}
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset)
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
qint64 offset)
{
qint64 ret = -1;
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
}
}
if (isOpen()) {
for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
}
}
return ret;
return ret;
}
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset)
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
qint64 offset)
{
qint64 ret = -1;
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
}
}
if (isOpen()) {
for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
}
}
return ret;
return ret;
}
qint64 PlanarFileDevice::size() const
{
if (isOpen()) {
return files_.first()->size();
} else {
return 0;
}
if (isOpen()) {
return files_.first()->size();
} else {
return 0;
}
}
bool PlanarFileDevice::seek(qint64 pos)
{
bool ret = true;
bool ret = true;
for (int i=0; i<files_.size(); i++) {
ret = files_[i]->seek(pos) & ret;
}
for (int i = 0; i < files_.size(); i++) {
ret = files_[i]->seek(pos) & ret;
}
return ret;
return ret;
}
void PlanarFileDevice::close()
{
for (int i=0; i<files_.size(); i++) {
QFile *f = files_.at(i);
if (f) {
if (f->isOpen()) {
f->close();
}
delete f;
}
}
files_.clear();
for (int i = 0; i < files_.size(); i++) {
QFile *f = files_.at(i);
if (f) {
if (f->isOpen()) {
f->close();
}
delete f;
}
}
files_.clear();
}
}
+18 -18
View File
@@ -25,38 +25,38 @@
#include <QFile>
#include <QObject>
namespace olive {
namespace olive
{
using namespace core;
class PlanarFileDevice : public QObject
{
Q_OBJECT
class PlanarFileDevice : public QObject {
Q_OBJECT
public:
PlanarFileDevice(QObject *parent = nullptr);
PlanarFileDevice(QObject *parent = nullptr);
virtual ~PlanarFileDevice() override;
virtual ~PlanarFileDevice() override;
bool isOpen() const
{
return !files_.isEmpty();
}
bool isOpen() const
{
return !files_.isEmpty();
}
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel,
qint64 offset = 0);
qint64 size() const;
qint64 size() const;
bool seek(qint64 pos);
bool seek(qint64 pos);
void close();
void close();
private:
QVector<QFile*> files_;
QVector<QFile *> files_;
};
}