Merge branch 'olive-editor:master' into av1
This commit is contained in:
+28
-14
@@ -37,11 +37,18 @@ namespace olive {
|
||||
|
||||
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
|
||||
|
||||
Decoder::Decoder()
|
||||
Decoder::Decoder() :
|
||||
cached_texture_(nullptr)
|
||||
{
|
||||
UpdateLastAccessed();
|
||||
}
|
||||
|
||||
void Decoder::IncrementAccessTime(qint64 t)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
last_accessed_ += t;
|
||||
}
|
||||
|
||||
bool Decoder::Open(const CodecStream &stream)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
@@ -86,7 +93,7 @@ bool Decoder::Open(const CodecStream &stream)
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ÷r, const QAtomicInt *cancelled)
|
||||
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
@@ -102,14 +109,21 @@ TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cancelled && *cancelled) {
|
||||
if (p.cancelled && p.cancelled->IsCancelled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return RetrieveVideoInternal(renderer, timecode, divider, cancelled);
|
||||
if (cached_texture_ && cached_time_ == p.time) {
|
||||
return cached_texture_;
|
||||
}
|
||||
|
||||
cached_texture_ = RetrieveVideoInternal(p);
|
||||
cached_time_ = p.time;
|
||||
|
||||
return cached_texture_;
|
||||
}
|
||||
|
||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
|
||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
@@ -152,6 +166,8 @@ void Decoder::Close()
|
||||
|
||||
UpdateLastAccessed();
|
||||
|
||||
cached_texture_ = nullptr;
|
||||
|
||||
if (stream_.IsValid()) {
|
||||
CloseInternal();
|
||||
stream_.Reset();
|
||||
@@ -160,7 +176,7 @@ void Decoder::Close()
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, const QAtomicInt *cancelled)
|
||||
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
||||
{
|
||||
return ConformAudioInternal(output_filenames, params, cancelled);
|
||||
}
|
||||
@@ -264,15 +280,13 @@ int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
||||
return number_only.toLongLong();
|
||||
}
|
||||
|
||||
TexturePtr Decoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ÷r, const QAtomicInt *cancelled)
|
||||
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
Q_UNUSED(timecode)
|
||||
Q_UNUSED(divider)
|
||||
Q_UNUSED(cancelled)
|
||||
Q_UNUSED(p)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, const QAtomicInt* cancelled)
|
||||
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
||||
{
|
||||
Q_UNUSED(filenames)
|
||||
Q_UNUSED(cancelled)
|
||||
@@ -280,7 +294,7 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
||||
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, LoopMode loop_mode, const AudioParams &input_params)
|
||||
{
|
||||
PlanarFileDevice input;
|
||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||
@@ -290,7 +304,7 @@ bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVecto
|
||||
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 == Footage::kLoopModeLoop) {
|
||||
if (loop_mode == kLoopModeLoop) {
|
||||
while (read_index >= input.size()) {
|
||||
read_index -= input.size();
|
||||
}
|
||||
@@ -335,7 +349,7 @@ void Decoder::UpdateLastAccessed()
|
||||
|
||||
uint qHash(Decoder::CodecStream stream, uint seed)
|
||||
{
|
||||
return qHash(stream.filename(), seed) ^ qHash(stream.stream(), seed);
|
||||
return qHash(stream.filename(), seed) ^ qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-34
@@ -34,7 +34,7 @@ extern "C" {
|
||||
#include "codec/frame.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "node/block/block.h"
|
||||
#include "node/project/footage/footagedescription.h"
|
||||
#include "task/task.h"
|
||||
|
||||
@@ -71,6 +71,12 @@ public:
|
||||
kIndexUnavailable
|
||||
};
|
||||
|
||||
enum LoopMode {
|
||||
kLoopModeOff,
|
||||
kLoopModeLoop,
|
||||
kLoopModeClamp
|
||||
};
|
||||
|
||||
Decoder();
|
||||
|
||||
/**
|
||||
@@ -81,17 +87,21 @@ public:
|
||||
virtual bool SupportsVideo(){return false;}
|
||||
virtual bool SupportsAudio(){return false;}
|
||||
|
||||
void IncrementAccessTime(qint64 t);
|
||||
|
||||
class CodecStream
|
||||
{
|
||||
public:
|
||||
CodecStream() :
|
||||
stream_(-1)
|
||||
stream_(-1),
|
||||
block_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CodecStream(const QString& filename, int stream) :
|
||||
CodecStream(const QString& filename, int stream, Block *block) :
|
||||
filename_(filename),
|
||||
stream_(stream)
|
||||
stream_(stream),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -125,11 +135,18 @@ public:
|
||||
return stream_;
|
||||
}
|
||||
|
||||
Block *block() const
|
||||
{
|
||||
return block_;
|
||||
}
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
int stream_;
|
||||
|
||||
Block *block_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -147,29 +164,13 @@ public:
|
||||
|
||||
struct RetrieveVideoParams
|
||||
{
|
||||
RetrieveVideoParams()
|
||||
{
|
||||
divider = 1;
|
||||
maximum_format = VideoParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
int divider;
|
||||
VideoParams::Format maximum_format;
|
||||
|
||||
void reset()
|
||||
{
|
||||
*this = RetrieveVideoParams();
|
||||
}
|
||||
|
||||
bool operator==(const RetrieveVideoParams& rhs) const
|
||||
{
|
||||
return divider == rhs.divider && maximum_format == rhs.maximum_format;
|
||||
}
|
||||
|
||||
bool operator!=(const RetrieveVideoParams& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
Renderer *renderer = nullptr;
|
||||
rational time;
|
||||
int divider = 1;
|
||||
VideoParams::Format maximum_format = VideoParams::kFormatInvalid;
|
||||
CancelAtom *cancelled = nullptr;
|
||||
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
|
||||
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -182,7 +183,7 @@ public:
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
TexturePtr RetrieveVideo(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled = nullptr);
|
||||
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
|
||||
|
||||
enum RetrieveAudioStatus {
|
||||
kInvalid = -1,
|
||||
@@ -199,7 +200,7 @@ 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, Footage::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
|
||||
@@ -217,7 +218,7 @@ public:
|
||||
*
|
||||
* This function is re-entrant.
|
||||
*/
|
||||
virtual FootageDescription Probe(const QString& filename, const QAtomicInt* cancelled) const = 0;
|
||||
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes media/deallocates memory
|
||||
@@ -229,7 +230,7 @@ public:
|
||||
/**
|
||||
* @brief Conform audio stream
|
||||
*/
|
||||
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, const QAtomicInt *cancelled = nullptr);
|
||||
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, CancelAtom *cancelled = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Create a Decoder instance using a Decoder ID
|
||||
@@ -277,9 +278,9 @@ protected:
|
||||
* 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(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled);
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
|
||||
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, const QAtomicInt* cancelled);
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled);
|
||||
|
||||
void SignalProcessingProgress(int64_t ts, int64_t duration);
|
||||
|
||||
@@ -306,7 +307,7 @@ signals:
|
||||
private:
|
||||
void UpdateLastAccessed();
|
||||
|
||||
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
||||
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, LoopMode loop_mode, const AudioParams ¶ms);
|
||||
|
||||
CodecStream stream_;
|
||||
|
||||
@@ -314,6 +315,9 @@ private:
|
||||
|
||||
qint64 last_accessed_;
|
||||
|
||||
TexturePtr cached_texture_;
|
||||
rational cached_time_;
|
||||
|
||||
};
|
||||
|
||||
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
||||
|
||||
+246
-108
@@ -23,6 +23,7 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "ffmpeg/ffmpegencoder.h"
|
||||
#include "oiio/oiioencoder.h"
|
||||
|
||||
@@ -92,13 +93,22 @@ EncodingParams::EncodingParams() :
|
||||
video_is_image_sequence_(false),
|
||||
audio_enabled_(false),
|
||||
audio_bit_rate_(0),
|
||||
subtitles_enabled_(false)
|
||||
subtitles_enabled_(false),
|
||||
subtitles_are_sidecar_(false),
|
||||
video_scaling_method_(kStretch),
|
||||
has_custom_range_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void EncodingParams::SetFilename(const QString &filename)
|
||||
QDir EncodingParams::GetPresetPath()
|
||||
{
|
||||
filename_ = filename;
|
||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
|
||||
}
|
||||
|
||||
QStringList EncodingParams::GetListOfPresets()
|
||||
{
|
||||
QDir d = EncodingParams::GetPresetPath();
|
||||
return d.entryList(QDir::Files);
|
||||
}
|
||||
|
||||
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec)
|
||||
@@ -121,134 +131,79 @@ void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
||||
subtitles_codec_ = scodec;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_option(const QString &key, const QString &value)
|
||||
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec)
|
||||
{
|
||||
video_opts_.insert(key, value);
|
||||
subtitles_enabled_ = true;
|
||||
subtitles_are_sidecar_ = true;
|
||||
subtitle_sidecar_fmt_ = sfmt;
|
||||
subtitles_codec_ = scodec;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_bit_rate(const int64_t &rate)
|
||||
void EncodingParams::DisableVideo()
|
||||
{
|
||||
video_bit_rate_ = rate;
|
||||
video_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_min_bit_rate(const int64_t &rate)
|
||||
void EncodingParams::DisableAudio()
|
||||
{
|
||||
video_min_bit_rate_ = rate;
|
||||
audio_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_max_bit_rate(const int64_t &rate)
|
||||
void EncodingParams::DisableSubtitles()
|
||||
{
|
||||
video_max_bit_rate_ = rate;
|
||||
subtitles_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_buffer_size(const int64_t &sz)
|
||||
bool EncodingParams::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
video_buffer_size_ = sz;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("export")) {
|
||||
int version = 0;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
return LoadV1(reader);
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_threads(const int &threads)
|
||||
bool EncodingParams::Load(QIODevice *device)
|
||||
{
|
||||
video_threads_ = threads;
|
||||
QXmlStreamReader reader(device);
|
||||
return Load(&reader);
|
||||
}
|
||||
|
||||
void EncodingParams::set_video_pix_fmt(const QString &s)
|
||||
void EncodingParams::Save(QIODevice *device) const
|
||||
{
|
||||
video_pix_fmt_ = s;
|
||||
}
|
||||
|
||||
const QString &EncodingParams::filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
bool EncodingParams::video_enabled() const
|
||||
{
|
||||
return video_enabled_;
|
||||
}
|
||||
|
||||
const ExportCodec::Codec &EncodingParams::video_codec() const
|
||||
{
|
||||
return video_codec_;
|
||||
}
|
||||
|
||||
const VideoParams &EncodingParams::video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
}
|
||||
|
||||
const QHash<QString, QString> &EncodingParams::video_opts() const
|
||||
{
|
||||
return video_opts_;
|
||||
}
|
||||
|
||||
const int64_t &EncodingParams::video_bit_rate() const
|
||||
{
|
||||
return video_bit_rate_;
|
||||
}
|
||||
|
||||
const int64_t &EncodingParams::video_min_bit_rate() const
|
||||
{
|
||||
return video_min_bit_rate_;
|
||||
}
|
||||
|
||||
const int64_t &EncodingParams::video_max_bit_rate() const
|
||||
{
|
||||
return video_max_bit_rate_;
|
||||
}
|
||||
|
||||
const int64_t &EncodingParams::video_buffer_size() const
|
||||
{
|
||||
return video_buffer_size_;
|
||||
}
|
||||
|
||||
const int &EncodingParams::video_threads() const
|
||||
{
|
||||
return video_threads_;
|
||||
}
|
||||
|
||||
const QString &EncodingParams::video_pix_fmt() const
|
||||
{
|
||||
return video_pix_fmt_;
|
||||
}
|
||||
|
||||
bool EncodingParams::audio_enabled() const
|
||||
{
|
||||
return audio_enabled_;
|
||||
}
|
||||
|
||||
const ExportCodec::Codec &EncodingParams::audio_codec() const
|
||||
{
|
||||
return audio_codec_;
|
||||
}
|
||||
|
||||
const AudioParams &EncodingParams::audio_params() const
|
||||
{
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
bool EncodingParams::subtitles_enabled() const
|
||||
{
|
||||
return subtitles_enabled_;
|
||||
}
|
||||
|
||||
ExportCodec::Codec EncodingParams::subtitles_codec() const
|
||||
{
|
||||
return subtitles_codec_;
|
||||
}
|
||||
|
||||
const rational &EncodingParams::GetExportLength() const
|
||||
{
|
||||
return export_length_;
|
||||
}
|
||||
|
||||
void EncodingParams::SetExportLength(const rational &export_length)
|
||||
{
|
||||
export_length_ = export_length;
|
||||
QXmlStreamWriter writer(device);
|
||||
Save(&writer);
|
||||
}
|
||||
|
||||
void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartDocument();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("export"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
|
||||
|
||||
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"), custom_range_.in().toString());
|
||||
writer->writeTextElement(QStringLiteral("customrangeout"), custom_range_.out().toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("video"));
|
||||
|
||||
@@ -262,10 +217,18 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("timebase"), 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_max_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->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
||||
|
||||
if (!video_opts_.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("opts"));
|
||||
@@ -297,7 +260,24 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("format"), QString::number(audio_params_.format()));
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("subtitles"));
|
||||
|
||||
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_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // subtitles
|
||||
|
||||
writer->writeEndElement(); // audio
|
||||
|
||||
writer->writeEndElement(); // export
|
||||
|
||||
writer->writeEndDocument();
|
||||
}
|
||||
|
||||
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
|
||||
@@ -346,6 +326,11 @@ Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams
|
||||
return CreateFromID(GetTypeFromFormat(f), params);
|
||||
}
|
||||
|
||||
Encoder *Encoder::CreateFromParams(const EncodingParams ¶ms)
|
||||
{
|
||||
return CreateFromFormat(params.format(), params);
|
||||
}
|
||||
|
||||
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
{
|
||||
return QStringList();
|
||||
@@ -356,4 +341,157 @@ std::vector<AudioParams::Format> Encoder::GetSampleFormatsForCodec(ExportCodec::
|
||||
return std::vector<AudioParams::Format>();
|
||||
}
|
||||
|
||||
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height)
|
||||
{
|
||||
QMatrix4x4 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return preview_matrix;
|
||||
}
|
||||
|
||||
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
||||
{
|
||||
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());
|
||||
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
||||
custom_range_out = rational::fromString(reader->readElementText());
|
||||
} 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<VideoParams::Format>(reader->readElementText().toInt()));
|
||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||
video_params_.set_time_base(rational::fromString(reader->readElementText()));
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
} 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")) {
|
||||
audio_params_.set_channel_layout(reader->readElementText().toULongLong());
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
audio_params_.set_format(static_cast<AudioParams::Format>(reader->readElementText().toInt()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+97
-48
@@ -41,69 +41,109 @@ namespace olive {
|
||||
class Encoder;
|
||||
using EncoderPtr = std::shared_ptr<Encoder>;
|
||||
|
||||
class EncodingParams {
|
||||
class EncodingParams
|
||||
{
|
||||
public:
|
||||
enum VideoScalingMethod {
|
||||
kFit,
|
||||
kStretch,
|
||||
kCrop
|
||||
};
|
||||
|
||||
EncodingParams();
|
||||
|
||||
void SetFilename(const QString& filename);
|
||||
static QDir GetPresetPath();
|
||||
static QStringList GetListOfPresets();
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
|
||||
}
|
||||
|
||||
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 set_video_option(const QString& key, const QString& value);
|
||||
void set_video_bit_rate(const int64_t& rate);
|
||||
void set_video_min_bit_rate(const int64_t& rate);
|
||||
void set_video_max_bit_rate(const int64_t& rate);
|
||||
void set_video_buffer_size(const int64_t& sz);
|
||||
void set_video_threads(const int& threads);
|
||||
void set_video_pix_fmt(const QString& s);
|
||||
void set_video_is_image_sequence(bool s)
|
||||
void DisableVideo();
|
||||
void DisableAudio();
|
||||
void DisableSubtitles();
|
||||
|
||||
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; }
|
||||
|
||||
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 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; }
|
||||
|
||||
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; }
|
||||
|
||||
bool Load(QIODevice *device);
|
||||
bool Load(QXmlStreamReader *reader);
|
||||
|
||||
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)
|
||||
{
|
||||
video_is_image_sequence_ = s;
|
||||
has_custom_range_ = true;
|
||||
custom_range_ = custom_range;
|
||||
}
|
||||
|
||||
const QString& filename() const;
|
||||
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; }
|
||||
|
||||
bool video_enabled() const;
|
||||
const ExportCodec::Codec& video_codec() const;
|
||||
const VideoParams& video_params() const;
|
||||
const QHash<QString, QString>& video_opts() const;
|
||||
const int64_t& video_bit_rate() const;
|
||||
const int64_t& video_min_bit_rate() const;
|
||||
const int64_t& video_max_bit_rate() const;
|
||||
const int64_t& video_buffer_size() const;
|
||||
const int& video_threads() const;
|
||||
const QString& video_pix_fmt() const;
|
||||
bool video_is_image_sequence() const
|
||||
{
|
||||
return video_is_image_sequence_;
|
||||
}
|
||||
|
||||
bool audio_enabled() const;
|
||||
const ExportCodec::Codec &audio_codec() const;
|
||||
const AudioParams& audio_params() const;
|
||||
|
||||
const int64_t& audio_bit_rate() const
|
||||
{
|
||||
return audio_bit_rate_;
|
||||
}
|
||||
|
||||
void set_audio_bit_rate(const int64_t& b)
|
||||
{
|
||||
audio_bit_rate_ = b;
|
||||
}
|
||||
|
||||
bool subtitles_enabled() const;
|
||||
ExportCodec::Codec subtitles_codec() const;
|
||||
|
||||
const rational& GetExportLength() const;
|
||||
void SetExportLength(const rational& GetExportLength);
|
||||
|
||||
virtual void Save(QXmlStreamWriter* writer) const;
|
||||
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height);
|
||||
|
||||
private:
|
||||
static const int kEncoderParamsVersion = 1;
|
||||
|
||||
bool LoadV1(QXmlStreamReader *reader);
|
||||
|
||||
QString filename_;
|
||||
ExportFormat::Format format_;
|
||||
|
||||
bool video_enabled_;
|
||||
ExportCodec::Codec video_codec_;
|
||||
@@ -116,6 +156,7 @@ private:
|
||||
int video_threads_;
|
||||
QString video_pix_fmt_;
|
||||
bool video_is_image_sequence_;
|
||||
ColorTransform color_transform_;
|
||||
|
||||
bool audio_enabled_;
|
||||
ExportCodec::Codec audio_codec_;
|
||||
@@ -123,9 +164,15 @@ private:
|
||||
int64_t audio_bit_rate_;
|
||||
|
||||
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_;
|
||||
|
||||
};
|
||||
|
||||
@@ -154,6 +201,8 @@ public:
|
||||
|
||||
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
||||
|
||||
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual std::vector<AudioParams::Format> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ FFmpegDecoder::FFmpegDecoder() :
|
||||
native_output_pix_fmt_(VideoParams::kFormatInvalid),
|
||||
working_frame_(nullptr),
|
||||
working_packet_(nullptr),
|
||||
is_working_(false),
|
||||
cache_at_zero_(false),
|
||||
cache_at_eof_(false)
|
||||
{
|
||||
@@ -80,6 +79,7 @@ bool FFmpegDecoder::OpenInternal()
|
||||
working_frame_ = av_frame_alloc();
|
||||
working_packet_ = av_packet_alloc();
|
||||
|
||||
frame_rate_tb_ = rational::NaN;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,28 +142,32 @@ bool FFmpegDecoder::OpenInternal()
|
||||
return output_frame;
|
||||
}*/
|
||||
|
||||
TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ¶ms, const QAtomicInt *cancelled)
|
||||
TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
if (AVFramePtr f = RetrieveFrame(timecode, cancelled)) {
|
||||
if (cancelled && *cancelled) {
|
||||
if (AVFramePtr f = RetrieveFrame(p.time, p.cancelled)) {
|
||||
if (p.cancelled && p.cancelled->IsCancelled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (InitScaler(f.get(), params)) {
|
||||
int &src_fmt = f.get()->format;
|
||||
src_fmt = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(static_cast<AVPixelFormat>(src_fmt));
|
||||
|
||||
f->color_range = p.force_range == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
|
||||
|
||||
if (InitScaler(f.get(), p)) {
|
||||
VideoParams vp(instance_.avstream()->codecpar->width,
|
||||
instance_.avstream()->codecpar->height,
|
||||
native_output_pix_fmt_,
|
||||
native_channel_count_,
|
||||
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), instance_.avstream(), nullptr),
|
||||
VideoParams::kInterlaceNone,
|
||||
params.divider);
|
||||
p.divider);
|
||||
|
||||
TexturePtr tex = nullptr;
|
||||
const bool hwscale = true;
|
||||
bool hwscale = true;
|
||||
|
||||
// Attempt to use GLSL shader for faster YUV to RGB conversion
|
||||
if (hwscale) {
|
||||
AVPixelFormat src_fmt = AVPixelFormat(f.get()->format);
|
||||
if (src_fmt == AV_PIX_FMT_YUV420P
|
||||
|| src_fmt == AV_PIX_FMT_YUV422P
|
||||
|| src_fmt == AV_PIX_FMT_YUV444P
|
||||
@@ -175,7 +179,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|
||||
|| src_fmt == AV_PIX_FMT_YUV444P12LE) {
|
||||
if (Yuv2RgbShader.isNull()) {
|
||||
// Compile shader
|
||||
Yuv2RgbShader = renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
|
||||
Yuv2RgbShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
|
||||
}
|
||||
|
||||
if (!Yuv2RgbShader.isNull()) {
|
||||
@@ -207,7 +211,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|
||||
plane_params.set_channel_count(1);
|
||||
plane_params.set_divider(1);
|
||||
plane_params.set_format(native_internal_pix_fmt_);
|
||||
TexturePtr y_plane = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
|
||||
TexturePtr y_plane = p.renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
|
||||
|
||||
if (src_fmt == AV_PIX_FMT_YUV420P
|
||||
|| src_fmt == AV_PIX_FMT_YUV422P
|
||||
@@ -224,17 +228,47 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|
||||
plane_params.set_height(plane_params.height()/2);
|
||||
}
|
||||
|
||||
TexturePtr u_plane = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
|
||||
TexturePtr v_plane = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
|
||||
TexturePtr u_plane = p.renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
|
||||
TexturePtr v_plane = p.renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
|
||||
|
||||
ShaderJob job;
|
||||
job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane)));
|
||||
job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane)));
|
||||
job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane)));
|
||||
job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel));
|
||||
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, f->color_range == AVCOL_RANGE_JPEG));
|
||||
|
||||
tex = renderer->CreateTexture(vp);
|
||||
renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
|
||||
const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(f.get()->colorspace));
|
||||
job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kInt, yuv_coeffs[0]));
|
||||
job.Insert(QStringLiteral("yuv_cgu"), NodeValue(NodeValue::kInt, yuv_coeffs[2]));
|
||||
job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kInt, yuv_coeffs[3]));
|
||||
job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kInt, yuv_coeffs[1]));
|
||||
|
||||
int interlacing = 0;
|
||||
if (p.src_interlacing != VideoParams::kInterlaceNone) {
|
||||
if (frame_rate_tb_.isNull()) {
|
||||
frame_rate_tb_ = av_guess_frame_rate(instance_.fmt_ctx(), instance_.avstream(), f.get());
|
||||
|
||||
// Double frame rate for interlaced fields
|
||||
frame_rate_tb_ *= 2;
|
||||
|
||||
// Flip frame rate so it can be used as a timebase
|
||||
frame_rate_tb_.flip();
|
||||
}
|
||||
|
||||
int64_t req = Timecode::time_to_timestamp(p.time, frame_rate_tb_);
|
||||
int64_t frm = Timecode::rescale_timestamp(f->pts - instance_.avstream()->start_time, instance_.avstream()->time_base, frame_rate_tb_);
|
||||
|
||||
bool first = (req == frm);
|
||||
bool top_first = (p.src_interlacing == VideoParams::kInterlacedTopFirst);
|
||||
|
||||
interlacing = (first == top_first) ? 1 : 2;
|
||||
}
|
||||
job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing));
|
||||
job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, f->height));
|
||||
|
||||
tex = p.renderer->CreateTexture(vp);
|
||||
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,6 +276,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|
||||
if (!tex) {
|
||||
// Fallback to software pixel format conversion
|
||||
int r;
|
||||
|
||||
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, f.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||
if (r < 0) {
|
||||
return nullptr;
|
||||
@@ -251,7 +286,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tex = renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
|
||||
tex = p.renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
|
||||
|
||||
av_frame_unref(working_frame_);
|
||||
}
|
||||
@@ -290,7 +325,7 @@ QString FFmpegDecoder::id() const
|
||||
return QStringLiteral("ffmpeg");
|
||||
}
|
||||
|
||||
FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelled) const
|
||||
FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *cancelled) const
|
||||
{
|
||||
// Return value
|
||||
FootageDescription desc(id());
|
||||
@@ -416,6 +451,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
||||
stream.set_start_time(avstream->start_time);
|
||||
stream.set_time_base(avstream->time_base);
|
||||
stream.set_duration(avstream->duration);
|
||||
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG ? VideoParams::kColorRangeFull : VideoParams::kColorRangeLimited);
|
||||
|
||||
// Defaults to false, requires user intervention if incorrect
|
||||
stream.set_premultiplied_alpha(false);
|
||||
@@ -500,6 +536,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
||||
|
||||
}
|
||||
|
||||
desc.SetStreamCount(fmt_ctx->nb_streams);
|
||||
|
||||
}
|
||||
|
||||
// Free all memory
|
||||
@@ -515,7 +553,7 @@ QString FFmpegDecoder::FFmpegError(int error_code)
|
||||
return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, const QAtomicInt *cancelled)
|
||||
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
||||
{
|
||||
// Iterate through each audio frame and extract the PCM data
|
||||
|
||||
@@ -565,7 +603,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
||||
|
||||
while (true) {
|
||||
// Check if we have a `cancelled` ptr and its value
|
||||
if (cancelled && *cancelled) {
|
||||
if (cancelled && cancelled->IsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -745,12 +783,12 @@ void FFmpegDecoder::ClearFrameCache()
|
||||
}
|
||||
}
|
||||
|
||||
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *cancelled)
|
||||
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancelled)
|
||||
{
|
||||
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
|
||||
|
||||
const int64_t min_seek = -instance_.avstream()->start_time;
|
||||
int64_t seek_ts = target_ts;
|
||||
int64_t seek_ts = std::max(min_seek, target_ts - MaximumQueueSize());
|
||||
bool still_seeking = false;
|
||||
|
||||
if (time != kAnyTimecode) {
|
||||
@@ -783,7 +821,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
|
||||
while (true) {
|
||||
// Break out of loop if we've cancelled
|
||||
if (cancelled && *cancelled) {
|
||||
if (cancelled && cancelled->IsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -794,7 +832,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
// Pull from the decoder
|
||||
ret = instance_.GetFrame(working_packet_, filtered.get());
|
||||
|
||||
if (cancelled && *cancelled) {
|
||||
if (cancelled && cancelled->IsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -839,7 +877,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
} else {
|
||||
|
||||
// Cut down to thread count - 1 before we acquire a new frame
|
||||
if (cached_frames_.size() == size_t(QThread::idealThreadCount())) {
|
||||
if (cached_frames_.size() > size_t(MaximumQueueSize())) {
|
||||
RemoveFirstFrame();
|
||||
}
|
||||
|
||||
@@ -879,7 +917,12 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
|
||||
bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params)
|
||||
{
|
||||
if (params == filter_params_ && filter_graph_ && input_fmt_ == input->format) {
|
||||
if (params.divider == filter_params_.divider
|
||||
&& params.force_range == filter_params_.force_range
|
||||
&& params.maximum_format == filter_params_.maximum_format
|
||||
&& params.src_interlacing == filter_params_.src_interlacing
|
||||
&& filter_graph_
|
||||
&& input_fmt_ == input->format) {
|
||||
// We have an appropriate filter for these parameters, just return true
|
||||
return true;
|
||||
}
|
||||
@@ -944,6 +987,20 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params
|
||||
// Link filters as necessary
|
||||
AVFilterContext *last_filter = buffersrc_ctx_;
|
||||
|
||||
// Add deinterlace filter if necessary
|
||||
if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) {
|
||||
AVFilterContext* deint_filter;
|
||||
|
||||
snprintf(filter_args, kFilterArgSz, "mode=1:parity=%s",
|
||||
filter_params_.src_interlacing == VideoParams::kInterlacedTopFirst ? "0" : "1");
|
||||
|
||||
avfilter_graph_create_filter(&deint_filter, avfilter_get_by_name("yadif"), "deint", filter_args, nullptr, filter_graph_);
|
||||
|
||||
avfilter_link(last_filter, 0, deint_filter, 0);
|
||||
|
||||
last_filter = deint_filter;
|
||||
}
|
||||
|
||||
// Add scale filter if necessary
|
||||
int dst_width, dst_height;
|
||||
if (filter_params_.divider > 1) {
|
||||
@@ -1040,6 +1097,15 @@ void FFmpegDecoder::RemoveFirstFrame()
|
||||
cache_at_zero_ = false;
|
||||
}
|
||||
|
||||
int FFmpegDecoder::MaximumQueueSize()
|
||||
{
|
||||
// Fairly arbitrary size. This used to need to be the number of current threads to ensure any
|
||||
// thread that arrived would have its frame available, but if we only have one render thread,
|
||||
// that's no longer a concern. Now, this value could technically be 1, but some memory cache
|
||||
// may be useful for reversing. This value may be tweaked over time.
|
||||
return 2;
|
||||
}
|
||||
|
||||
FFmpegDecoder::Instance::Instance() :
|
||||
fmt_ctx_(nullptr),
|
||||
codec_ctx_(nullptr),
|
||||
|
||||
@@ -25,27 +25,22 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
#include <QAtomicInt>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "codec/decoder.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||
{
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||
*/
|
||||
@@ -64,12 +59,12 @@ public:
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
virtual bool SupportsAudio() override{return true;}
|
||||
|
||||
virtual FootageDescription Probe(const QString &filename, const QAtomicInt *cancelled) const override;
|
||||
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override;
|
||||
|
||||
protected:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled) override;
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, const QAtomicInt* cancelled) override;
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
private:
|
||||
@@ -151,10 +146,12 @@ private:
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
AVFramePtr RetrieveFrame(const rational &time, const QAtomicInt *cancelled);
|
||||
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
|
||||
|
||||
void RemoveFirstFrame();
|
||||
|
||||
static int MaximumQueueSize();
|
||||
|
||||
RetrieveVideoParams filter_params_;
|
||||
AVFilterGraph* filter_graph_;
|
||||
AVFilterContext* buffersrc_ctx_;
|
||||
@@ -163,6 +160,7 @@ private:
|
||||
VideoParams::Format native_internal_pix_fmt_;
|
||||
VideoParams::Format native_output_pix_fmt_;
|
||||
int native_channel_count_;
|
||||
rational frame_rate_tb_;
|
||||
|
||||
AVFrame *working_frame_;
|
||||
AVPacket *working_packet_;
|
||||
@@ -171,9 +169,6 @@ private:
|
||||
|
||||
std::list<AVFramePtr> cached_frames_;
|
||||
|
||||
bool is_working_;
|
||||
QMutex is_working_mutex_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "ffmpegencoder.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
@@ -36,8 +38,9 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) :
|
||||
fmt_ctx_(nullptr),
|
||||
video_stream_(nullptr),
|
||||
video_codec_ctx_(nullptr),
|
||||
video_alpha_scale_ctx_(nullptr),
|
||||
video_noalpha_scale_ctx_(nullptr),
|
||||
video_scale_ctx_(nullptr),
|
||||
video_buffersrc_ctx_(nullptr),
|
||||
video_buffersink_ctx_(nullptr),
|
||||
audio_stream_(nullptr),
|
||||
audio_codec_ctx_(nullptr),
|
||||
audio_resample_ctx_(nullptr),
|
||||
@@ -54,6 +57,11 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
|
||||
if (codec_info) {
|
||||
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
||||
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
|
||||
// This is a deprecated "JPEG" space, skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
|
||||
pix_fmts.append(pix_fmt_name);
|
||||
}
|
||||
@@ -142,29 +150,59 @@ bool FFmpegEncoder::Open()
|
||||
// This is the pixel format the encoder wants to encode to
|
||||
AVPixelFormat encoder_pix_fmt = video_codec_ctx_->pix_fmt;
|
||||
|
||||
// Set up a scaling context - if the native pixel format is not equal to the encoder's, we'll need to convert it
|
||||
// before encoding. Even if we don't, this may be useful for converting between linesizes, etc.
|
||||
video_alpha_scale_ctx_ = sws_getContext(params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
src_alpha_pix_fmt,
|
||||
params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
encoder_pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
video_scale_ctx_ = avfilter_graph_alloc();
|
||||
if (!video_scale_ctx_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_noalpha_scale_ctx_ = sws_getContext(params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
src_noalpha_pix_fmt,
|
||||
params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
encoder_pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
static const int FILTER_ARG_SZ = 1024;
|
||||
char filter_args[FILTER_ARG_SZ];
|
||||
|
||||
snprintf(filter_args, FILTER_ARG_SZ, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||
params().video_params().effective_width(),
|
||||
params().video_params().effective_height(),
|
||||
src_alpha_pix_fmt,
|
||||
params().video_params().time_base().numerator(),
|
||||
params().video_params().time_base().denominator(),
|
||||
params().video_params().pixel_aspect_ratio().numerator(),
|
||||
params().video_params().pixel_aspect_ratio().denominator());
|
||||
|
||||
avfilter_graph_create_filter(&video_buffersrc_ctx_, avfilter_get_by_name("buffer"), "in", filter_args, nullptr, video_scale_ctx_);
|
||||
avfilter_graph_create_filter(&video_buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, video_scale_ctx_);
|
||||
|
||||
AVFilterContext *last_filter = video_buffersrc_ctx_;
|
||||
|
||||
{
|
||||
// Set color range
|
||||
AVFilterContext* range_filter;
|
||||
|
||||
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
|
||||
params().video_params().color_range() == VideoParams::kColorRangeFull ? "full" : "limited");
|
||||
|
||||
avfilter_graph_create_filter(&range_filter, avfilter_get_by_name("scale"), "range", filter_args, nullptr, video_scale_ctx_);
|
||||
|
||||
avfilter_link(last_filter, 0, range_filter, 0);
|
||||
last_filter = range_filter;
|
||||
}
|
||||
|
||||
if (src_alpha_pix_fmt != encoder_pix_fmt) {
|
||||
// Transform pixel format
|
||||
AVFilterContext* format_filter;
|
||||
|
||||
snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u", encoder_pix_fmt);
|
||||
|
||||
avfilter_graph_create_filter(&format_filter, avfilter_get_by_name("format"), "format", filter_args, nullptr, video_scale_ctx_);
|
||||
|
||||
avfilter_link(last_filter, 0, format_filter, 0);
|
||||
last_filter = format_filter;
|
||||
}
|
||||
|
||||
avfilter_link(last_filter, 0, video_buffersink_ctx_, 0);
|
||||
|
||||
if (avfilter_graph_config(video_scale_ctx_, nullptr) < 0) {
|
||||
SetError(tr("Failed to configure filter graph"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an audio stream if it's enabled
|
||||
@@ -203,67 +241,41 @@ bool FFmpegEncoder::Open()
|
||||
|
||||
bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
AVFrame* encoded_frame = av_frame_alloc();
|
||||
|
||||
int error_code;
|
||||
const char* input_data;
|
||||
int input_linesize;
|
||||
|
||||
// Frame must be video
|
||||
encoded_frame->width = frame->width();
|
||||
encoded_frame->height = frame->height();
|
||||
encoded_frame->format = video_codec_ctx_->pix_fmt;
|
||||
|
||||
// Set interlacing
|
||||
if (frame->video_params().interlacing() != VideoParams::kInterlaceNone) {
|
||||
encoded_frame->interlaced_frame = 1;
|
||||
|
||||
if (frame->video_params().interlacing() == VideoParams::kInterlacedTopFirst) {
|
||||
encoded_frame->top_field_first = 1;
|
||||
} else {
|
||||
encoded_frame->top_field_first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
error_code = av_frame_get_buffer(encoded_frame, 0);
|
||||
if (error_code < 0) {
|
||||
FFmpegError(tr("Failed to create AVFrame buffer"), error_code);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// We may need to convert this frame to a frame that swscale will understand
|
||||
if (frame->format() != video_conversion_fmt_) {
|
||||
frame = frame->convert(video_conversion_fmt_);
|
||||
}
|
||||
|
||||
// Use swscale context to convert formats/linesizes
|
||||
input_data = frame->const_data();
|
||||
input_linesize = frame->linesize_bytes();
|
||||
AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc());
|
||||
input_frame->width = frame->width();
|
||||
input_frame->height = frame->height();
|
||||
input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(frame->format(), frame->channel_count());
|
||||
input_frame->data[0] = reinterpret_cast<uint8_t*>(frame->data());
|
||||
input_frame->linesize[0] = frame->linesize_bytes();
|
||||
|
||||
error_code = sws_scale((frame->channel_count() == VideoParams::kRGBAChannelCount) ? video_alpha_scale_ctx_ : video_noalpha_scale_ctx_,
|
||||
reinterpret_cast<const uint8_t**>(&input_data),
|
||||
&input_linesize,
|
||||
0,
|
||||
frame->height(),
|
||||
encoded_frame->data,
|
||||
encoded_frame->linesize);
|
||||
input_frame->color_primaries = video_codec_ctx_->color_primaries;
|
||||
input_frame->color_trc = video_codec_ctx_->color_trc;
|
||||
input_frame->colorspace = video_codec_ctx_->colorspace;
|
||||
input_frame->color_range = video_codec_ctx_->color_range;
|
||||
|
||||
int r;
|
||||
r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||
if (r < 0) {
|
||||
FFmpegError(tr("Failed to add frame to filter graph"), r);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (error_code < 0) {
|
||||
FFmpegError(tr("Failed to scale frame"), error_code);
|
||||
goto fail;
|
||||
AVFramePtr encoded_frame = CreateAVFramePtr(av_frame_alloc());
|
||||
r = av_buffersink_get_frame(video_buffersink_ctx_, encoded_frame.get());
|
||||
if (r < 0) {
|
||||
FFmpegError(tr("Failed to retrieve frame from buffer sink"), r);
|
||||
return false;
|
||||
}
|
||||
|
||||
encoded_frame->pts = qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base));
|
||||
|
||||
success = WriteAVFrame(encoded_frame, video_codec_ctx_, video_stream_);
|
||||
|
||||
fail:
|
||||
av_frame_free(&encoded_frame);
|
||||
|
||||
return success;
|
||||
return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_);
|
||||
}
|
||||
|
||||
bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
|
||||
@@ -484,14 +496,11 @@ void FFmpegEncoder::Close()
|
||||
audio_frame_ = nullptr;
|
||||
}
|
||||
|
||||
if (video_alpha_scale_ctx_) {
|
||||
sws_freeContext(video_alpha_scale_ctx_);
|
||||
video_alpha_scale_ctx_ = nullptr;
|
||||
}
|
||||
|
||||
if (video_noalpha_scale_ctx_) {
|
||||
sws_freeContext(video_noalpha_scale_ctx_);
|
||||
video_noalpha_scale_ctx_ = nullptr;
|
||||
if (video_scale_ctx_) {
|
||||
avfilter_graph_free(&video_scale_ctx_);
|
||||
video_scale_ctx_ = nullptr;
|
||||
video_buffersrc_ctx_ = nullptr;
|
||||
video_buffersink_ctx_ = nullptr;
|
||||
}
|
||||
|
||||
if (video_codec_ctx_) {
|
||||
@@ -606,6 +615,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
|
||||
codec_ctx->framerate = params().video_params().frame_rate().toAVRational();
|
||||
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
|
||||
codec_ctx->color_range = params().video_params().color_range() == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
|
||||
|
||||
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
|
||||
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
|
||||
@@ -628,7 +638,9 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
// Set custom options
|
||||
{
|
||||
for (auto i=params().video_opts().begin();i!=params().video_opts().end();i++) {
|
||||
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
|
||||
if (!i.key().startsWith(QStringLiteral("ove_"))) {
|
||||
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
}
|
||||
|
||||
if (params().video_bit_rate() > 0) {
|
||||
@@ -646,6 +658,18 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
if (params().video_buffer_size() > 0) {
|
||||
codec_ctx->rc_buffer_size = static_cast<int>(params().video_buffer_size());
|
||||
}
|
||||
|
||||
// nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7
|
||||
// ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov"
|
||||
if (params().color_transform().output().contains(QStringLiteral("sRGB"), Qt::CaseInsensitive)) {
|
||||
codec_ctx->color_primaries = AVCOL_PRI_BT709;
|
||||
codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1;
|
||||
codec_ctx->colorspace = AVCOL_SPC_BT709;
|
||||
} else { // Assume Rec.709
|
||||
codec_ctx->color_primaries = AVCOL_PRI_BT709;
|
||||
codec_ctx->color_trc = AVCOL_TRC_BT709;
|
||||
codec_ctx->colorspace = AVCOL_SPC_BT709;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == AVMEDIA_TYPE_AUDIO) {
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
@@ -88,8 +88,9 @@ private:
|
||||
|
||||
AVStream* video_stream_;
|
||||
AVCodecContext* video_codec_ctx_;
|
||||
SwsContext* video_alpha_scale_ctx_;
|
||||
SwsContext* video_noalpha_scale_ctx_;
|
||||
AVFilterGraph *video_scale_ctx_;
|
||||
AVFilterContext *video_buffersrc_ctx_;
|
||||
AVFilterContext *video_buffersink_ctx_;
|
||||
VideoParams::Format video_conversion_fmt_;
|
||||
|
||||
AVStream* audio_stream_;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef FOOTAGEMETA_H
|
||||
#define FOOTAGEMETA_H
|
||||
|
||||
struct FootageData {
|
||||
struct StreamData {
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif // FOOTAGEMETA_H
|
||||
@@ -45,7 +45,7 @@ QString OIIODecoder::id() const
|
||||
return QStringLiteral("oiio");
|
||||
}
|
||||
|
||||
FootageDescription OIIODecoder::Probe(const QString &filename, const QAtomicInt* cancelled) const
|
||||
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const
|
||||
{
|
||||
Q_UNUSED(cancelled)
|
||||
|
||||
@@ -73,7 +73,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, const QAtomicInt*
|
||||
|
||||
bool stream_enabled = true;
|
||||
|
||||
for (int i=0; in->seek_subimage(i, 0); i++) {
|
||||
int i;
|
||||
for (i=0; in->seek_subimage(i, 0); i++) {
|
||||
OIIO::ImageSpec spec = in->spec();
|
||||
|
||||
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
|
||||
@@ -104,6 +105,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, const QAtomicInt*
|
||||
desc.AddVideoStream(video_params);
|
||||
}
|
||||
|
||||
desc.SetStreamCount(i);
|
||||
|
||||
// If we're here, we have a successful image open
|
||||
in->close();
|
||||
|
||||
@@ -116,22 +119,20 @@ bool OIIODecoder::OpenInternal()
|
||||
return OpenImageHandler(stream().filename(), stream().stream());
|
||||
}
|
||||
|
||||
TexturePtr OIIODecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ¶ms, const QAtomicInt *cancelled)
|
||||
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
Q_UNUSED(timecode)
|
||||
Q_UNUSED(cancelled)
|
||||
|
||||
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
|
||||
vp.set_divider(params.divider);
|
||||
vp.set_divider(p.divider);
|
||||
|
||||
if (!buffer_.is_allocated() || last_params_ != params) {
|
||||
last_params_ = params;
|
||||
if (!buffer_.is_allocated()
|
||||
|| last_params_.divider != p.divider) {
|
||||
last_params_ = p;
|
||||
|
||||
buffer_.destroy();
|
||||
buffer_.set_video_params(vp);
|
||||
buffer_.allocate();
|
||||
|
||||
if (params.divider == 1) {
|
||||
if (p.divider == 1) {
|
||||
// Just upload straight to the buffer
|
||||
image_->read_image(oiio_pix_fmt_, buffer_.data(), OIIO::AutoStride, buffer_.linesize_bytes());
|
||||
} else {
|
||||
@@ -153,7 +154,7 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(Renderer *renderer, const rational
|
||||
}
|
||||
}
|
||||
|
||||
return renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
|
||||
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
|
||||
}
|
||||
|
||||
void OIIODecoder::CloseInternal()
|
||||
|
||||
@@ -40,11 +40,11 @@ public:
|
||||
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
|
||||
virtual FootageDescription Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
||||
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override;
|
||||
|
||||
protected:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled) override;
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user