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
+3 -6
View File
@@ -21,13 +21,10 @@
#ifndef ALPHAASSOC_H
#define ALPHAASSOC_H
namespace olive {
namespace olive
{
enum AlphaAssociated {
kAlphaNone,
kAlphaUnassociated,
kAlphaAssociated
};
enum AlphaAssociated { kAlphaNone, kAlphaUnassociated, kAlphaAssociated };
}
+78 -63
View File
@@ -28,12 +28,14 @@
#include "common/filefunctions.h"
#include "node/output/viewer/viewer.h"
namespace olive {
namespace olive
{
const qint64 AudioPlaybackCache::kDefaultSegmentSizePerChannel = 10 * 1024 * 1024;
const qint64 AudioPlaybackCache::kDefaultSegmentSizePerChannel =
10 * 1024 * 1024;
AudioPlaybackCache::AudioPlaybackCache(QObject* parent) :
PlaybackCache(parent)
AudioPlaybackCache::AudioPlaybackCache(QObject *parent)
: PlaybackCache(parent)
{
}
@@ -43,93 +45,106 @@ AudioPlaybackCache::~AudioPlaybackCache()
void AudioPlaybackCache::SetParameters(const AudioParams &params)
{
if (params_ == params) {
return;
}
if (params_ == params) {
return;
}
params_ = params;
params_ = params;
}
void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples)
void AudioPlaybackCache::WritePCM(const TimeRange &range,
const TimeRangeList &valid_ranges,
const SampleBuffer &samples)
{
for (const TimeRange &r : valid_ranges) {
if (WritePartOfSampleBuffer(samples, r.in(), r.in() - range.in(), r.length())) {
Validate(r);
}
}
for (const TimeRange &r : valid_ranges) {
if (WritePartOfSampleBuffer(samples, r.in(), r.in() - range.in(),
r.length())) {
Validate(r);
}
}
}
void AudioPlaybackCache::WriteSilence(const TimeRange &range)
{
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
// it an empty sample buffer
WritePCM(range, {range}, SampleBuffer());
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
// it an empty sample buffer
WritePCM(range, { range }, SampleBuffer());
}
bool AudioPlaybackCache::WritePartOfSampleBuffer(const SampleBuffer &samples, const rational &write_start, const rational &buffer_start, const rational &length)
bool AudioPlaybackCache::WritePartOfSampleBuffer(const SampleBuffer &samples,
const rational &write_start,
const rational &buffer_start,
const rational &length)
{
int64_t length_in_bytes = params_.time_to_bytes_per_channel(length);
int64_t length_in_bytes = params_.time_to_bytes_per_channel(length);
int64_t start_cache_offset = params_.time_to_bytes_per_channel(write_start);
int64_t end_cache_offset = start_cache_offset + length_in_bytes;
int64_t start_cache_offset = params_.time_to_bytes_per_channel(write_start);
int64_t end_cache_offset = start_cache_offset + length_in_bytes;
int64_t start_buffer_offset = params_.time_to_bytes_per_channel(buffer_start);
int64_t end_buffer_offset = std::min(start_buffer_offset + length_in_bytes, params_.samples_to_bytes_per_channel(samples.sample_count()));
int64_t start_buffer_offset =
params_.time_to_bytes_per_channel(buffer_start);
int64_t end_buffer_offset =
std::min(start_buffer_offset + length_in_bytes,
params_.samples_to_bytes_per_channel(samples.sample_count()));
int64_t current_cache_offset = start_cache_offset;
int64_t current_buffer_offset = start_buffer_offset;
int64_t current_cache_offset = start_cache_offset;
int64_t current_buffer_offset = start_buffer_offset;
bool success = true;
bool success = true;
while (current_cache_offset != end_cache_offset) {
int64_t segment = current_cache_offset / kDefaultSegmentSizePerChannel;
int64_t segment_start = segment * kDefaultSegmentSizePerChannel;
int64_t segment_end = segment_start + kDefaultSegmentSizePerChannel;
while (current_cache_offset != end_cache_offset) {
int64_t segment = current_cache_offset / kDefaultSegmentSizePerChannel;
int64_t segment_start = segment * kDefaultSegmentSizePerChannel;
int64_t segment_end = segment_start + kDefaultSegmentSizePerChannel;
int64_t offset_in_segment = current_cache_offset - segment_start;
int64_t write_len = segment_end - offset_in_segment;
int64_t max_buffer_len = end_buffer_offset - current_buffer_offset;
int64_t zero_len = 0;
int64_t offset_in_segment = current_cache_offset - segment_start;
int64_t write_len = segment_end - offset_in_segment;
int64_t max_buffer_len = end_buffer_offset - current_buffer_offset;
int64_t zero_len = 0;
if (write_len > max_buffer_len) {
zero_len = write_len - max_buffer_len;
write_len = max_buffer_len;
}
if (write_len > max_buffer_len) {
zero_len = write_len - max_buffer_len;
write_len = max_buffer_len;
}
for (int channel=0; channel<params_.channel_count(); channel++) {
QString filename = GetSegmentFilename(segment, channel);
for (int channel = 0; channel < params_.channel_count(); channel++) {
QString filename = GetSegmentFilename(segment, channel);
if (!FileFunctions::DirectoryIsValid(QFileInfo(filename).dir())) {
success = false;
break;
}
if (!FileFunctions::DirectoryIsValid(QFileInfo(filename).dir())) {
success = false;
break;
}
QFile f(filename);
if (f.open(QFile::ReadWrite)) {
f.seek(offset_in_segment);
f.write(reinterpret_cast<const char*>(samples.data(channel)) + current_buffer_offset, write_len);
QFile f(filename);
if (f.open(QFile::ReadWrite)) {
f.seek(offset_in_segment);
f.write(reinterpret_cast<const char *>(samples.data(channel)) +
current_buffer_offset,
write_len);
if (zero_len > 0) {
QByteArray b(zero_len, 0);
f.write(b.constData());
}
if (zero_len > 0) {
QByteArray b(zero_len, 0);
f.write(b.constData());
}
f.close();
} else {
success = false;
}
}
f.close();
} else {
success = false;
}
}
current_cache_offset += write_len;
current_buffer_offset += write_len;
}
current_cache_offset += write_len;
current_buffer_offset += write_len;
}
return success;
return success;
}
QString AudioPlaybackCache::GetSegmentFilename(qint64 segment_index, int channel)
QString AudioPlaybackCache::GetSegmentFilename(qint64 segment_index,
int channel)
{
return GetThisCacheDirectory().filePath(QStringLiteral("%1.%2").arg(QString::number(segment_index), QString::number(channel)));
return GetThisCacheDirectory().filePath(QStringLiteral("%1.%2").arg(
QString::number(segment_index), QString::number(channel)));
}
}
+21 -18
View File
@@ -24,7 +24,8 @@
#include "audio/audiovisualwaveform.h"
#include "render/playbackcache.h"
namespace olive {
namespace olive
{
/**
* @brief A fully integrated system of storing and playing cached audio
@@ -49,34 +50,36 @@ namespace olive {
* acts identically to a file-based IO device, transparently joining segments together and acting
* like one contiguous file.
*/
class AudioPlaybackCache : public PlaybackCache
{
Q_OBJECT
class AudioPlaybackCache : public PlaybackCache {
Q_OBJECT
public:
AudioPlaybackCache(QObject* parent = nullptr);
AudioPlaybackCache(QObject *parent = nullptr);
virtual ~AudioPlaybackCache() override;
virtual ~AudioPlaybackCache() override;
AudioParams GetParameters()
{
return params_;
}
AudioParams GetParameters()
{
return params_;
}
void SetParameters(const AudioParams& params);
void SetParameters(const AudioParams &params);
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples);
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges,
const SampleBuffer &samples);
void WriteSilence(const TimeRange &range);
void WriteSilence(const TimeRange &range);
private:
bool WritePartOfSampleBuffer(const SampleBuffer &samples, const rational &write_start, const rational &buffer_start, const rational &length);
bool WritePartOfSampleBuffer(const SampleBuffer &samples,
const rational &write_start,
const rational &buffer_start,
const rational &length);
QString GetSegmentFilename(qint64 segment_index, int channel);
QString GetSegmentFilename(qint64 segment_index, int channel);
static const qint64 kDefaultSegmentSizePerChannel;
AudioParams params_;
static const qint64 kDefaultSegmentSizePerChannel;
AudioParams params_;
};
}
+69 -55
View File
@@ -20,96 +20,110 @@
#include "audiowaveformcache.h"
namespace olive {
namespace olive
{
#define super PlaybackCache
AudioWaveformCache::AudioWaveformCache(QObject *parent) :
super{parent}
AudioWaveformCache::AudioWaveformCache(QObject *parent)
: super{ parent }
{
waveforms_ = std::make_shared<AudioVisualWaveform>();
waveforms_ = std::make_shared<AudioVisualWaveform>();
}
void AudioWaveformCache::WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform)
void AudioWaveformCache::WriteWaveform(const TimeRange &range,
const TimeRangeList &valid_ranges,
const AudioVisualWaveform *waveform)
{
// Write each valid range to the segments
foreach (const TimeRange& r, valid_ranges) {
if (waveform) {
waveforms_->OverwriteSums(*waveform, r.in(), r.in() - range.in(), r.length());
}
// Write each valid range to the segments
foreach (const TimeRange &r, valid_ranges) {
if (waveform) {
waveforms_->OverwriteSums(*waveform, r.in(), r.in() - range.in(),
r.length());
}
Validate(r);
}
Validate(r);
}
}
void DrawSubRect(QPainter *painter, const QRect &rect, const double &scale, const TimeRange &wave_range, const AudioVisualWaveform &waveform, const TimeRange &subrange)
void DrawSubRect(QPainter *painter, const QRect &rect, const double &scale,
const TimeRange &wave_range,
const AudioVisualWaveform &waveform, const TimeRange &subrange)
{
// Find start time of passthrough
TimeRange intersect = wave_range.Intersected(subrange);
// Find start time of passthrough
TimeRange intersect = wave_range.Intersected(subrange);
// Create new rect that starts at the offset of pass_start from start_time
// Set rect width to either length of passthrough or until the end
QRect pass_rect(rect.x() + (intersect.in() - wave_range.in()).toDouble() * scale,
rect.y(),
intersect.length().toDouble() * scale,
rect.height());
// Create new rect that starts at the offset of pass_start from start_time
// Set rect width to either length of passthrough or until the end
QRect pass_rect(
rect.x() + (intersect.in() - wave_range.in()).toDouble() * scale,
rect.y(), intersect.length().toDouble() * scale, rect.height());
// Draw waveform with this info
AudioVisualWaveform::DrawWaveform(painter, pass_rect, scale, waveform, intersect.in());
// Draw waveform with this info
AudioVisualWaveform::DrawWaveform(painter, pass_rect, scale, waveform,
intersect.in());
}
void AudioWaveformCache::Draw(QPainter *painter, const QRect &rect, const double &scale, const rational &start_time) const
void AudioWaveformCache::Draw(QPainter *painter, const QRect &rect,
const double &scale,
const rational &start_time) const
{
if (!passthroughs_.empty()) {
TimeRange wave_range(start_time, start_time + rational::fromDouble(rect.width() / scale));
TimeRangeList draw_range = {wave_range};
for (const WaveformPassthrough &p : passthroughs_) {
if (draw_range.OverlapsWith(p, true, false)) {
DrawSubRect(painter, rect, scale, wave_range, *p.waveform, p);
if (!passthroughs_.empty()) {
TimeRange wave_range(start_time,
start_time +
rational::fromDouble(rect.width() / scale));
TimeRangeList draw_range = { wave_range };
for (const WaveformPassthrough &p : passthroughs_) {
if (draw_range.OverlapsWith(p, true, false)) {
DrawSubRect(painter, rect, scale, wave_range, *p.waveform, p);
// Remove this range
draw_range.remove(p);
}
}
// Remove this range
draw_range.remove(p);
}
}
for (const TimeRange &r : draw_range) {
DrawSubRect(painter, rect, scale, wave_range, *waveforms_, r);
}
} else {
AudioVisualWaveform::DrawWaveform(painter, rect, scale, *waveforms_, start_time);
}
for (const TimeRange &r : draw_range) {
DrawSubRect(painter, rect, scale, wave_range, *waveforms_, r);
}
} else {
AudioVisualWaveform::DrawWaveform(painter, rect, scale, *waveforms_,
start_time);
}
}
AudioVisualWaveform::Sample AudioWaveformCache::GetSummaryFromTime(const rational &start, const rational &length) const
AudioVisualWaveform::Sample
AudioWaveformCache::GetSummaryFromTime(const rational &start,
const rational &length) const
{
return waveforms_->GetSummaryFromTime(start, length);
return waveforms_->GetSummaryFromTime(start, length);
}
rational AudioWaveformCache::length() const
{
return waveforms_->length();
return waveforms_->length();
}
void AudioWaveformCache::SetPassthrough(PlaybackCache *cache)
{
AudioWaveformCache *c = static_cast<AudioWaveformCache*>(cache);
AudioWaveformCache *c = static_cast<AudioWaveformCache *>(cache);
for (const TimeRange &r : c->GetValidatedRanges()) {
WaveformPassthrough t = r;
t.waveform = c->waveforms_;
passthroughs_.push_back(t);
}
passthroughs_.insert(passthroughs_.end(), c->passthroughs_.begin(), c->passthroughs_.end());
for (const TimeRange &r : c->GetValidatedRanges()) {
WaveformPassthrough t = r;
t.waveform = c->waveforms_;
passthroughs_.push_back(t);
}
passthroughs_.insert(passthroughs_.end(), c->passthroughs_.begin(),
c->passthroughs_.end());
SetParameters(c->GetParameters());
SetSavingEnabled(c->IsSavingEnabled());
SetParameters(c->GetParameters());
SetSavingEnabled(c->IsSavingEnabled());
}
void AudioWaveformCache::InvalidateEvent(const TimeRange& range)
void AudioWaveformCache::InvalidateEvent(const TimeRange &range)
{
TimeRangeList::util_remove(&passthroughs_, range);
TimeRangeList::util_remove(&passthroughs_, range);
super::InvalidateEvent(range);
super::InvalidateEvent(range);
}
}
+36 -30
View File
@@ -24,53 +24,59 @@
#include "audio/audiovisualwaveform.h"
#include "playbackcache.h"
namespace olive {
class AudioWaveformCache : public PlaybackCache
namespace olive
{
Q_OBJECT
class AudioWaveformCache : public PlaybackCache {
Q_OBJECT
public:
AudioWaveformCache(QObject *parent = nullptr);
AudioWaveformCache(QObject *parent = nullptr);
void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform);
void WriteWaveform(const TimeRange &range,
const TimeRangeList &valid_ranges,
const AudioVisualWaveform *waveform);
const AudioParams &GetParameters() const { return params_; }
void SetParameters(const AudioParams &p)
{
params_ = p;
waveforms_->set_channel_count(p.channel_count());
}
const AudioParams &GetParameters() const
{
return params_;
}
void SetParameters(const AudioParams &p)
{
params_ = p;
waveforms_->set_channel_count(p.channel_count());
}
void Draw(QPainter* painter, const QRect &rect, const double &scale, const rational &start_time) const;
void Draw(QPainter *painter, const QRect &rect, const double &scale,
const rational &start_time) const;
AudioVisualWaveform::Sample GetSummaryFromTime(const rational &start, const rational &length) const;
AudioVisualWaveform::Sample
GetSummaryFromTime(const rational &start, const rational &length) const;
rational length() const;
rational length() const;
virtual void SetPassthrough(PlaybackCache *cache) override;
virtual void SetPassthrough(PlaybackCache *cache) override;
protected:
virtual void InvalidateEvent(const TimeRange& range) override;
virtual void InvalidateEvent(const TimeRange &range) override;
private:
using WaveformPtr = std::shared_ptr<AudioVisualWaveform>;
using WaveformPtr = std::shared_ptr<AudioVisualWaveform>;
WaveformPtr waveforms_;
WaveformPtr waveforms_;
AudioParams params_;
AudioParams params_;
class WaveformPassthrough : public TimeRange
{
public:
WaveformPassthrough(const TimeRange &r) :
TimeRange(r)
{}
class WaveformPassthrough : public TimeRange {
public:
WaveformPassthrough(const TimeRange &r)
: TimeRange(r)
{
}
WaveformPtr waveform;
};
std::vector<WaveformPassthrough> passthroughs_;
WaveformPtr waveform;
};
std::vector<WaveformPassthrough> passthroughs_;
};
}
+29 -29
View File
@@ -3,44 +3,44 @@
#include <QMutex>
namespace olive {
class CancelAtom
namespace olive
{
class CancelAtom {
public:
CancelAtom() :
cancelled_(false),
heard_(false)
{}
CancelAtom()
: cancelled_(false)
, heard_(false)
{
}
bool IsCancelled()
{
QMutexLocker locker(&mutex_);
if (cancelled_) {
heard_ = true;
}
return cancelled_;
}
bool IsCancelled()
{
QMutexLocker locker(&mutex_);
if (cancelled_) {
heard_ = true;
}
return cancelled_;
}
void Cancel()
{
QMutexLocker locker(&mutex_);
cancelled_ = true;
}
void Cancel()
{
QMutexLocker locker(&mutex_);
cancelled_ = true;
}
bool HeardCancel()
{
QMutexLocker locker(&mutex_);
return heard_;
}
bool HeardCancel()
{
QMutexLocker locker(&mutex_);
return heard_;
}
private:
QMutex mutex_;
QMutex mutex_;
bool cancelled_;
bool heard_;
bool cancelled_;
bool heard_;
};
}
+78 -68
View File
@@ -24,117 +24,127 @@
#include "common/ocioutils.h"
#include "node/color/colormanager/colormanager.h"
namespace olive {
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform, Direction direction)
namespace olive
{
const QString& output = (transform.output().isEmpty()) ? config->GetDefaultDisplay() : transform.output();
if (transform.is_display()) {
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
const ColorTransform &transform,
Direction direction)
{
const QString &output = (transform.output().isEmpty()) ?
config->GetDefaultDisplay() :
transform.output();
const QString& view = (transform.view().isEmpty()) ? config->GetDefaultView(output) : transform.view();
if (transform.is_display()) {
const QString &view = (transform.view().isEmpty()) ?
config->GetDefaultView(output) :
transform.view();
auto display_transform = OCIO::DisplayViewTransform::Create();
auto display_transform = OCIO::DisplayViewTransform::Create();
display_transform->setSrc(input.toUtf8());
display_transform->setDisplay(output.toUtf8());
display_transform->setView(view.toUtf8());
display_transform->setDirection(direction == kNormal ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE);
display_transform->setSrc(input.toUtf8());
display_transform->setDisplay(output.toUtf8());
display_transform->setView(view.toUtf8());
display_transform->setDirection(direction == kNormal ?
OCIO::TRANSFORM_DIR_FORWARD :
OCIO::TRANSFORM_DIR_INVERSE);
if (transform.look().isEmpty()) {
processor_ = config->GetConfig()->getProcessor(display_transform);
} else {
auto group = OCIO::GroupTransform::Create();
if (transform.look().isEmpty()) {
processor_ = config->GetConfig()->getProcessor(display_transform);
} else {
auto group = OCIO::GroupTransform::Create();
const char* out_cs = OCIO::LookTransform::GetLooksResultColorSpace(config->GetConfig(),
config->GetConfig()->getCurrentContext(),
transform.look().toUtf8());
const char *out_cs = OCIO::LookTransform::GetLooksResultColorSpace(
config->GetConfig(), config->GetConfig()->getCurrentContext(),
transform.look().toUtf8());
auto lt = OCIO::LookTransform::Create();
lt->setSrc(input.toUtf8());
lt->setDst(out_cs);
lt->setLooks(transform.look().toUtf8());
lt->setSkipColorSpaceConversion(false);
group->appendTransform(lt);
auto lt = OCIO::LookTransform::Create();
lt->setSrc(input.toUtf8());
lt->setDst(out_cs);
lt->setLooks(transform.look().toUtf8());
lt->setSkipColorSpaceConversion(false);
group->appendTransform(lt);
display_transform->setSrc(out_cs);
group->appendTransform(display_transform);
display_transform->setSrc(out_cs);
group->appendTransform(display_transform);
processor_ = config->GetConfig()->getProcessor(group);
}
processor_ = config->GetConfig()->getProcessor(group);
}
} else {
} else {
try {
if (direction == kNormal) {
processor_ = config->GetConfig()->getProcessor(input.toUtf8(),
output.toUtf8());
} else {
processor_ = config->GetConfig()->getProcessor(output.toUtf8(),
input.toUtf8());
}
} catch (OCIO::Exception &e) {
qWarning() << "ColorProcessor exception:" << e.what();
}
}
try {
if (direction == kNormal) {
processor_ = config->GetConfig()->getProcessor(input.toUtf8(), output.toUtf8());
} else {
processor_ = config->GetConfig()->getProcessor(output.toUtf8(), input.toUtf8());
}
} catch (OCIO::Exception &e) {
qWarning() << "ColorProcessor exception:" << e.what();
}
}
cpu_processor_ = processor_->getDefaultCPUProcessor();
cpu_processor_ = processor_->getDefaultCPUProcessor();
}
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
{
processor_ = processor;
cpu_processor_ = processor_->getDefaultCPUProcessor();
processor_ = processor;
cpu_processor_ = processor_->getDefaultCPUProcessor();
}
void ColorProcessor::ConvertFrame(Frame *f)
{
OCIO::BitDepth ocio_bit_depth = OCIOUtils::GetOCIOBitDepthFromPixelFormat(f->format());
OCIO::BitDepth ocio_bit_depth =
OCIOUtils::GetOCIOBitDepthFromPixelFormat(f->format());
if (ocio_bit_depth == OCIO::BIT_DEPTH_UNKNOWN) {
qCritical() << "Tried to color convert frame with no format";
return;
}
if (ocio_bit_depth == OCIO::BIT_DEPTH_UNKNOWN) {
qCritical() << "Tried to color convert frame with no format";
return;
}
OCIO::PackedImageDesc img(f->data(),
f->width(),
f->height(),
f->channel_count(),
ocio_bit_depth,
OCIO::AutoStride,
OCIO::AutoStride,
f->linesize_bytes());
OCIO::PackedImageDesc img(f->data(), f->width(), f->height(),
f->channel_count(), ocio_bit_depth,
OCIO::AutoStride, OCIO::AutoStride,
f->linesize_bytes());
cpu_processor_->apply(img);
cpu_processor_->apply(img);
}
Color ColorProcessor::ConvertColor(const Color& in)
Color ColorProcessor::ConvertColor(const Color &in)
{
// I've been bamboozled
float c[4] = {float(in.red()), float(in.green()), float(in.blue()), float(in.alpha())};
// I've been bamboozled
float c[4] = { float(in.red()), float(in.green()), float(in.blue()),
float(in.alpha()) };
cpu_processor_->applyRGBA(c);
cpu_processor_->applyRGBA(c);
return Color(c[0], c[1], c[2], c[3]);
return Color(c[0], c[1], c[2], c[3]);
}
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform, Direction direction)
ColorProcessorPtr ColorProcessor::Create(ColorManager *config,
const QString &input,
const ColorTransform &transform,
Direction direction)
{
return std::make_shared<ColorProcessor>(config, input, transform, direction);
return std::make_shared<ColorProcessor>(config, input, transform,
direction);
}
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstProcessorRcPtr processor)
{
return std::make_shared<ColorProcessor>(processor);
return std::make_shared<ColorProcessor>(processor);
}
OCIO::ConstProcessorRcPtr ColorProcessor::GetProcessor()
{
return processor_;
return processor_;
}
void ColorProcessor::ConvertFrame(FramePtr f)
{
ConvertFrame(f.get());
ConvertFrame(f.get());
}
}
+23 -23
View File
@@ -25,46 +25,46 @@
#include "common/ocioutils.h"
#include "render/colortransform.h"
namespace olive {
namespace olive
{
class ColorManager;
class ColorProcessor;
using ColorProcessorPtr = std::shared_ptr<ColorProcessor>;
class ColorProcessor
{
class ColorProcessor {
public:
enum Direction {
kNormal,
kInverse
};
enum Direction { kNormal, kInverse };
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
ColorProcessor(OCIO::ConstProcessorRcPtr processor);
ColorProcessor(ColorManager *config, const QString &input,
const ColorTransform &dest_space,
Direction direction = kNormal);
ColorProcessor(OCIO::ConstProcessorRcPtr processor);
DISABLE_COPY_MOVE(ColorProcessor)
DISABLE_COPY_MOVE(ColorProcessor)
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
static ColorProcessorPtr Create(OCIO::ConstProcessorRcPtr processor);
static ColorProcessorPtr Create(ColorManager *config, const QString &input,
const ColorTransform &dest_space,
Direction direction = kNormal);
static ColorProcessorPtr Create(OCIO::ConstProcessorRcPtr processor);
OCIO::ConstProcessorRcPtr GetProcessor();
OCIO::ConstProcessorRcPtr GetProcessor();
void ConvertFrame(FramePtr f);
void ConvertFrame(Frame* f);
void ConvertFrame(FramePtr f);
void ConvertFrame(Frame *f);
Color ConvertColor(const Color &in);
Color ConvertColor(const Color &in);
const char *id() const
{
return processor_->getCacheID();
}
const char *id() const
{
return processor_->getCacheID();
}
private:
OCIO::ConstProcessorRcPtr processor_;
OCIO::ConstCPUProcessorRcPtr cpu_processor_;
OCIO::ConstProcessorRcPtr processor_;
OCIO::ConstCPUProcessorRcPtr cpu_processor_;
};
using ColorProcessorChain = QVector<ColorProcessorPtr>;
+2 -1
View File
@@ -23,7 +23,8 @@
#include "render/colorprocessor.h"
namespace olive {
namespace olive
{
using ColorProcessorCache = QHash<QString, ColorProcessorPtr>;
+44 -39
View File
@@ -26,57 +26,62 @@
#include "common/define.h"
#include "common/ocioutils.h"
namespace olive {
class ColorTransform
namespace olive
{
class ColorTransform {
public:
ColorTransform()
{
is_display_ = false;
}
ColorTransform()
{
is_display_ = false;
}
ColorTransform(const QString& output)
{
is_display_ = false;
output_ = output;
}
ColorTransform(const QString &output)
{
is_display_ = false;
output_ = output;
}
ColorTransform(const QString& display, const QString& view, const QString& look)
{
is_display_ = true;
output_ = display;
view_ = view;
look_ = look;
}
ColorTransform(const QString &display, const QString &view,
const QString &look)
{
is_display_ = true;
output_ = display;
view_ = view;
look_ = look;
}
bool is_display() const {
return is_display_;
}
bool is_display() const
{
return is_display_;
}
const QString& display() const {
return output_;
}
const QString &display() const
{
return output_;
}
const QString& output() const {
return output_;
}
const QString &output() const
{
return output_;
}
const QString& view() const {
return view_;
}
const QString &view() const
{
return view_;
}
const QString& look() const {
return look_;
}
const QString &look() const
{
return look_;
}
private:
QString output_;
bool is_display_;
QString view_;
QString look_;
QString output_;
bool is_display_;
QString view_;
QString look_;
};
}
+231 -216
View File
@@ -32,375 +32,390 @@
#include "core.h"
#include "dialog/diskcache/diskcachedialog.h"
namespace olive {
namespace olive
{
DiskManager* DiskManager::instance_ = nullptr;
DiskManager *DiskManager::instance_ = nullptr;
DiskManager::DiskManager()
{
// Add default cache location
QFile default_disk_cache_file(GetDefaultDiskCacheConfigFile());
if (default_disk_cache_file.open(QFile::ReadOnly)) {
QString default_dir = default_disk_cache_file.readAll();
// Add default cache location
QFile default_disk_cache_file(GetDefaultDiskCacheConfigFile());
if (default_disk_cache_file.open(QFile::ReadOnly)) {
QString default_dir = default_disk_cache_file.readAll();
if (!default_dir.isEmpty()) {
if (FileFunctions::DirectoryIsValid(default_dir)) {
GetOpenFolder(default_dir);
} else {
QMessageBox::warning(nullptr,
tr("Disk Cache Error"),
tr("Unable to set custom application disk cache. Using default instead."));
}
}
if (!default_dir.isEmpty()) {
if (FileFunctions::DirectoryIsValid(default_dir)) {
GetOpenFolder(default_dir);
} else {
QMessageBox::warning(
nullptr, tr("Disk Cache Error"),
tr("Unable to set custom application disk cache. Using default instead."));
}
}
default_disk_cache_file.close();
}
default_disk_cache_file.close();
}
// If no custom default was loaded, load default
if (open_folders_.isEmpty()) {
GetOpenFolder(GetDefaultDiskCachePath());
}
// If no custom default was loaded, load default
if (open_folders_.isEmpty()) {
GetOpenFolder(GetDefaultDiskCachePath());
}
QFile disk_cache_index(QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("diskcache2")));
QFile disk_cache_index(QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("diskcache2")));
if (disk_cache_index.open(QFile::ReadOnly)) {
QTextStream stream(&disk_cache_index);
if (disk_cache_index.open(QFile::ReadOnly)) {
QTextStream stream(&disk_cache_index);
QString line;
while (stream.readLineInto(&line)) {
GetOpenFolder(line);
}
QString line;
while (stream.readLineInto(&line)) {
GetOpenFolder(line);
}
disk_cache_index.close();
}
disk_cache_index.close();
}
}
DiskManager::~DiskManager()
{
QFile default_disk_cache_file(GetDefaultDiskCacheConfigFile());
if (default_disk_cache_file.open(QFile::WriteOnly)) {
if (GetDefaultDiskCachePath() != GetDefaultCachePath()) {
default_disk_cache_file.write(GetDefaultCachePath().toUtf8());
}
QFile default_disk_cache_file(GetDefaultDiskCacheConfigFile());
if (default_disk_cache_file.open(QFile::WriteOnly)) {
if (GetDefaultDiskCachePath() != GetDefaultCachePath()) {
default_disk_cache_file.write(GetDefaultCachePath().toUtf8());
}
default_disk_cache_file.close();
}
default_disk_cache_file.close();
}
}
void DiskManager::CreateInstance()
{
instance_ = new DiskManager();
instance_ = new DiskManager();
}
void DiskManager::DestroyInstance()
{
delete instance_;
instance_ = nullptr;
delete instance_;
instance_ = nullptr;
}
DiskManager *DiskManager::instance()
{
return instance_;
return instance_;
}
void DiskManager::Accessed(const QString &cache_folder, const QString &filename)
{
DiskCacheFolder* f = GetOpenFolder(cache_folder);
DiskCacheFolder *f = GetOpenFolder(cache_folder);
f->Accessed(filename);
f->Accessed(filename);
}
void DiskManager::CreatedFile(const QString &cache_folder, const QString &filename)
void DiskManager::CreatedFile(const QString &cache_folder,
const QString &filename)
{
DiskCacheFolder* f = GetOpenFolder(cache_folder);
DiskCacheFolder *f = GetOpenFolder(cache_folder);
f->CreatedFile(filename);
f->CreatedFile(filename);
}
void DiskManager::DeleteSpecificFile(const QString &filename)
{
foreach (DiskCacheFolder* f, open_folders_) {
f->DeleteSpecificFile(filename);
}
foreach (DiskCacheFolder *f, open_folders_) {
f->DeleteSpecificFile(filename);
}
}
bool DiskManager::ClearDiskCache(const QString &cache_folder)
{
DiskCacheFolder* f = GetOpenFolder(cache_folder);
DiskCacheFolder *f = GetOpenFolder(cache_folder);
return f->ClearCache();
return f->ClearCache();
}
DiskCacheFolder *DiskManager::GetOpenFolder(const QString &path)
{
// If path is empty, this must mean default
if (path.isEmpty()) {
return GetDefaultCacheFolder();
}
// If path is empty, this must mean default
if (path.isEmpty()) {
return GetDefaultCacheFolder();
}
// See if we have an existing path with this name
foreach (DiskCacheFolder* f, open_folders_) {
if (f->GetPath() == path) {
return f;
}
}
// See if we have an existing path with this name
foreach (DiskCacheFolder *f, open_folders_) {
if (f->GetPath() == path) {
return f;
}
}
// We must have to open this folder
DiskCacheFolder* f = new DiskCacheFolder(path, this);
connect(f, &DiskCacheFolder::DeletedFrame, this, &DiskManager::DeletedFrame);
open_folders_.append(f);
// We must have to open this folder
DiskCacheFolder *f = new DiskCacheFolder(path, this);
connect(f, &DiskCacheFolder::DeletedFrame, this,
&DiskManager::DeletedFrame);
open_folders_.append(f);
return f;
return f;
}
bool DiskManager::ShowDiskCacheChangeConfirmationDialog(QWidget *parent)
{
return (QMessageBox::question(parent,
tr("Disk Cache"),
tr("You've chosen to change the default disk cache location. This "
"will invalidate your current cache. Would you like to continue?"),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok);
return (
QMessageBox::question(
parent, tr("Disk Cache"),
tr("You've chosen to change the default disk cache location. This "
"will invalidate your current cache. Would you like to continue?"),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok);
}
QString DiskManager::GetDefaultDiskCacheConfigFile()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("defaultdiskcache"));
return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("defaultdiskcache"));
}
QString DiskManager::GetDefaultDiskCachePath()
{
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("mediacache");
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation))
.filePath("mediacache");
}
void DiskManager::ShowDiskCacheSettingsDialog(DiskCacheFolder *folder, QWidget *parent)
void DiskManager::ShowDiskCacheSettingsDialog(DiskCacheFolder *folder,
QWidget *parent)
{
DiskCacheDialog d(folder, parent);
d.exec();
DiskCacheDialog d(folder, parent);
d.exec();
}
void DiskManager::ShowDiskCacheSettingsDialog(const QString &path, QWidget *parent)
void DiskManager::ShowDiskCacheSettingsDialog(const QString &path,
QWidget *parent)
{
if (!FileFunctions::DirectoryIsValid(path)) {
QMessageBox::critical(parent, tr("Disk Cache Error"),
tr("Failed to open disk cache at \"%1\". Try a different folder.").arg(path));
return;
}
if (!FileFunctions::DirectoryIsValid(path)) {
QMessageBox::critical(
parent, tr("Disk Cache Error"),
tr("Failed to open disk cache at \"%1\". Try a different folder.")
.arg(path));
return;
}
DiskCacheFolder* folder = GetOpenFolder(path);
DiskCacheFolder *folder = GetOpenFolder(path);
ShowDiskCacheSettingsDialog(folder, parent);
ShowDiskCacheSettingsDialog(folder, parent);
}
DiskCacheFolder::DiskCacheFolder(const QString &path, QObject *parent) :
QObject(parent)
DiskCacheFolder::DiskCacheFolder(const QString &path, QObject *parent)
: QObject(parent)
{
SetPath(path);
SetPath(path);
save_timer_.setInterval(OLIVE_CONFIG("DiskCacheSaveInterval").toInt());
connect(&save_timer_, &QTimer::timeout, this, &DiskCacheFolder::SaveDiskCacheIndex);
save_timer_.start();
save_timer_.setInterval(OLIVE_CONFIG("DiskCacheSaveInterval").toInt());
connect(&save_timer_, &QTimer::timeout, this,
&DiskCacheFolder::SaveDiskCacheIndex);
save_timer_.start();
}
DiskCacheFolder::~DiskCacheFolder()
{
CloseCacheFolder();
CloseCacheFolder();
}
bool DiskCacheFolder::ClearCache()
{
bool deleted_files = true;
bool deleted_files = true;
auto i = disk_data_.begin();
auto i = disk_data_.begin();
while (i != disk_data_.end()) {
// We return a false result if any of the files fail to delete, but still try to delete as many as we can
QString filename = i.key();
while (i != disk_data_.end()) {
// We return a false result if any of the files fail to delete, but still try to delete as many as we can
QString filename = i.key();
if (QFile::remove(filename) || !QFileInfo::exists(filename)) {
emit DeletedFrame(path_, filename);
i = disk_data_.erase(i);
} else {
qWarning() << "Failed to delete" << filename;
deleted_files = false;
i++;
}
}
if (QFile::remove(filename) || !QFileInfo::exists(filename)) {
emit DeletedFrame(path_, filename);
i = disk_data_.erase(i);
} else {
qWarning() << "Failed to delete" << filename;
deleted_files = false;
i++;
}
}
return deleted_files;
return deleted_files;
}
void DiskCacheFolder::Accessed(const QString &filename)
{
if (!disk_data_.contains(filename)) {
return;
}
if (!disk_data_.contains(filename)) {
return;
}
disk_data_[filename].access_time = QDateTime::currentMSecsSinceEpoch();
disk_data_[filename].access_time = QDateTime::currentMSecsSinceEpoch();
}
void DiskCacheFolder::CreatedFile(const QString &filename)
{
qint64 file_size = QFile(filename).size();
qint64 file_size = QFile(filename).size();
disk_data_.insert(filename, {file_size, QDateTime::currentMSecsSinceEpoch()});
disk_data_.insert(filename,
{ file_size, QDateTime::currentMSecsSinceEpoch() });
consumption_ += file_size;
consumption_ += file_size;
while (consumption_ > limit_) {
DeleteLeastRecent();
}
while (consumption_ > limit_) {
DeleteLeastRecent();
}
}
void DiskCacheFolder::SetPath(const QString &path)
{
// If this is currently set to a folder, close it out now
CloseCacheFolder();
// If this is currently set to a folder, close it out now
CloseCacheFolder();
// Signal that disk cache is gone
if (!disk_data_.empty()) {
for (auto it=disk_data_.cbegin(); it!=disk_data_.cend(); it++) {
emit DeletedFrame(path_, it.key());
}
disk_data_.clear();
}
// Signal that disk cache is gone
if (!disk_data_.empty()) {
for (auto it = disk_data_.cbegin(); it != disk_data_.cend(); it++) {
emit DeletedFrame(path_, it.key());
}
disk_data_.clear();
}
// Set defaults
clear_on_close_ = false;
consumption_ = 0;
limit_ = 21474836480; // Default to 20 GB
// Set defaults
clear_on_close_ = false;
consumption_ = 0;
limit_ = 21474836480; // Default to 20 GB
// Set path
path_ = path;
// Set path
path_ = path;
// Attempt to load existing index file from path
QDir path_dir(path_);
FileFunctions::DirectoryIsValid(path_dir);
// Attempt to load existing index file from path
QDir path_dir(path_);
FileFunctions::DirectoryIsValid(path_dir);
index_path_ = path_dir.filePath(QStringLiteral("index"));
index_path_ = path_dir.filePath(QStringLiteral("index"));
// Try to load any current cache index from file
QFile cache_index_file(index_path_);
// Try to load any current cache index from file
QFile cache_index_file(index_path_);
if (cache_index_file.open(QFile::ReadOnly)) {
QDataStream ds(&cache_index_file);
if (cache_index_file.open(QFile::ReadOnly)) {
QDataStream ds(&cache_index_file);
ds >> limit_;
ds >> clear_on_close_;
ds >> limit_;
ds >> clear_on_close_;
while (!cache_index_file.atEnd()) {
QString filename;
HashTime h;
while (!cache_index_file.atEnd()) {
QString filename;
HashTime h;
ds >> filename;
ds >> h.file_size;
ds >> h.access_time;
ds >> filename;
ds >> h.file_size;
ds >> h.access_time;
if (QFileInfo::exists(filename)) {
consumption_ += h.file_size;
disk_data_.insert(filename, h);
}
}
if (QFileInfo::exists(filename)) {
consumption_ += h.file_size;
disk_data_.insert(filename, h);
}
}
cache_index_file.close();
}
cache_index_file.close();
}
}
bool DiskCacheFolder::DeleteFileInternal(QMap<QString, HashTime>::iterator hash_to_delete)
bool DiskCacheFolder::DeleteFileInternal(
QMap<QString, HashTime>::iterator hash_to_delete)
{
// Cache HashTime object
QString filename = hash_to_delete.key();
HashTime ht = hash_to_delete.value();
// Cache HashTime object
QString filename = hash_to_delete.key();
HashTime ht = hash_to_delete.value();
// Remove from disk
QFile f(filename);
// Remove from disk
QFile f(filename);
if (!f.exists() || f.remove()) {
// Remove from internal map
disk_data_.erase(hash_to_delete);
if (!f.exists() || f.remove()) {
// Remove from internal map
disk_data_.erase(hash_to_delete);
// Reduce consumption
consumption_ -= ht.file_size;
// Reduce consumption
consumption_ -= ht.file_size;
emit DeletedFrame(path_, filename);
return true;
}
emit DeletedFrame(path_, filename);
return true;
}
return false;
return false;
}
bool DiskCacheFolder::DeleteSpecificFile(const QString &f)
{
for (auto it=disk_data_.begin(); it!=disk_data_.end(); it++) {
if (it.key() == f) {
// Break out of this loop, assuming we'll only have one instance of each filename
return DeleteFileInternal(it);
}
}
for (auto it = disk_data_.begin(); it != disk_data_.end(); it++) {
if (it.key() == f) {
// Break out of this loop, assuming we'll only have one instance of each filename
return DeleteFileInternal(it);
}
}
return false;
return false;
}
bool DiskCacheFolder::DeleteLeastRecent()
{
auto hash_to_delete = disk_data_.begin();
auto hash_to_delete = disk_data_.begin();
if (disk_data_.begin() != disk_data_.end()) {
for (auto it=disk_data_.begin()+1; it!=disk_data_.end(); it++) {
if (it->access_time < hash_to_delete->access_time) {
hash_to_delete = it;
}
}
if (disk_data_.begin() != disk_data_.end()) {
for (auto it = disk_data_.begin() + 1; it != disk_data_.end(); it++) {
if (it->access_time < hash_to_delete->access_time) {
hash_to_delete = it;
}
}
bool e = DeleteFileInternal(hash_to_delete);
bool e = DeleteFileInternal(hash_to_delete);
if (e) {
Core::instance()->WarnCacheFull();
}
if (e) {
Core::instance()->WarnCacheFull();
}
return e;
} else {
return false;
}
return e;
} else {
return false;
}
}
void DiskCacheFolder::CloseCacheFolder()
{
if (path_.isEmpty()) {
return;
}
if (path_.isEmpty()) {
return;
}
if (clear_on_close_) {
// If we're not moving to new and we're set to clear on close, clear now or else it'll never
// get cleared later
ClearCache();
}
if (clear_on_close_) {
// If we're not moving to new and we're set to clear on close, clear now or else it'll never
// get cleared later
ClearCache();
}
// Save current cache index
SaveDiskCacheIndex();
// Save current cache index
SaveDiskCacheIndex();
}
void DiskCacheFolder::SaveDiskCacheIndex()
{
QFile cache_index_file(index_path_);
QFile cache_index_file(index_path_);
if (cache_index_file.open(QFile::WriteOnly)) {
QDataStream ds(&cache_index_file);
if (cache_index_file.open(QFile::WriteOnly)) {
QDataStream ds(&cache_index_file);
ds << limit_;
ds << clear_on_close_;
ds << limit_;
ds << clear_on_close_;
for (auto it=disk_data_.cbegin(); it!=disk_data_.cend(); it++) {
const HashTime& ht = it.value();
for (auto it = disk_data_.cbegin(); it != disk_data_.cend(); it++) {
const HashTime &ht = it.value();
ds << it.key();
ds << ht.file_size;
ds << ht.access_time;
}
ds << it.key();
ds << ht.file_size;
ds << ht.access_time;
}
cache_index_file.close();
} else {
qWarning() << "Failed to write cache index:" << index_path_;
}
cache_index_file.close();
} else {
qWarning() << "Failed to write cache index:" << index_path_;
}
}
}
+81 -84
View File
@@ -29,145 +29,142 @@
#include "common/define.h"
#include "node/project.h"
namespace olive {
class DiskCacheFolder : public QObject
namespace olive
{
Q_OBJECT
class DiskCacheFolder : public QObject {
Q_OBJECT
public:
DiskCacheFolder(const QString& path, QObject* parent = nullptr);
DiskCacheFolder(const QString &path, QObject *parent = nullptr);
virtual ~DiskCacheFolder() override;
virtual ~DiskCacheFolder() override;
bool ClearCache();
bool ClearCache();
void Accessed(const QString& filename);
void Accessed(const QString &filename);
void CreatedFile(const QString& filename);
void CreatedFile(const QString &filename);
const QString& GetPath() const
{
return path_;
}
const QString &GetPath() const
{
return path_;
}
void SetPath(const QString& path);
void SetPath(const QString &path);
qint64 GetLimit() const
{
return limit_;
}
qint64 GetLimit() const
{
return limit_;
}
bool GetClearOnClose() const
{
return clear_on_close_;
}
bool GetClearOnClose() const
{
return clear_on_close_;
}
void SetLimit(qint64 l)
{
limit_ = l;
}
void SetLimit(qint64 l)
{
limit_ = l;
}
void SetClearOnClose(bool e)
{
clear_on_close_ = e;
}
void SetClearOnClose(bool e)
{
clear_on_close_ = e;
}
bool DeleteSpecificFile(const QString &f);
bool DeleteSpecificFile(const QString &f);
signals:
void DeletedFrame(const QString& path, const QString& filename);
void DeletedFrame(const QString &path, const QString &filename);
private:
struct HashTime {
qint64 file_size;
qint64 access_time;
};
struct HashTime {
qint64 file_size;
qint64 access_time;
};
bool DeleteFileInternal(QMap<QString, HashTime>::iterator hash_to_delete);
bool DeleteFileInternal(QMap<QString, HashTime>::iterator hash_to_delete);
bool DeleteLeastRecent();
bool DeleteLeastRecent();
void CloseCacheFolder();
void CloseCacheFolder();
QString path_;
QString path_;
QString index_path_;
QString index_path_;
QMap<QString, HashTime> disk_data_;
QMap<QString, HashTime> disk_data_;
qint64 consumption_;
qint64 consumption_;
qint64 limit_;
qint64 limit_;
bool clear_on_close_;
bool clear_on_close_;
QTimer save_timer_;
QTimer save_timer_;
private slots:
void SaveDiskCacheIndex();
void SaveDiskCacheIndex();
};
class DiskManager : public QObject
{
Q_OBJECT
class DiskManager : public QObject {
Q_OBJECT
public:
static void CreateInstance();
static void CreateInstance();
static void DestroyInstance();
static void DestroyInstance();
static DiskManager* instance();
static DiskManager *instance();
bool ClearDiskCache(const QString& cache_folder);
bool ClearDiskCache(const QString &cache_folder);
DiskCacheFolder* GetDefaultCacheFolder() const
{
// The first folder will always be the default
return open_folders_.first();
}
DiskCacheFolder *GetDefaultCacheFolder() const
{
// The first folder will always be the default
return open_folders_.first();
}
const QString& GetDefaultCachePath() const
{
return GetDefaultCacheFolder()->GetPath();
}
const QString &GetDefaultCachePath() const
{
return GetDefaultCacheFolder()->GetPath();
}
DiskCacheFolder* GetOpenFolder(const QString& path);
DiskCacheFolder *GetOpenFolder(const QString &path);
const QVector<DiskCacheFolder*>& GetOpenFolders() const
{
return open_folders_;
}
const QVector<DiskCacheFolder *> &GetOpenFolders() const
{
return open_folders_;
}
static bool ShowDiskCacheChangeConfirmationDialog(QWidget* parent);
static bool ShowDiskCacheChangeConfirmationDialog(QWidget *parent);
static QString GetDefaultDiskCacheConfigFile();
static QString GetDefaultDiskCacheConfigFile();
static QString GetDefaultDiskCachePath();
static QString GetDefaultDiskCachePath();
void ShowDiskCacheSettingsDialog(DiskCacheFolder* folder, QWidget* parent);
void ShowDiskCacheSettingsDialog(const QString& path, QWidget* parent);
void ShowDiskCacheSettingsDialog(DiskCacheFolder *folder, QWidget *parent);
void ShowDiskCacheSettingsDialog(const QString &path, QWidget *parent);
public slots:
void Accessed(const QString& cache_folder, const QString& filename);
void Accessed(const QString &cache_folder, const QString &filename);
void CreatedFile(const QString& cache_folder, const QString& filename);
void CreatedFile(const QString &cache_folder, const QString &filename);
void DeleteSpecificFile(const QString &filename);
void DeleteSpecificFile(const QString &filename);
signals:
void DeletedFrame(const QString& path, const QString& filename);
void DeletedFrame(const QString &path, const QString &filename);
void InvalidateProject(Project* p);
void InvalidateProject(Project *p);
private:
DiskManager();
DiskManager();
virtual ~DiskManager() override;
virtual ~DiskManager() override;
static DiskManager* instance_;
QVector<DiskCacheFolder*> open_folders_;
static DiskManager *instance_;
QVector<DiskCacheFolder *> open_folders_;
};
}
+282 -247
View File
@@ -35,393 +35,428 @@
#include "common/oiioutils.h"
#include "render/diskmanager.h"
namespace olive {
namespace olive
{
#define super PlaybackCache
FrameHashCache::FrameHashCache(QObject *parent) :
super(parent)
FrameHashCache::FrameHashCache(QObject *parent)
: super(parent)
{
if (DiskManager::instance()) {
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &FrameHashCache::HashDeleted);
connect(DiskManager::instance(), &DiskManager::InvalidateProject, this, &FrameHashCache::ProjectInvalidated);
}
if (DiskManager::instance()) {
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this,
&FrameHashCache::HashDeleted);
connect(DiskManager::instance(), &DiskManager::InvalidateProject, this,
&FrameHashCache::ProjectInvalidated);
}
}
void FrameHashCache::SetTimebase(const rational &tb)
{
timebase_ = tb;
timebase_ = tb;
}
void FrameHashCache::ValidateTimestamp(const int64_t &ts)
{
TimeRange frame_range(ToTime(ts), ToTime(ts+1));
Validate(frame_range);
TimeRange frame_range(ToTime(ts), ToTime(ts + 1));
Validate(frame_range);
}
void FrameHashCache::ValidateTime(const rational &time)
{
Validate(TimeRange(time, time + timebase_));
Validate(TimeRange(time, time + timebase_));
}
QString FrameHashCache::GetValidCacheFilename(const rational &time) const
{
if (IsFrameCached(time)) {
return CachePathName(time);
} else if (!GetPassthroughs().empty()) {
for (const Passthrough &p : GetPassthroughs()) {
if (p.Contains(time)) {
return CachePathName(GetCacheDirectory(), p.cache, time, timebase_);
}
}
}
if (IsFrameCached(time)) {
return CachePathName(time);
} else if (!GetPassthroughs().empty()) {
for (const Passthrough &p : GetPassthroughs()) {
if (p.Contains(time)) {
return CachePathName(GetCacheDirectory(), p.cache, time,
timebase_);
}
}
}
return QString();
return QString();
}
bool FrameHashCache::SaveCacheFrame(const int64_t &time, FramePtr frame) const
{
return SaveCacheFrame(GetCacheDirectory(), GetUuid(), time, frame);
return SaveCacheFrame(GetCacheDirectory(), GetUuid(), time, frame);
}
bool FrameHashCache::SaveCacheFrame(const QString &cache_path, const QUuid &uuid, const int64_t &time, FramePtr frame)
bool FrameHashCache::SaveCacheFrame(const QString &cache_path,
const QUuid &uuid, const int64_t &time,
FramePtr frame)
{
if (cache_path.isEmpty()) {
qWarning() << "Failed to save cache frame with empty path";
return false;
}
if (cache_path.isEmpty()) {
qWarning() << "Failed to save cache frame with empty path";
return false;
}
QString fn = CachePathName(cache_path, uuid, time);
QString fn = CachePathName(cache_path, uuid, time);
bool ret = SaveCacheFrame(fn, frame);
bool ret = SaveCacheFrame(fn, frame);
// Register frame with the disk manager
if (ret) {
QMetaObject::invokeMethod(DiskManager::instance(), "CreatedFile", Q_ARG(QString, cache_path), Q_ARG(QString, fn));
}
// Register frame with the disk manager
if (ret) {
QMetaObject::invokeMethod(DiskManager::instance(), "CreatedFile",
Q_ARG(QString, cache_path),
Q_ARG(QString, fn));
}
return ret;
return ret;
}
bool FrameHashCache::SaveCacheFrame(const QString &cache_path, const QUuid &uuid, const rational &time, const rational &tb, FramePtr frame)
bool FrameHashCache::SaveCacheFrame(const QString &cache_path,
const QUuid &uuid, const rational &time,
const rational &tb, FramePtr frame)
{
if (cache_path.isEmpty()) {
qWarning() << "Failed to save cache frame with empty path";
return false;
}
if (cache_path.isEmpty()) {
qWarning() << "Failed to save cache frame with empty path";
return false;
}
QString fn = CachePathName(cache_path, uuid, time, tb);
QString fn = CachePathName(cache_path, uuid, time, tb);
bool ret = SaveCacheFrame(fn, frame);
bool ret = SaveCacheFrame(fn, frame);
// Register frame with the disk manager
if (ret) {
QMetaObject::invokeMethod(DiskManager::instance(), "CreatedFile", Q_ARG(QString, cache_path), Q_ARG(QString, fn));
}
// Register frame with the disk manager
if (ret) {
QMetaObject::invokeMethod(DiskManager::instance(), "CreatedFile",
Q_ARG(QString, cache_path),
Q_ARG(QString, fn));
}
return ret;
return ret;
}
FramePtr FrameHashCache::LoadCacheFrame(const QString &cache_path, const QUuid &uuid, const int64_t &time)
FramePtr FrameHashCache::LoadCacheFrame(const QString &cache_path,
const QUuid &uuid, const int64_t &time)
{
// Minor optimization, we store frames currently being saved just in case something tries to load
// while we're saving. This should *occasionally* optimize and also prevent scenarios where
// we try to load a frame that's half way through being saved.
QString filename = CachePathName(cache_path, uuid, time);
// Minor optimization, we store frames currently being saved just in case something tries to load
// while we're saving. This should *occasionally* optimize and also prevent scenarios where
// we try to load a frame that's half way through being saved.
QString filename = CachePathName(cache_path, uuid, time);
if (cache_path.isEmpty()) {
qWarning() << "Failed to load cache frame with empty path";
return nullptr;
}
if (cache_path.isEmpty()) {
qWarning() << "Failed to load cache frame with empty path";
return nullptr;
}
return LoadCacheFrame(filename);
return LoadCacheFrame(filename);
}
FramePtr FrameHashCache::LoadCacheFrame(const int64_t &hash) const
{
return LoadCacheFrame(GetCacheDirectory(), GetUuid(), hash);
return LoadCacheFrame(GetCacheDirectory(), GetUuid(), hash);
}
FramePtr FrameHashCache::LoadCacheFrame(const QString &fn)
{
FramePtr frame = nullptr;
FramePtr frame = nullptr;
if (!fn.isEmpty() && QFileInfo::exists(fn)) {
try {
Imf::InputFile file(fn.toUtf8(), 0);
if (!fn.isEmpty() && QFileInfo::exists(fn)) {
try {
Imf::InputFile file(fn.toUtf8(), 0);
Imath::Box2i dw = file.header().dataWindow();
Imf::PixelType pix_type = file.header().channels().begin().channel().type;
int width = dw.max.x - dw.min.x + 1;
int height = dw.max.y - dw.min.y + 1;
bool has_alpha = file.header().channels().findChannel("A");
Imath::Box2i dw = file.header().dataWindow();
Imf::PixelType pix_type =
file.header().channels().begin().channel().type;
int width = dw.max.x - dw.min.x + 1;
int height = dw.max.y - dw.min.y + 1;
bool has_alpha = file.header().channels().findChannel("A");
int div = qMax(1, static_cast<const Imf::IntAttribute&>(file.header()["oliveDivider"]).value());
int div = qMax(1, static_cast<const Imf::IntAttribute &>(
file.header()["oliveDivider"])
.value());
PixelFormat image_format;
if (pix_type == Imf::HALF) {
image_format = PixelFormat::F16;
} else {
image_format = PixelFormat::F32;
}
PixelFormat image_format;
if (pix_type == Imf::HALF) {
image_format = PixelFormat::F16;
} else {
image_format = PixelFormat::F32;
}
int channel_count = has_alpha ? VideoParams::kRGBAChannelCount : VideoParams::kRGBChannelCount;
int channel_count = has_alpha ? VideoParams::kRGBAChannelCount :
VideoParams::kRGBChannelCount;
frame = Frame::Create();
frame->set_video_params(VideoParams(width * div,
height * div,
image_format,
channel_count,
rational::fromDouble(file.header().pixelAspectRatio()),
VideoParams::kInterlaceNone,
div));
frame = Frame::Create();
frame->set_video_params(VideoParams(
width * div, height * div, image_format, channel_count,
rational::fromDouble(file.header().pixelAspectRatio()),
VideoParams::kInterlaceNone, div));
frame->allocate();
frame->allocate();
int bpc = VideoParams::GetBytesPerChannel(image_format);
int bpc = VideoParams::GetBytesPerChannel(image_format);
size_t xs = channel_count * bpc;
size_t ys = frame->linesize_bytes();
size_t xs = channel_count * bpc;
size_t ys = frame->linesize_bytes();
Imf::FrameBuffer framebuffer;
framebuffer.insert("R", Imf::Slice(pix_type, frame->data(), xs, ys));
framebuffer.insert("G", Imf::Slice(pix_type, frame->data() + bpc, xs, ys));
framebuffer.insert("B", Imf::Slice(pix_type, frame->data() + 2*bpc, xs, ys));
if (has_alpha) {
framebuffer.insert("A", Imf::Slice(pix_type, frame->data() + 3*bpc, xs, ys));
}
Imf::FrameBuffer framebuffer;
framebuffer.insert("R",
Imf::Slice(pix_type, frame->data(), xs, ys));
framebuffer.insert("G", Imf::Slice(pix_type, frame->data() + bpc,
xs, ys));
framebuffer.insert(
"B", Imf::Slice(pix_type, frame->data() + 2 * bpc, xs, ys));
if (has_alpha) {
framebuffer.insert(
"A", Imf::Slice(pix_type, frame->data() + 3 * bpc, xs, ys));
}
file.setFrameBuffer(framebuffer);
file.setFrameBuffer(framebuffer);
file.readPixels(dw.min.y, dw.max.y);
} catch (const std::exception &e) {
// Not an EXR, maybe it's a JPEG?
QImage img;
file.readPixels(dw.min.y, dw.max.y);
} catch (const std::exception &e) {
// Not an EXR, maybe it's a JPEG?
QImage img;
if (img.load(fn, "jpg")) {
if (img.load(fn, "jpg")) {
// FIXME: Hardcoded
const int div = 1;
const PixelFormat image_format = PixelFormat::U8;
const int channel_count = 4;
const rational par(1, 1);
// FIXME: Hardcoded
const int div = 1;
const PixelFormat image_format = PixelFormat::U8;
const int channel_count = 4;
const rational par(1, 1);
// Convert to frame (FIXME: might be slow? may be a better way to do this on the GPU)
img.convertTo(QImage::Format_RGBA8888_Premultiplied);
// Convert to frame (FIXME: might be slow? may be a better way to do this on the GPU)
img.convertTo(QImage::Format_RGBA8888_Premultiplied);
frame = Frame::Create();
frame->set_video_params(VideoParams(
img.width() * div, img.height() * div, image_format,
channel_count, par, VideoParams::kInterlaceNone, div));
frame = Frame::Create();
frame->set_video_params(VideoParams(img.width() * div,
img.height() * div,
image_format,
channel_count,
par,
VideoParams::kInterlaceNone,
div));
frame->allocate();
frame->allocate();
for (int i = 0; i < img.height(); i++) {
memcpy(frame->data() + frame->linesize_bytes() * i,
img.bits() + img.bytesPerLine() * i,
frame->width() *
frame->video_params().GetBytesPerPixel());
}
for (int i=0; i<img.height(); i++) {
memcpy(frame->data() + frame->linesize_bytes() * i,
img.bits() + img.bytesPerLine() * i,
frame->width() * frame->video_params().GetBytesPerPixel());
}
} else {
qCritical() << "Failed to read cache frame:" << e.what();
} else {
qCritical() << "Failed to read cache frame:" << e.what();
// Clear frame to signal that nothing was loaded
frame = nullptr;
// Clear frame to signal that nothing was loaded
frame = nullptr;
// Assume this frame is corrupt in some way and delete it
QMetaObject::invokeMethod(DiskManager::instance(),
"DeleteSpecificFile",
Q_ARG(QString, fn));
}
}
}
// Assume this frame is corrupt in some way and delete it
QMetaObject::invokeMethod(DiskManager::instance(), "DeleteSpecificFile", Q_ARG(QString, fn));
}
}
}
return frame;
return frame;
}
void FrameHashCache::SetPassthrough(PlaybackCache *cache)
{
super::SetPassthrough(cache);
SetTimebase(static_cast<FrameHashCache*>(cache)->GetTimebase());
super::SetPassthrough(cache);
SetTimebase(static_cast<FrameHashCache *>(cache)->GetTimebase());
}
void FrameHashCache::LoadStateEvent(QDataStream &stream)
{
uint32_t version;
int num, den;
uint32_t version;
int num, den;
stream >> version;
stream >> version;
switch (version) {
case 1:
stream >> num;
stream >> den;
timebase_ = rational(num, den);
break;
}
switch (version) {
case 1:
stream >> num;
stream >> den;
timebase_ = rational(num, den);
break;
}
}
void FrameHashCache::SaveStateEvent(QDataStream &stream)
{
uint32_t version = 1;
uint32_t version = 1;
stream << version;
stream << version;
stream << timebase_.numerator();
stream << timebase_.denominator();
stream << timebase_.numerator();
stream << timebase_.denominator();
}
rational FrameHashCache::ToTime(const int64_t &ts) const
{
return Timecode::timestamp_to_time(ts, timebase_);
return Timecode::timestamp_to_time(ts, timebase_);
}
int64_t FrameHashCache::ToTimestamp(const rational &ts, Timecode::Rounding rounding) const
int64_t FrameHashCache::ToTimestamp(const rational &ts,
Timecode::Rounding rounding) const
{
return Timecode::time_to_timestamp(ts, timebase_, rounding);
return Timecode::time_to_timestamp(ts, timebase_, rounding);
}
void FrameHashCache::HashDeleted(const QString& path, const QString &filename)
void FrameHashCache::HashDeleted(const QString &path, const QString &filename)
{
QString cache_dir = GetCacheDirectory();
if (cache_dir.isEmpty() || path != cache_dir) {
return;
}
QString cache_dir = GetCacheDirectory();
if (cache_dir.isEmpty() || path != cache_dir) {
return;
}
QFileInfo info(filename);
if (GetUuid().toString() != info.dir().dirName()) {
return;
}
QFileInfo info(filename);
if (GetUuid().toString() != info.dir().dirName()) {
return;
}
int64_t timestamp = info.fileName().toLongLong();
Invalidate(TimeRange(ToTime(timestamp), ToTime(timestamp + 1)));
int64_t timestamp = info.fileName().toLongLong();
Invalidate(TimeRange(ToTime(timestamp), ToTime(timestamp + 1)));
}
void FrameHashCache::ProjectInvalidated(Project *p)
{
if (GetProject() == p) {
InvalidateAll();
}
if (GetProject() == p) {
InvalidateAll();
}
}
QString FrameHashCache::CachePathName(const int64_t &time) const
{
return CachePathName(GetCacheDirectory(), GetUuid(), time);
return CachePathName(GetCacheDirectory(), GetUuid(), time);
}
QString FrameHashCache::CachePathName(const rational &time) const
{
return CachePathName(GetCacheDirectory(), GetUuid(), time, timebase_);
return CachePathName(GetCacheDirectory(), GetUuid(), time, timebase_);
}
QString FrameHashCache::CachePathName(const QString &cache_path, const QUuid &cache_id, const int64_t &time)
QString FrameHashCache::CachePathName(const QString &cache_path,
const QUuid &cache_id,
const int64_t &time)
{
QString filename = GetThisCacheDirectory(cache_path, cache_id).filePath(QString::number(time));
QString filename = GetThisCacheDirectory(cache_path, cache_id)
.filePath(QString::number(time));
// Register that in some way this hash has been accessed
if (DiskManager::instance()) {
QMetaObject::invokeMethod(DiskManager::instance(), "Accessed", Q_ARG(QString, cache_path), Q_ARG(QString, filename));
}
// Register that in some way this hash has been accessed
if (DiskManager::instance()) {
QMetaObject::invokeMethod(DiskManager::instance(), "Accessed",
Q_ARG(QString, cache_path),
Q_ARG(QString, filename));
}
return filename;
return filename;
}
QString FrameHashCache::CachePathName(const QString &cache_path, const QUuid &cache_id, const rational &time, const rational &tb)
QString FrameHashCache::CachePathName(const QString &cache_path,
const QUuid &cache_id,
const rational &time, const rational &tb)
{
return CachePathName(cache_path, cache_id, Timecode::time_to_timestamp(time, tb, Timecode::kRound));
return CachePathName(cache_path, cache_id,
Timecode::time_to_timestamp(time, tb,
Timecode::kRound));
}
bool FrameHashCache::SaveCacheFrame(const QString &filename, const FramePtr frame)
bool FrameHashCache::SaveCacheFrame(const QString &filename,
const FramePtr frame)
{
// Ensure directory is created
QDir cache_dir = QFileInfo(filename).dir();
if (!FileFunctions::DirectoryIsValid(cache_dir)) {
return false;
}
// Ensure directory is created
QDir cache_dir = QFileInfo(filename).dir();
if (!FileFunctions::DirectoryIsValid(cache_dir)) {
return false;
}
if (VideoParams::FormatIsFloat(frame->format())) {
// Floating point types are stored in EXR
Imf::PixelType pix_type;
if (VideoParams::FormatIsFloat(frame->format())) {
// Floating point types are stored in EXR
Imf::PixelType pix_type;
if (frame->format() == PixelFormat::F16) {
pix_type = Imf::HALF;
} else {
pix_type = Imf::FLOAT;
}
if (frame->format() == PixelFormat::F16) {
pix_type = Imf::HALF;
} else {
pix_type = Imf::FLOAT;
}
Imf::Header header(frame->width(), frame->height());
header.channels().insert("R", Imf::Channel(pix_type));
header.channels().insert("G", Imf::Channel(pix_type));
header.channels().insert("B", Imf::Channel(pix_type));
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
header.channels().insert("A", Imf::Channel(pix_type));
}
Imf::Header header(frame->width(), frame->height());
header.channels().insert("R", Imf::Channel(pix_type));
header.channels().insert("G", Imf::Channel(pix_type));
header.channels().insert("B", Imf::Channel(pix_type));
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
header.channels().insert("A", Imf::Channel(pix_type));
}
header.compression() = Imf::DWAA_COMPRESSION;
header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f));
header.pixelAspectRatio() = frame->video_params().pixel_aspect_ratio().toDouble();
header.compression() = Imf::DWAA_COMPRESSION;
header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f));
header.pixelAspectRatio() =
frame->video_params().pixel_aspect_ratio().toDouble();
header.insert("oliveDivider", Imf::IntAttribute(frame->video_params().divider()));
header.insert("oliveDivider",
Imf::IntAttribute(frame->video_params().divider()));
try {
Imf::OutputFile out(filename.toUtf8(), header, 0);
try {
Imf::OutputFile out(filename.toUtf8(), header, 0);
int bpc = VideoParams::GetBytesPerChannel(frame->format());
int bpc = VideoParams::GetBytesPerChannel(frame->format());
size_t xs = frame->channel_count() * bpc;
size_t ys = frame->linesize_bytes();
size_t xs = frame->channel_count() * bpc;
size_t ys = frame->linesize_bytes();
Imf::FrameBuffer framebuffer;
framebuffer.insert("R", Imf::Slice(pix_type, frame->data(), xs, ys));
framebuffer.insert("G", Imf::Slice(pix_type, frame->data() + bpc, xs, ys));
framebuffer.insert("B", Imf::Slice(pix_type, frame->data() + 2*bpc, xs, ys));
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
framebuffer.insert("A", Imf::Slice(pix_type, frame->data() + 3*bpc, xs, ys));
}
out.setFrameBuffer(framebuffer);
Imf::FrameBuffer framebuffer;
framebuffer.insert("R",
Imf::Slice(pix_type, frame->data(), xs, ys));
framebuffer.insert("G", Imf::Slice(pix_type, frame->data() + bpc,
xs, ys));
framebuffer.insert(
"B", Imf::Slice(pix_type, frame->data() + 2 * bpc, xs, ys));
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
framebuffer.insert(
"A", Imf::Slice(pix_type, frame->data() + 3 * bpc, xs, ys));
}
out.setFrameBuffer(framebuffer);
out.writePixels(frame->height());
out.writePixels(frame->height());
return true;
} catch (const std::exception &e) {
qCritical() << "Failed to write cache frame:" << e.what();
return true;
} catch (const std::exception &e) {
qCritical() << "Failed to write cache frame:" << e.what();
return false;
}
} else {
QImage::Format fmt = QImage::Format_Invalid;
return false;
}
} else {
QImage::Format fmt = QImage::Format_Invalid;
switch (frame->format()) {
case PixelFormat::U8:
if (frame->channel_count() == VideoParams::kRGBAChannelCount){
fmt = QImage::Format_RGBA8888_Premultiplied;
} else if (frame->channel_count() == VideoParams::kRGBChannelCount){
fmt = QImage::Format_RGB888;
}
break;
case PixelFormat::U16:
if (frame->channel_count() == VideoParams::kRGBAChannelCount){
fmt = QImage::Format_RGBA64_Premultiplied;
}
break;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::COUNT:
case PixelFormat::INVALID:
break;
}
switch (frame->format()) {
case PixelFormat::U8:
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
fmt = QImage::Format_RGBA8888_Premultiplied;
} else if (frame->channel_count() ==
VideoParams::kRGBChannelCount) {
fmt = QImage::Format_RGB888;
}
break;
case PixelFormat::U16:
if (frame->channel_count() == VideoParams::kRGBAChannelCount) {
fmt = QImage::Format_RGBA64_Premultiplied;
}
break;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::COUNT:
case PixelFormat::INVALID:
break;
}
if (fmt == QImage::Format_Invalid) {
return false;
}
if (fmt == QImage::Format_Invalid) {
return false;
}
QImage img(reinterpret_cast<const uchar*>(frame->data()), frame->width(), frame->height(), frame->linesize_bytes(), fmt);
QImage img(reinterpret_cast<const uchar *>(frame->data()),
frame->width(), frame->height(), frame->linesize_bytes(),
fmt);
return img.save(filename, "jpg");
}
return img.save(filename, "jpg");
}
}
}
+52 -43
View File
@@ -25,73 +25,82 @@
#include "render/playbackcache.h"
#include "render/videoparams.h"
namespace olive {
class FrameHashCache : public PlaybackCache
namespace olive
{
Q_OBJECT
class FrameHashCache : public PlaybackCache {
Q_OBJECT
public:
FrameHashCache(QObject* parent = nullptr);
FrameHashCache(QObject *parent = nullptr);
const rational &GetTimebase() const { return timebase_; }
const rational &GetTimebase() const
{
return timebase_;
}
void SetTimebase(const rational& tb);
void SetTimebase(const rational &tb);
void ValidateTimestamp(const int64_t &ts);
void ValidateTime(const rational &time);
void ValidateTimestamp(const int64_t &ts);
void ValidateTime(const rational &time);
bool IsFrameCached(const rational &time) const
{
return GetValidatedRanges().contains(time);
}
bool IsFrameCached(const rational &time) const
{
return GetValidatedRanges().contains(time);
}
QString GetValidCacheFilename(const rational &time) const;
QString GetValidCacheFilename(const rational &time) const;
static bool SaveCacheFrame(const QString& filename, FramePtr frame);
bool SaveCacheFrame(const int64_t &time, FramePtr frame) const;
static bool SaveCacheFrame(const QString& cache_path, const QUuid &uuid, const int64_t &time, FramePtr frame);
static bool SaveCacheFrame(const QString& cache_path, const QUuid &uuid, const rational &time, const rational &tb, FramePtr frame);
static FramePtr LoadCacheFrame(const QString& cache_path, const QUuid &uuid, const int64_t &time);
FramePtr LoadCacheFrame(const int64_t &time) const;
static FramePtr LoadCacheFrame(const QString& fn);
static bool SaveCacheFrame(const QString &filename, FramePtr frame);
bool SaveCacheFrame(const int64_t &time, FramePtr frame) const;
static bool SaveCacheFrame(const QString &cache_path, const QUuid &uuid,
const int64_t &time, FramePtr frame);
static bool SaveCacheFrame(const QString &cache_path, const QUuid &uuid,
const rational &time, const rational &tb,
FramePtr frame);
static FramePtr LoadCacheFrame(const QString &cache_path, const QUuid &uuid,
const int64_t &time);
FramePtr LoadCacheFrame(const int64_t &time) const;
static FramePtr LoadCacheFrame(const QString &fn);
virtual void SetPassthrough(PlaybackCache *cache) override;
virtual void SetPassthrough(PlaybackCache *cache) override;
protected:
virtual void LoadStateEvent(QDataStream &stream) override;
virtual void SaveStateEvent(QDataStream &stream) override;
virtual void LoadStateEvent(QDataStream &stream) override;
virtual void SaveStateEvent(QDataStream &stream) override;
private:
rational ToTime(const int64_t &ts) const;
int64_t ToTimestamp(const rational &ts, Timecode::Rounding rounding = Timecode::kRound) const;
rational ToTime(const int64_t &ts) const;
int64_t ToTimestamp(const rational &ts,
Timecode::Rounding rounding = Timecode::kRound) const;
/**
/**
* @brief Return the path of the cached image at this time
*/
QString CachePathName(const int64_t &time) const;
QString CachePathName(const rational &time) const;
QString CachePathName(const int64_t &time) const;
QString CachePathName(const rational &time) const;
static QString CachePathName(const QString& cache_path, const QUuid &cache_id, const int64_t &time);
static QString CachePathName(const QString& cache_path, const QUuid &cache_id, const rational &time, const rational &tb);
static QString CachePathName(const QString &cache_path,
const QUuid &cache_id, const int64_t &time);
static QString CachePathName(const QString &cache_path,
const QUuid &cache_id, const rational &time,
const rational &tb);
rational timebase_;
rational timebase_;
private slots:
void HashDeleted(const QString &path, const QString &filename);
void ProjectInvalidated(Project* p);
void HashDeleted(const QString &path, const QString &filename);
void ProjectInvalidated(Project *p);
};
class ThumbnailCache : public FrameHashCache
{
Q_OBJECT
class ThumbnailCache : public FrameHashCache {
Q_OBJECT
public:
ThumbnailCache(QObject* parent = nullptr) :
FrameHashCache(parent)
{
SetTimebase(rational(1, 10));
}
ThumbnailCache(QObject *parent = nullptr)
: FrameHashCache(parent)
{
SetTimebase(rational(1, 10));
}
};
}
+52 -50
View File
@@ -23,107 +23,109 @@
#include <QDateTime>
#include <QDebug>
namespace olive {
namespace olive
{
FrameManager* FrameManager::instance_ = nullptr;
FrameManager *FrameManager::instance_ = nullptr;
const int FrameManager::kFrameLifetime = 5000;
void FrameManager::CreateInstance()
{
instance_ = new FrameManager();
instance_ = new FrameManager();
}
void FrameManager::DestroyInstance()
{
delete instance_;
instance_ = nullptr;
delete instance_;
instance_ = nullptr;
}
FrameManager *FrameManager::instance()
{
return instance_;
return instance_;
}
char *FrameManager::Allocate(int size)
{
if (instance()) {
return instance()->AllocateFromPool(size);
} else {
return new char[size];
}
if (instance()) {
return instance()->AllocateFromPool(size);
} else {
return new char[size];
}
}
void FrameManager::Deallocate(int size, char *buffer)
{
if (instance()) {
instance()->DeallocateToPool(size, buffer);
} else {
delete [] buffer;
}
if (instance()) {
instance()->DeallocateToPool(size, buffer);
} else {
delete[] buffer;
}
}
FrameManager::FrameManager()
{
clear_timer_.setInterval(kFrameLifetime);
connect(&clear_timer_, &QTimer::timeout, this, &FrameManager::GarbageCollection);
clear_timer_.start();
clear_timer_.setInterval(kFrameLifetime);
connect(&clear_timer_, &QTimer::timeout, this,
&FrameManager::GarbageCollection);
clear_timer_.start();
}
char *FrameManager::AllocateFromPool(int size)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
std::list<Buffer>& buffer_list = pool_[size];
char* buf = nullptr;
std::list<Buffer> &buffer_list = pool_[size];
char *buf = nullptr;
if (buffer_list.empty()) {
buf = new char[size];
} else {
// Take this buffer from the list
buf = buffer_list.front().data;
buffer_list.pop_front();
}
if (buffer_list.empty()) {
buf = new char[size];
} else {
// Take this buffer from the list
buf = buffer_list.front().data;
buffer_list.pop_front();
}
return buf;
return buf;
}
void FrameManager::DeallocateToPool(int size, char *buffer)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
std::list<Buffer>& buffer_list = pool_[size];
std::list<Buffer> &buffer_list = pool_[size];
buffer_list.push_back({QDateTime::currentMSecsSinceEpoch(), buffer});
buffer_list.push_back({ QDateTime::currentMSecsSinceEpoch(), buffer });
}
void FrameManager::GarbageCollection()
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
qint64 min_life = QDateTime::currentMSecsSinceEpoch() - kFrameLifetime;
qint64 min_life = QDateTime::currentMSecsSinceEpoch() - kFrameLifetime;
for (auto it=pool_.begin(); it!=pool_.end(); it++) {
std::list<Buffer>& list = it->second;
for (auto it = pool_.begin(); it != pool_.end(); it++) {
std::list<Buffer> &list = it->second;
while (list.size() > 0 && list.front().time < min_life) {
delete [] list.front().data;
list.pop_front();
}
}
while (list.size() > 0 && list.front().time < min_life) {
delete[] list.front().data;
list.pop_front();
}
}
}
FrameManager::~FrameManager()
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
for (auto it=pool_.begin(); it!=pool_.end(); it++) {
std::list<Buffer>& list = it->second;
for (auto jt=list.begin(); jt!=list.end(); jt++) {
delete [] (*jt).data;
}
}
for (auto it = pool_.begin(); it != pool_.end(); it++) {
std::list<Buffer> &list = it->second;
for (auto jt = list.begin(); jt != list.end(); jt++) {
delete[] (*jt).data;
}
}
pool_.clear();
pool_.clear();
}
}
+25 -27
View File
@@ -25,28 +25,28 @@
#include <QObject>
#include <QTimer>
namespace olive {
class FrameManager : public QObject
namespace olive
{
Q_OBJECT
class FrameManager : public QObject {
Q_OBJECT
public:
static void CreateInstance();
static void CreateInstance();
static void DestroyInstance();
static void DestroyInstance();
static FrameManager* instance();
static FrameManager *instance();
static char* Allocate(int size);
static char *Allocate(int size);
static void Deallocate(int size, char* buffer);
static void Deallocate(int size, char *buffer);
private:
FrameManager();
FrameManager();
virtual ~FrameManager() override;
virtual ~FrameManager() override;
/**
/**
* @brief Allocate buffer
*
* Caller takes ownership of buffer and can delete it if they want. It can also be returned to
@@ -54,9 +54,9 @@ private:
*
* Thread-safe.
*/
char* AllocateFromPool(int size);
char *AllocateFromPool(int size);
/**
/**
* @brief Deallocate buffer
*
* Manager will take ownership and buffer will stay allocated for some time in case it can be
@@ -64,27 +64,25 @@ private:
*
* Thread-safe.
*/
void DeallocateToPool(int size, char* buffer);
void DeallocateToPool(int size, char *buffer);
static FrameManager* instance_;
static FrameManager *instance_;
static const int kFrameLifetime;
static const int kFrameLifetime;
struct Buffer
{
qint64 time;
char* data;
};
struct Buffer {
qint64 time;
char *data;
};
std::map< int, std::list<Buffer> > pool_;
std::map<int, std::list<Buffer>> pool_;
QMutex mutex_;
QMutex mutex_;
QTimer clear_timer_;
QTimer clear_timer_;
private slots:
void GarbageCollection();
void GarbageCollection();
};
}
+2 -1
View File
@@ -20,6 +20,7 @@
#include "acceleratedjob.h"
namespace olive {
namespace olive
{
}
+35 -28
View File
@@ -24,47 +24,54 @@
#include "node/param.h"
#include "node/valuedatabase.h"
namespace olive {
class AcceleratedJob
namespace olive
{
class AcceleratedJob {
public:
AcceleratedJob() = default;
AcceleratedJob() = default;
virtual ~AcceleratedJob(){}
virtual ~AcceleratedJob()
{
}
NodeValue Get(const QString& input) const
{
return value_map_.value(input);
}
NodeValue Get(const QString &input) const
{
return value_map_.value(input);
}
void Insert(const QString &input, const NodeValueRow &row)
{
value_map_.insert(input, row.value(input));
}
void Insert(const QString &input, const NodeValueRow &row)
{
value_map_.insert(input, row.value(input));
}
void Insert(const QString& input, const NodeValue& value)
{
value_map_.insert(input, value);
}
void Insert(const QString &input, const NodeValue &value)
{
value_map_.insert(input, value);
}
void Insert(const NodeValueRow &row)
{
void Insert(const NodeValueRow &row)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
value_map_.insert(row);
value_map_.insert(row);
#else
for (auto it=row.cbegin(); it!=row.cend(); it++) {
value_map_.insert(it.key(), it.value());
}
for (auto it = row.cbegin(); it != row.cend(); it++) {
value_map_.insert(it.key(), it.value());
}
#endif
}
}
const NodeValueRow &GetValues() const { return value_map_; }
NodeValueRow &GetValues() { return value_map_; }
const NodeValueRow &GetValues() const
{
return value_map_;
}
NodeValueRow &GetValues()
{
return value_map_;
}
private:
NodeValueRow value_map_;
NodeValueRow value_map_;
};
}
+26 -15
View File
@@ -27,28 +27,39 @@
#include "node/value.h"
#include "render/job/acceleratedjob.h"
namespace olive {
class CacheJob : public AcceleratedJob
namespace olive
{
class CacheJob : public AcceleratedJob {
public:
CacheJob() = default;
CacheJob(const QString &filename, const NodeValue &fallback = NodeValue())
{
filename_ = filename;
}
CacheJob() = default;
CacheJob(const QString &filename, const NodeValue &fallback = NodeValue())
{
filename_ = filename;
}
const QString &GetFilename() const { return filename_; }
void SetFilename(const QString &s) { filename_ = s; }
const QString &GetFilename() const
{
return filename_;
}
void SetFilename(const QString &s)
{
filename_ = s;
}
const NodeValue &GetFallback() const { return fallback_; }
void SetFallback(const NodeValue &val) { fallback_ = val; }
const NodeValue &GetFallback() const
{
return fallback_;
}
void SetFallback(const NodeValue &val)
{
fallback_ = val;
}
private:
QString filename_;
NodeValue fallback_;
QString filename_;
NodeValue fallback_;
};
}
+121 -65
View File
@@ -29,97 +29,153 @@
#include "render/colorprocessor.h"
#include "render/texture.h"
namespace olive {
namespace olive
{
class Node;
class ColorTransformJob : public AcceleratedJob
{
class ColorTransformJob : public AcceleratedJob {
public:
ColorTransformJob()
{
processor_ = nullptr;
custom_shader_src_ = nullptr;
input_alpha_association_ = kAlphaNone;
clear_destination_ = true;
force_opaque_ = false;
}
ColorTransformJob()
{
processor_ = nullptr;
custom_shader_src_ = nullptr;
input_alpha_association_ = kAlphaNone;
clear_destination_ = true;
force_opaque_ = false;
}
ColorTransformJob(const NodeValueRow &row) :
ColorTransformJob()
{
Insert(row);
}
ColorTransformJob(const NodeValueRow &row)
: ColorTransformJob()
{
Insert(row);
}
QString id() const
{
if (id_.isEmpty()) {
return processor_->id();
} else {
return id_;
}
}
QString id() const
{
if (id_.isEmpty()) {
return processor_->id();
} else {
return id_;
}
}
void SetOverrideID(const QString &id) { id_ = id; }
void SetOverrideID(const QString &id)
{
id_ = id;
}
const NodeValue &GetInputTexture() const { return input_texture_; }
void SetInputTexture(const NodeValue &tex) { input_texture_ = tex; }
void SetInputTexture(TexturePtr tex)
{
Q_ASSERT(!tex->IsDummy());
input_texture_ = NodeValue(NodeValue::kTexture, tex);
}
const NodeValue &GetInputTexture() const
{
return input_texture_;
}
void SetInputTexture(const NodeValue &tex)
{
input_texture_ = tex;
}
void SetInputTexture(TexturePtr tex)
{
Q_ASSERT(!tex->IsDummy());
input_texture_ = NodeValue(NodeValue::kTexture, tex);
}
ColorProcessorPtr GetColorProcessor() const { return processor_; }
void SetColorProcessor(ColorProcessorPtr p) { processor_ = p; }
ColorProcessorPtr GetColorProcessor() const
{
return processor_;
}
void SetColorProcessor(ColorProcessorPtr p)
{
processor_ = p;
}
const AlphaAssociated &GetInputAlphaAssociation() const { return input_alpha_association_; }
void SetInputAlphaAssociation(const AlphaAssociated &e) { input_alpha_association_ = e; }
const AlphaAssociated &GetInputAlphaAssociation() const
{
return input_alpha_association_;
}
void SetInputAlphaAssociation(const AlphaAssociated &e)
{
input_alpha_association_ = e;
}
const Node *CustomShaderSource() const { return custom_shader_src_; }
const QString &CustomShaderID() const { return custom_shader_id_; }
void SetNeedsCustomShader(const Node *node, const QString &id = QString())
{
custom_shader_src_ = node;
custom_shader_id_ = id;
}
const Node *CustomShaderSource() const
{
return custom_shader_src_;
}
const QString &CustomShaderID() const
{
return custom_shader_id_;
}
void SetNeedsCustomShader(const Node *node, const QString &id = QString())
{
custom_shader_src_ = node;
custom_shader_id_ = id;
}
bool IsClearDestinationEnabled() const { return clear_destination_; }
void SetClearDestinationEnabled(bool e) { clear_destination_ = e; }
bool IsClearDestinationEnabled() const
{
return clear_destination_;
}
void SetClearDestinationEnabled(bool e)
{
clear_destination_ = e;
}
const QMatrix4x4 &GetTransformMatrix() const { return matrix_; }
void SetTransformMatrix(const QMatrix4x4 &m) { matrix_ = m; }
const QMatrix4x4 &GetTransformMatrix() const
{
return matrix_;
}
void SetTransformMatrix(const QMatrix4x4 &m)
{
matrix_ = m;
}
const QMatrix4x4 &GetCropMatrix() const { return crop_matrix_; }
void SetCropMatrix(const QMatrix4x4 &m) { crop_matrix_ = m; }
const QMatrix4x4 &GetCropMatrix() const
{
return crop_matrix_;
}
void SetCropMatrix(const QMatrix4x4 &m)
{
crop_matrix_ = m;
}
const QString &GetFunctionName() const { return function_name_; }
void SetFunctionName(const QString &function_name = QString()) { function_name_ = function_name; };
const QString &GetFunctionName() const
{
return function_name_;
}
void SetFunctionName(const QString &function_name = QString())
{
function_name_ = function_name;
};
bool GetForceOpaque() const { return force_opaque_; }
void SetForceOpaque(bool e) { force_opaque_ = e; }
bool GetForceOpaque() const
{
return force_opaque_;
}
void SetForceOpaque(bool e)
{
force_opaque_ = e;
}
private:
ColorProcessorPtr processor_;
QString id_;
ColorProcessorPtr processor_;
QString id_;
NodeValue input_texture_;
NodeValue input_texture_;
const Node *custom_shader_src_;
QString custom_shader_id_;
const Node *custom_shader_src_;
QString custom_shader_id_;
AlphaAssociated input_alpha_association_;
AlphaAssociated input_alpha_association_;
bool clear_destination_;
bool clear_destination_;
QMatrix4x4 matrix_;
QMatrix4x4 matrix_;
QMatrix4x4 crop_matrix_;
QMatrix4x4 crop_matrix_;
QString function_name_;
bool force_opaque_;
QString function_name_;
bool force_opaque_;
};
}
+83 -73
View File
@@ -23,105 +23,115 @@
#include "node/project/footage/footage.h"
namespace olive {
class FootageJob : public AcceleratedJob
namespace olive
{
class FootageJob : public AcceleratedJob {
public:
FootageJob() :
type_(Track::kNone)
{
}
FootageJob()
: type_(Track::kNone)
{
}
FootageJob(const TimeRange &time, const QString& decoder, const QString& filename, Track::Type type, const rational& length, LoopMode loop_mode) :
time_(time),
decoder_(decoder),
filename_(filename),
type_(type),
length_(length),
loop_mode_(loop_mode)
{
}
FootageJob(const TimeRange &time, const QString &decoder,
const QString &filename, Track::Type type,
const rational &length, LoopMode loop_mode)
: time_(time)
, decoder_(decoder)
, filename_(filename)
, type_(type)
, length_(length)
, loop_mode_(loop_mode)
{
}
const QString& decoder() const
{
return decoder_;
}
const QString &decoder() const
{
return decoder_;
}
const QString& filename() const
{
return filename_;
}
const QString &filename() const
{
return filename_;
}
Track::Type type() const
{
return type_;
}
Track::Type type() const
{
return type_;
}
const VideoParams& video_params() const
{
return video_params_;
}
const VideoParams &video_params() const
{
return video_params_;
}
void set_video_params(const VideoParams& p)
{
video_params_ = p;
}
void set_video_params(const VideoParams &p)
{
video_params_ = p;
}
const AudioParams& audio_params() const
{
return audio_params_;
}
const AudioParams &audio_params() const
{
return audio_params_;
}
void set_audio_params(const AudioParams& p)
{
audio_params_ = p;
}
void set_audio_params(const AudioParams &p)
{
audio_params_ = p;
}
const QString& cache_path() const
{
return cache_path_;
}
const QString &cache_path() const
{
return cache_path_;
}
void set_cache_path(const QString& p)
{
cache_path_ = p;
}
void set_cache_path(const QString &p)
{
cache_path_ = p;
}
const rational& length() const
{
return length_;
}
const rational &length() const
{
return length_;
}
void set_length(const rational& length)
{
length_ = length;
}
void set_length(const rational &length)
{
length_ = length;
}
const TimeRange &time() const { return time_; }
const TimeRange &time() const
{
return time_;
}
LoopMode loop_mode() const { return loop_mode_; }
void set_loop_mode(LoopMode m) { loop_mode_ = m; }
LoopMode loop_mode() const
{
return loop_mode_;
}
void set_loop_mode(LoopMode m)
{
loop_mode_ = m;
}
private:
TimeRange time_;
TimeRange time_;
QString decoder_;
QString decoder_;
QString filename_;
QString filename_;
Track::Type type_;
Track::Type type_;
VideoParams video_params_;
VideoParams video_params_;
AudioParams audio_params_;
AudioParams audio_params_;
QString cache_path_;
QString cache_path_;
rational length_;
LoopMode loop_mode_;
rational length_;
LoopMode loop_mode_;
};
}
+9 -10
View File
@@ -24,18 +24,17 @@
#include "acceleratedjob.h"
#include "codec/frame.h"
namespace olive {
class GenerateJob : public AcceleratedJob
namespace olive
{
public:
GenerateJob() = default;
GenerateJob(const NodeValueRow &row) :
GenerateJob()
{
Insert(row);
}
class GenerateJob : public AcceleratedJob {
public:
GenerateJob() = default;
GenerateJob(const NodeValueRow &row)
: GenerateJob()
{
Insert(row);
}
};
}
+31 -28
View File
@@ -23,44 +23,47 @@
#include "acceleratedjob.h"
namespace olive {
class SampleJob : public AcceleratedJob
namespace olive
{
class SampleJob : public AcceleratedJob {
public:
SampleJob()
{
}
SampleJob()
{
}
SampleJob(const TimeRange &time, const NodeValue& value)
{
samples_ = value.toSamples();
time_ = time;
}
SampleJob(const TimeRange &time, const NodeValue &value)
{
samples_ = value.toSamples();
time_ = time;
}
SampleJob(const TimeRange &time, const QString& from, const NodeValueRow& row)
{
samples_ = row[from].toSamples();
time_ = time;
}
SampleJob(const TimeRange &time, const QString &from,
const NodeValueRow &row)
{
samples_ = row[from].toSamples();
time_ = time;
}
const SampleBuffer &samples() const
{
return samples_;
}
const SampleBuffer &samples() const
{
return samples_;
}
bool HasSamples() const
{
return samples_.is_allocated();
}
bool HasSamples() const
{
return samples_.is_allocated();
}
const TimeRange &time() const { return time_; }
const TimeRange &time() const
{
return time_;
}
private:
SampleBuffer samples_;
TimeRange time_;
SampleBuffer samples_;
TimeRange time_;
};
}
+67 -68
View File
@@ -27,95 +27,94 @@
#include "acceleratedjob.h"
#include "render/texture.h"
namespace olive {
class ShaderJob : public AcceleratedJob
namespace olive
{
class ShaderJob : public AcceleratedJob {
public:
ShaderJob()
{
iterations_ = 1;
iterative_input_ = nullptr;
}
ShaderJob()
{
iterations_ = 1;
iterative_input_ = nullptr;
}
ShaderJob(const NodeValueRow &row) :
ShaderJob()
{
Insert(row);
}
ShaderJob(const NodeValueRow &row)
: ShaderJob()
{
Insert(row);
}
const QString& GetShaderID() const
{
return shader_id_;
}
const QString &GetShaderID() const
{
return shader_id_;
}
void SetShaderID(const QString& id)
{
shader_id_ = id;
}
void SetShaderID(const QString &id)
{
shader_id_ = id;
}
void SetIterations(int iterations, const NodeInput& iterative_input)
{
SetIterations(iterations, iterative_input.input());
}
void SetIterations(int iterations, const NodeInput &iterative_input)
{
SetIterations(iterations, iterative_input.input());
}
void SetIterations(int iterations, const QString& iterative_input)
{
iterations_ = iterations;
iterative_input_ = iterative_input;
}
void SetIterations(int iterations, const QString &iterative_input)
{
iterations_ = iterations;
iterative_input_ = iterative_input;
}
int GetIterationCount() const
{
return iterations_;
}
int GetIterationCount() const
{
return iterations_;
}
const QString& GetIterativeInput() const
{
return iterative_input_;
}
const QString &GetIterativeInput() const
{
return iterative_input_;
}
Texture::Interpolation GetInterpolation(const QString& id) const
{
return interpolation_.value(id, Texture::kDefaultInterpolation);
}
Texture::Interpolation GetInterpolation(const QString &id) const
{
return interpolation_.value(id, Texture::kDefaultInterpolation);
}
const QHash<QString, Texture::Interpolation> &GetInterpolationMap() const
{
return interpolation_;
}
const QHash<QString, Texture::Interpolation> &GetInterpolationMap() const
{
return interpolation_;
}
void SetInterpolation(const NodeInput& input, Texture::Interpolation interp)
{
interpolation_.insert(input.input(), interp);
}
void SetInterpolation(const NodeInput &input, Texture::Interpolation interp)
{
interpolation_.insert(input.input(), interp);
}
void SetInterpolation(const QString& id, Texture::Interpolation interp)
{
interpolation_.insert(id, interp);
}
void SetInterpolation(const QString &id, Texture::Interpolation interp)
{
interpolation_.insert(id, interp);
}
void SetVertexCoordinates(const QVector<float> &vertex_coords)
{
vertex_overrides_ = vertex_coords;
}
void SetVertexCoordinates(const QVector<float> &vertex_coords)
{
vertex_overrides_ = vertex_coords;
}
const QVector<float>& GetVertexCoordinates()
{
return vertex_overrides_;
}
const QVector<float> &GetVertexCoordinates()
{
return vertex_overrides_;
}
private:
QString shader_id_;
QString shader_id_;
int iterations_;
int iterations_;
QString iterative_input_;
QString iterative_input_;
QHash<QString, Texture::Interpolation> interpolation_;
QVector<float> vertex_overrides_;
QHash<QString, Texture::Interpolation> interpolation_;
QVector<float> vertex_overrides_;
};
}
+3 -6
View File
@@ -1,13 +1,10 @@
#ifndef LOOPMODE_H
#define LOOPMODE_H
namespace olive {
namespace olive
{
enum class LoopMode {
kLoopModeOff,
kLoopModeLoop,
kLoopModeClamp
};
enum class LoopMode { kLoopModeOff, kLoopModeLoop, kLoopModeClamp };
}
+14 -11
View File
@@ -20,45 +20,48 @@
#include "managedcolor.h"
namespace olive {
namespace olive
{
ManagedColor::ManagedColor()
{
}
ManagedColor::ManagedColor(const double &r, const double &g, const double &b, const double &a) :
Color(r, g, b, a)
ManagedColor::ManagedColor(const double &r, const double &g, const double &b,
const double &a)
: Color(r, g, b, a)
{
}
ManagedColor::ManagedColor(const char *data, const PixelFormat &format, int channel_layout) :
Color(data, format, channel_layout)
ManagedColor::ManagedColor(const char *data, const PixelFormat &format,
int channel_layout)
: Color(data, format, channel_layout)
{
}
ManagedColor::ManagedColor(const Color &c) :
Color(c)
ManagedColor::ManagedColor(const Color &c)
: Color(c)
{
}
const QString &ManagedColor::color_input() const
{
return color_input_;
return color_input_;
}
void ManagedColor::set_color_input(const QString &color_input)
{
color_input_ = color_input;
color_input_ = color_input;
}
const ColorTransform &ManagedColor::color_output() const
{
return color_transform_;
return color_transform_;
}
void ManagedColor::set_color_output(const ColorTransform &color_output)
{
color_transform_ = color_output;
color_transform_ = color_output;
}
}
+15 -14
View File
@@ -25,27 +25,28 @@
#include "colortransform.h"
namespace olive {
class ManagedColor : public Color
namespace olive
{
class ManagedColor : public Color {
public:
ManagedColor();
ManagedColor(const double& r, const double& g, const double& b, const double& a = 1.0);
ManagedColor(const char *data, const PixelFormat &format, int channel_layout);
ManagedColor(const Color& c);
ManagedColor();
ManagedColor(const double &r, const double &g, const double &b,
const double &a = 1.0);
ManagedColor(const char *data, const PixelFormat &format,
int channel_layout);
ManagedColor(const Color &c);
const QString& color_input() const;
void set_color_input(const QString &color_input);
const QString &color_input() const;
void set_color_input(const QString &color_input);
const ColorTransform& color_output() const;
void set_color_output(const ColorTransform &color_output);
const ColorTransform &color_output() const;
void set_color_output(const ColorTransform &color_output);
private:
QString color_input_;
ColorTransform color_transform_;
QString color_input_;
ColorTransform color_transform_;
};
}
File diff suppressed because it is too large Load Diff
+62 -52
View File
@@ -31,94 +31,104 @@
#include "render/renderer.h"
namespace olive {
class OpenGLRenderer : public Renderer
namespace olive
{
Q_OBJECT
class OpenGLRenderer : public Renderer {
Q_OBJECT
public:
OpenGLRenderer(QObject* parent = nullptr);
OpenGLRenderer(QObject *parent = nullptr);
virtual ~OpenGLRenderer() override;
virtual ~OpenGLRenderer() override;
void Init(QOpenGLContext* existing_ctx);
void Init(QOpenGLContext *existing_ctx);
virtual bool Init() override;
virtual bool Init() override;
virtual void PostDestroy() override;
virtual void PostDestroy() override;
virtual void PostInit() override;
virtual void PostInit() override;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual void ClearDestination(olive::Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
virtual void DestroyNativeShader(QVariant shader) override;
virtual void DestroyNativeShader(QVariant shader) override;
virtual void UploadToTexture(const QVariant &handle, const VideoParams &params, const void* data, int linesize) override;
virtual void UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize) override;
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams &params, void* data, int linesize) override;
virtual void DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize) override;
virtual void Flush() override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
virtual Color GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt) override;
protected:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
olive::VideoParams destination_params,
bool clear_destination) override;
virtual void Blit(QVariant shader, olive::ShaderJob job,
olive::Texture *destination,
olive::VideoParams destination_params,
bool clear_destination) override;
virtual QVariant CreateNativeTexture(int width, int height, int depth, PixelFormat format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual QVariant CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data = nullptr,
int linesize = 0) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual void DestroyInternal() override;
virtual void DestroyInternal() override;
private:
static GLint GetInternalFormat(PixelFormat format, int channel_layout);
static GLint GetInternalFormat(PixelFormat format, int channel_layout);
static GLenum GetPixelType(PixelFormat format);
static GLenum GetPixelType(PixelFormat format);
static GLenum GetPixelFormat(int channel_count);
static GLenum GetPixelFormat(int channel_count);
void AttachTextureAsDestination(const QVariant &texture);
void AttachTextureAsDestination(const QVariant &texture);
void DetachTextureAsDestination();
void DetachTextureAsDestination();
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0);
void ClearDestinationInternal(double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0);
GLuint CompileShader(GLenum type, const QString &code);
GLuint CompileShader(GLenum type, const QString &code);
QOpenGLContext* context_;
QOpenGLContext *context_;
QOpenGLFunctions* functions_;
QOpenGLFunctions *functions_;
QOffscreenSurface surface_;
QOffscreenSurface surface_;
GLuint framebuffer_;
GLuint framebuffer_;
struct TextureCacheKey {
int width;
int height;
int depth;
PixelFormat format;
int channel_count;
struct TextureCacheKey {
int width;
int height;
int depth;
PixelFormat format;
int channel_count;
bool operator==(const TextureCacheKey &rhs) const
{
return width == rhs.width && height == rhs.height && depth == rhs.depth
&& format == rhs.format && channel_count == rhs.channel_count;
}
};
bool operator==(const TextureCacheKey &rhs) const
{
return width == rhs.width && height == rhs.height &&
depth == rhs.depth && format == rhs.format &&
channel_count == rhs.channel_count;
}
};
QMap<GLuint, TextureCacheKey> texture_params_;
static const int kTextureCacheMaxSize;
QMap<GLuint, TextureCacheKey> texture_params_;
static const int kTextureCacheMaxSize;
};
}
+175 -169
View File
@@ -25,225 +25,231 @@
#include "node/project/sequence/sequence.h"
#include "render/diskmanager.h"
namespace olive {
namespace olive
{
void PlaybackCache::Invalidate(const TimeRange &r)
{
if (r.in() == r.out()) {
qWarning() << "Tried to invalidate zero-length range";
return;
}
if (r.in() == r.out()) {
qWarning() << "Tried to invalidate zero-length range";
return;
}
validated_.remove(r);
validated_.remove(r);
if (!passthroughs_.empty()) {
TimeRangeList::util_remove(&passthroughs_, r);
}
if (!passthroughs_.empty()) {
TimeRangeList::util_remove(&passthroughs_, r);
}
InvalidateEvent(r);
InvalidateEvent(r);
emit Invalidated(r);
emit Invalidated(r);
if (saving_enabled_) {
SaveState();
}
if (saving_enabled_) {
SaveState();
}
}
Node *PlaybackCache::parent() const
{
return dynamic_cast<Node*>(QObject::parent());
return dynamic_cast<Node *>(QObject::parent());
}
QDir PlaybackCache::GetThisCacheDirectory() const
{
return GetThisCacheDirectory(GetCacheDirectory(), GetUuid());
return GetThisCacheDirectory(GetCacheDirectory(), GetUuid());
}
QDir PlaybackCache::GetThisCacheDirectory(const QString &cache_path, const QUuid &cache_id)
QDir PlaybackCache::GetThisCacheDirectory(const QString &cache_path,
const QUuid &cache_id)
{
return QDir(cache_path).filePath(cache_id.toString());
return QDir(cache_path).filePath(cache_id.toString());
}
void PlaybackCache::LoadState()
{
QDir cache_dir = GetThisCacheDirectory();
QFile f(cache_dir.filePath(QStringLiteral("state")));
QDir cache_dir = GetThisCacheDirectory();
QFile f(cache_dir.filePath(QStringLiteral("state")));
if (!f.exists()) {
// No state exists, assume nothing valid
validated_.clear();
passthroughs_.clear();
return;
}
if (!f.exists()) {
// No state exists, assume nothing valid
validated_.clear();
passthroughs_.clear();
return;
}
qint64 file_time = f.fileTime(QFileDevice::FileModificationTime).toMSecsSinceEpoch();
if (file_time > last_loaded_state_ && f.open(QFile::ReadOnly)) {
QDataStream s(&f);
qint64 file_time =
f.fileTime(QFileDevice::FileModificationTime).toMSecsSinceEpoch();
if (file_time > last_loaded_state_ && f.open(QFile::ReadOnly)) {
QDataStream s(&f);
uint32_t version;
s >> version;
uint32_t version;
s >> version;
LoadStateEvent(s);
LoadStateEvent(s);
switch (version) {
case 1:
{
int valid_count, pass_count;
switch (version) {
case 1: {
int valid_count, pass_count;
s >> valid_count;
for (int i=0; i<valid_count; i++) {
int in_num, in_den, out_num, out_den;
s >> valid_count;
for (int i = 0; i < valid_count; i++) {
int in_num, in_den, out_num, out_den;
s >> in_num;
s >> in_den;
s >> out_num;
s >> out_den;
s >> in_num;
s >> in_den;
s >> out_num;
s >> out_den;
validated_.insert(TimeRange(rational(in_num, in_den), rational(out_num, out_den)));
}
validated_.insert(TimeRange(rational(in_num, in_den),
rational(out_num, out_den)));
}
s >> pass_count;
for (int i=0; i<pass_count; i++) {
QUuid id;
int in_num, in_den, out_num, out_den;
s >> pass_count;
for (int i = 0; i < pass_count; i++) {
QUuid id;
int in_num, in_den, out_num, out_den;
s >> in_num;
s >> in_den;
s >> out_num;
s >> out_den;
s >> id;
s >> in_num;
s >> in_den;
s >> out_num;
s >> out_den;
s >> id;
Passthrough p = TimeRange(rational(in_num, in_den), rational(out_num, out_den));
p.cache = id;
passthroughs_.push_back(p);
}
Passthrough p = TimeRange(rational(in_num, in_den),
rational(out_num, out_den));
p.cache = id;
passthroughs_.push_back(p);
}
break;
}
}
break;
}
}
f.close();
f.close();
last_loaded_state_ = file_time;
}
last_loaded_state_ = file_time;
}
}
void PlaybackCache::SaveState()
{
if (!DiskManager::instance()) {
return;
}
if (!DiskManager::instance()) {
return;
}
QDir cache_dir = GetThisCacheDirectory();
QFile f(cache_dir.filePath(QStringLiteral("state")));
if (validated_.isEmpty() && passthroughs_.empty()) {
if (f.exists()) {
f.remove();
}
} else {
if (FileFunctions::DirectoryIsValid(cache_dir)) {
if (f.open(QFile::WriteOnly)) {
QDataStream s(&f);
QDir cache_dir = GetThisCacheDirectory();
QFile f(cache_dir.filePath(QStringLiteral("state")));
if (validated_.isEmpty() && passthroughs_.empty()) {
if (f.exists()) {
f.remove();
}
} else {
if (FileFunctions::DirectoryIsValid(cache_dir)) {
if (f.open(QFile::WriteOnly)) {
QDataStream s(&f);
uint32_t version = 1;
s << version;
uint32_t version = 1;
s << version;
SaveStateEvent(s);
SaveStateEvent(s);
// Using "int" for backwards compatibility with when we used QVector, could potentially overflow
s << int(validated_.size());
// Using "int" for backwards compatibility with when we used QVector, could potentially overflow
s << int(validated_.size());
for (const TimeRange &r : validated_) {
s << r.in().numerator();
s << r.in().denominator();
s << r.out().numerator();
s << r.out().denominator();
}
for (const TimeRange &r : validated_) {
s << r.in().numerator();
s << r.in().denominator();
s << r.out().numerator();
s << r.out().denominator();
}
// Using "int" for backwards compatibility with when we used QVector, could potentially overflow
s << int(passthroughs_.size());
// Using "int" for backwards compatibility with when we used QVector, could potentially overflow
s << int(passthroughs_.size());
for (const Passthrough &p : passthroughs_) {
s << p.in().numerator();
s << p.in().denominator();
s << p.out().numerator();
s << p.out().denominator();
s << p.cache;
}
for (const Passthrough &p : passthroughs_) {
s << p.in().numerator();
s << p.in().denominator();
s << p.out().numerator();
s << p.out().denominator();
s << p.cache;
}
f.close();
f.close();
last_loaded_state_ = f.fileTime(QFileDevice::FileModificationTime).toMSecsSinceEpoch();
}
}
}
last_loaded_state_ =
f.fileTime(QFileDevice::FileModificationTime)
.toMSecsSinceEpoch();
}
}
}
}
void PlaybackCache::Draw(QPainter *p, const rational &start, double scale, const QRect &rect) const
void PlaybackCache::Draw(QPainter *p, const rational &start, double scale,
const QRect &rect) const
{
p->fillRect(rect, Qt::red);
p->fillRect(rect, Qt::red);
foreach (const TimeRange& range, GetValidatedRanges()) {
int range_left = rect.left() + (range.in() - start).toDouble() * scale;
if (range_left >= rect.right()) {
continue;
}
foreach (const TimeRange &range, GetValidatedRanges()) {
int range_left = rect.left() + (range.in() - start).toDouble() * scale;
if (range_left >= rect.right()) {
continue;
}
int range_right = rect.left() + (range.out() - start).toDouble() * scale;
if (range_right < rect.left()) {
continue;
}
int range_right =
rect.left() + (range.out() - start).toDouble() * scale;
if (range_right < rect.left()) {
continue;
}
int adjusted_left = std::max(range_left, rect.left());
int adjusted_right = std::min(range_right, rect.right());
int adjusted_left = std::max(range_left, rect.left());
int adjusted_right = std::min(range_right, rect.right());
p->fillRect(adjusted_left,
rect.top(),
adjusted_right - adjusted_left,
rect.height(),
Qt::green);
}
p->fillRect(adjusted_left, rect.top(), adjusted_right - adjusted_left,
rect.height(), Qt::green);
}
}
void PlaybackCache::SetPassthrough(PlaybackCache *cache)
{
for (const TimeRange &r : cache->GetValidatedRanges()) {
Passthrough p = r;
p.cache = cache->GetUuid();
passthroughs_.push_back(p);
}
for (const TimeRange &r : cache->GetValidatedRanges()) {
Passthrough p = r;
p.cache = cache->GetUuid();
passthroughs_.push_back(p);
}
passthroughs_.insert(passthroughs_.end(), cache->GetPassthroughs().begin(), cache->GetPassthroughs().end());
passthroughs_.insert(passthroughs_.end(), cache->GetPassthroughs().begin(),
cache->GetPassthroughs().end());
if (saving_enabled_) {
SaveState();
}
if (saving_enabled_) {
SaveState();
}
}
void PlaybackCache::InvalidateAll()
{
Invalidate(TimeRange(0, RATIONAL_MAX));
Invalidate(TimeRange(0, RATIONAL_MAX));
}
void PlaybackCache::Request(ViewerOutput *context, const TimeRange &r)
{
request_context_ = context;
requested_.insert(r);
request_context_ = context;
requested_.insert(r);
emit Requested(request_context_, r);
emit Requested(request_context_, r);
}
void PlaybackCache::Validate(const TimeRange &r, bool signal)
{
validated_.insert(r);
validated_.insert(r);
if (signal) {
emit Validated(r);
}
if (signal) {
emit Validated(r);
}
if (saving_enabled_) {
SaveState();
}
if (saving_enabled_) {
SaveState();
}
}
void PlaybackCache::InvalidateEvent(const TimeRange &)
@@ -252,60 +258,60 @@ void PlaybackCache::InvalidateEvent(const TimeRange &)
Project *PlaybackCache::GetProject() const
{
return Project::GetProjectFromObject(this);
return Project::GetProjectFromObject(this);
}
PlaybackCache::PlaybackCache(QObject *parent) :
QObject(parent),
saving_enabled_(true),
last_loaded_state_(0)
PlaybackCache::PlaybackCache(QObject *parent)
: QObject(parent)
, saving_enabled_(true)
, last_loaded_state_(0)
{
uuid_ = QUuid::createUuid();
uuid_ = QUuid::createUuid();
}
void PlaybackCache::SetUuid(const QUuid &u)
{
uuid_ = u;
uuid_ = u;
LoadState();
LoadState();
}
TimeRangeList PlaybackCache::GetInvalidatedRanges(TimeRange intersecting) const
{
TimeRangeList invalidated;
TimeRangeList invalidated;
// Prevent TimeRange from being below 0, some other behavior in Olive relies on this behavior
// and it seemed reasonable to have safety code in here
intersecting.set_out(qMax(rational(0), intersecting.out()));
intersecting.set_in(qMax(rational(0), intersecting.in()));
// Prevent TimeRange from being below 0, some other behavior in Olive relies on this behavior
// and it seemed reasonable to have safety code in here
intersecting.set_out(qMax(rational(0), intersecting.out()));
intersecting.set_in(qMax(rational(0), intersecting.in()));
invalidated.insert(intersecting);
invalidated.insert(intersecting);
foreach (const TimeRange &range, validated_) {
invalidated.remove(range);
}
foreach (const TimeRange &range, validated_) {
invalidated.remove(range);
}
foreach (const TimeRange &range, passthroughs_) {
invalidated.remove(range);
}
foreach (const TimeRange &range, passthroughs_) {
invalidated.remove(range);
}
return invalidated;
return invalidated;
}
bool PlaybackCache::HasInvalidatedRanges(const TimeRange &intersecting) const
{
return !validated_.contains(intersecting);
return !validated_.contains(intersecting);
}
QString PlaybackCache::GetCacheDirectory() const
{
Project* project = GetProject();
Project *project = GetProject();
if (project) {
return project->cache_path();
} else {
return DiskManager::instance()->GetDefaultCachePath();
}
if (project) {
return project->cache_path();
} else {
return DiskManager::instance()->GetDefaultCachePath();
}
}
}
+100 -74
View File
@@ -32,127 +32,153 @@
using namespace olive::core;
namespace olive {
namespace olive
{
class Node;
class Project;
class ViewerOutput;
class PlaybackCache : public QObject
{
Q_OBJECT
class PlaybackCache : public QObject {
Q_OBJECT
public:
PlaybackCache(QObject* parent = nullptr);
PlaybackCache(QObject *parent = nullptr);
const QUuid &GetUuid() const { return uuid_; }
void SetUuid(const QUuid &u);
const QUuid &GetUuid() const
{
return uuid_;
}
void SetUuid(const QUuid &u);
TimeRangeList GetInvalidatedRanges(TimeRange intersecting) const;
TimeRangeList GetInvalidatedRanges(const rational &length) const
{
return GetInvalidatedRanges(TimeRange(0, length));
}
TimeRangeList GetInvalidatedRanges(TimeRange intersecting) const;
TimeRangeList GetInvalidatedRanges(const rational &length) const
{
return GetInvalidatedRanges(TimeRange(0, length));
}
bool HasInvalidatedRanges(const TimeRange &intersecting) const;
bool HasInvalidatedRanges(const rational &length) const
{
return HasInvalidatedRanges(TimeRange(0, length));
}
bool HasInvalidatedRanges(const TimeRange &intersecting) const;
bool HasInvalidatedRanges(const rational &length) const
{
return HasInvalidatedRanges(TimeRange(0, length));
}
QString GetCacheDirectory() const;
QString GetCacheDirectory() const;
void Invalidate(const TimeRange& r);
void Invalidate(const TimeRange &r);
bool HasValidatedRanges() const { return !validated_.isEmpty(); }
const TimeRangeList &GetValidatedRanges() const { return validated_; }
bool HasValidatedRanges() const
{
return !validated_.isEmpty();
}
const TimeRangeList &GetValidatedRanges() const
{
return validated_;
}
Node *parent() const;
Node *parent() const;
QDir GetThisCacheDirectory() const;
static QDir GetThisCacheDirectory(const QString &cache_path, const QUuid &cache_id);
QDir GetThisCacheDirectory() const;
static QDir GetThisCacheDirectory(const QString &cache_path,
const QUuid &cache_id);
void LoadState();
void SaveState();
void LoadState();
void SaveState();
void Draw(QPainter *painter, const rational &start, double scale, const QRect &rect) const;
void Draw(QPainter *painter, const rational &start, double scale,
const QRect &rect) const;
static int GetCacheIndicatorHeight()
{
return QFontMetrics(QFont()).height()/4;
}
static int GetCacheIndicatorHeight()
{
return QFontMetrics(QFont()).height() / 4;
}
bool IsSavingEnabled() const { return saving_enabled_; }
void SetSavingEnabled(bool e) { saving_enabled_ = e; }
bool IsSavingEnabled() const
{
return saving_enabled_;
}
void SetSavingEnabled(bool e)
{
saving_enabled_ = e;
}
virtual void SetPassthrough(PlaybackCache *cache);
virtual void SetPassthrough(PlaybackCache *cache);
QMutex *mutex() { return &mutex_; }
QMutex *mutex()
{
return &mutex_;
}
class Passthrough : public TimeRange
{
public:
Passthrough(const TimeRange &r) :
TimeRange(r)
{}
class Passthrough : public TimeRange {
public:
Passthrough(const TimeRange &r)
: TimeRange(r)
{
}
QUuid cache;
};
QUuid cache;
};
const std::vector<Passthrough> &GetPassthroughs() const { return passthroughs_; }
const std::vector<Passthrough> &GetPassthroughs() const
{
return passthroughs_;
}
void ClearRequestRange(const TimeRange &r)
{
requested_.remove(r);
}
void ClearRequestRange(const TimeRange &r)
{
requested_.remove(r);
}
void ResignalRequests()
{
for (const TimeRange &r : requested_) {
emit Requested(request_context_, r);
}
}
void ResignalRequests()
{
for (const TimeRange &r : requested_) {
emit Requested(request_context_, r);
}
}
public slots:
void InvalidateAll();
void InvalidateAll();
void Request(ViewerOutput *context, const TimeRange &r);
void Request(ViewerOutput *context, const TimeRange &r);
signals:
void Invalidated(const TimeRange& r);
void Invalidated(const TimeRange &r);
void Validated(const TimeRange& r);
void Validated(const TimeRange &r);
void Requested(ViewerOutput *context, const TimeRange& r);
void Requested(ViewerOutput *context, const TimeRange &r);
void CancelAll();
void CancelAll();
protected:
void Validate(const TimeRange& r, bool signal = true);
void Validate(const TimeRange &r, bool signal = true);
virtual void InvalidateEvent(const TimeRange& range);
virtual void InvalidateEvent(const TimeRange &range);
virtual void LoadStateEvent(QDataStream &stream){}
virtual void LoadStateEvent(QDataStream &stream)
{
}
virtual void SaveStateEvent(QDataStream &stream){}
virtual void SaveStateEvent(QDataStream &stream)
{
}
Project* GetProject() const;
Project *GetProject() const;
private:
TimeRangeList validated_;
TimeRangeList validated_;
TimeRangeList requested_;
ViewerOutput *request_context_;
TimeRangeList requested_;
ViewerOutput *request_context_;
QUuid uuid_;
QUuid uuid_;
bool saving_enabled_;
bool saving_enabled_;
QMutex mutex_;
QMutex mutex_;
std::vector<Passthrough> passthroughs_;
qint64 last_loaded_state_;
std::vector<Passthrough> passthroughs_;
qint64 last_loaded_state_;
};
}
+28 -26
View File
@@ -20,63 +20,65 @@
#include "previewaudiodevice.h"
namespace olive {
namespace olive
{
PreviewAudioDevice::PreviewAudioDevice(QObject *parent) :
notify_interval_(0),
bytes_read_(0)
PreviewAudioDevice::PreviewAudioDevice(QObject *parent)
: notify_interval_(0)
, bytes_read_(0)
{
}
PreviewAudioDevice::~PreviewAudioDevice()
{
close();
close();
}
bool PreviewAudioDevice::isSequential() const
{
return true;
return true;
}
qint64 PreviewAudioDevice::readData(char *data, qint64 maxSize)
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
qint64 copy_length = qMin(maxSize, qint64(buffer_.size()));
qint64 copy_length = qMin(maxSize, qint64(buffer_.size()));
if (copy_length) {
qint64 new_bytes_read = bytes_read_ + copy_length;
if (copy_length) {
qint64 new_bytes_read = bytes_read_ + copy_length;
if (notify_interval_ > 0) {
if ((bytes_read_ / notify_interval_) != (new_bytes_read / notify_interval_)) {
emit Notify();
}
}
if (notify_interval_ > 0) {
if ((bytes_read_ / notify_interval_) !=
(new_bytes_read / notify_interval_)) {
emit Notify();
}
}
bytes_read_ = new_bytes_read;
bytes_read_ = new_bytes_read;
memcpy(data, buffer_.constData(), copy_length);
buffer_ = buffer_.mid(copy_length);
}
memcpy(data, buffer_.constData(), copy_length);
buffer_ = buffer_.mid(copy_length);
}
return copy_length;
return copy_length;
}
qint64 PreviewAudioDevice::writeData(const char *data, qint64 length)
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
buffer_.append(data, length);
buffer_.append(data, length);
return length;
return length;
}
void PreviewAudioDevice::clear()
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
buffer_.clear();
bytes_read_ = 0;
buffer_.clear();
bytes_read_ = 0;
}
}
+29 -30
View File
@@ -23,55 +23,54 @@
#include "previewautocacher.h"
namespace olive {
class PreviewAudioDevice : public QIODevice
namespace olive
{
Q_OBJECT
class PreviewAudioDevice : public QIODevice {
Q_OBJECT
public:
PreviewAudioDevice(QObject *parent = nullptr);
PreviewAudioDevice(QObject *parent = nullptr);
virtual ~PreviewAudioDevice() override;
virtual ~PreviewAudioDevice() override;
void StartQueuing();
void StartQueuing();
virtual bool isSequential() const override;
virtual bool isSequential() const override;
virtual qint64 readData(char *data, qint64 maxSize) override;
virtual qint64 readData(char *data, qint64 maxSize) override;
virtual qint64 writeData(const char *data, qint64 length) override;
virtual qint64 writeData(const char *data, qint64 length) override;
int bytes_per_frame() const
{
return bytes_per_frame_;
}
int bytes_per_frame() const
{
return bytes_per_frame_;
}
void set_bytes_per_frame(int b)
{
bytes_per_frame_ = b;
}
void set_bytes_per_frame(int b)
{
bytes_per_frame_ = b;
}
void set_notify_interval(qint64 i)
{
notify_interval_ = i;
}
void set_notify_interval(qint64 i)
{
notify_interval_ = i;
}
void clear();
void clear();
signals:
void Notify();
void Notify();
private:
QMutex lock_;
QMutex lock_;
QByteArray buffer_;
QByteArray buffer_;
int bytes_per_frame_;
int bytes_per_frame_;
qint64 notify_interval_;
qint64 bytes_read_;
qint64 notify_interval_;
qint64 bytes_read_;
};
}
File diff suppressed because it is too large Load Diff
+115 -97
View File
@@ -33,49 +33,51 @@
#include "render/renderjobtracker.h"
#include "render/renderticket.h"
namespace olive {
namespace olive
{
/**
* @brief Manager for dynamically caching a sequence in the background
*
* Intended to be used with a Viewer to dynamically cache parts of a sequence based on the playhead.
*/
class PreviewAutoCacher : public QObject
{
Q_OBJECT
class PreviewAutoCacher : public QObject {
Q_OBJECT
public:
PreviewAutoCacher(QObject *parent = nullptr);
PreviewAutoCacher(QObject *parent = nullptr);
virtual ~PreviewAutoCacher() override;
virtual ~PreviewAutoCacher() override;
RenderTicketPtr GetSingleFrame(ViewerOutput *viewer, const rational& t, bool dry = false);
RenderTicketPtr GetSingleFrame(Node *n, ViewerOutput *viewer, const rational& t, bool dry = false);
RenderTicketPtr GetSingleFrame(ViewerOutput *viewer, const rational &t,
bool dry = false);
RenderTicketPtr GetSingleFrame(Node *n, ViewerOutput *viewer,
const rational &t, bool dry = false);
RenderTicketPtr GetRangeOfAudio(ViewerOutput *viewer, TimeRange range);
RenderTicketPtr GetRangeOfAudio(ViewerOutput *viewer, TimeRange range);
void ClearSingleFrameRenders();
void ClearSingleFrameRendersThatArentRunning();
void ClearSingleFrameRenders();
void ClearSingleFrameRendersThatArentRunning();
/**
/**
* @brief Set the viewer node to auto-cache
*/
void SetProject(Project *project);
void SetProject(Project *project);
/**
/**
* @brief Force a certain range to be cached
*
* Usually, PreviewAutoCacher caches a user-defined range around the playhead, however there are
* times they may want certain non-playhead-related time ranges to be cached (i.e. entire sequence
* or in/out range), so that can be set here.
*/
void ForceCacheRange(ViewerOutput *context, const TimeRange& range);
void ForceCacheRange(ViewerOutput *context, const TimeRange &range);
/**
/**
* @brief Updates the range of frames to auto-cache
*/
void SetPlayhead(const rational& playhead);
void SetPlayhead(const rational &playhead);
/**
/**
* @brief Call cancel on all currently running video tasks
*
* Signalling cancel to a video task indicates that we're no longer interested in its end result.
@@ -83,140 +85,156 @@ public:
* up finishing the task. The RenderManager will also return "no result", which can be checked
* with watcher->HasResult.
*/
void CancelVideoTasks(bool and_wait_for_them_to_finish = false);
void CancelAudioTasks(bool and_wait_for_them_to_finish = false);
void CancelVideoTasks(bool and_wait_for_them_to_finish = false);
void CancelAudioTasks(bool and_wait_for_them_to_finish = false);
bool IsRenderingCustomRange() const;
bool IsRenderingCustomRange() const;
void SetRendersPaused(bool e);
void SetThumbnailsPaused(bool e);
void SetRendersPaused(bool e);
void SetThumbnailsPaused(bool e);
void SetMulticamNode(MultiCamNode *n) { multicam_ = n; }
void SetMulticamNode(MultiCamNode *n)
{
multicam_ = n;
}
void SetIgnoreCacheRequests(bool e) { ignore_cache_requests_ = e; }
void SetIgnoreCacheRequests(bool e)
{
ignore_cache_requests_ = e;
}
public slots:
void SetDisplayColorProcessor(ColorProcessorPtr processor)
{
display_color_processor_ = processor;
}
void SetDisplayColorProcessor(ColorProcessorPtr processor)
{
display_color_processor_ = processor;
}
signals:
void StopCacheProxyTasks();
void StopCacheProxyTasks();
void SignalCacheProxyTaskProgress(double d);
void SignalCacheProxyTaskProgress(double d);
private:
void TryRender();
void TryRender();
RenderTicketWatcher *RenderFrame(Node *node, ViewerOutput *context, const rational &time, PlaybackCache *cache, bool dry);
RenderTicketWatcher *RenderFrame(Node *node, ViewerOutput *context,
const rational &time, PlaybackCache *cache,
bool dry);
RenderTicketPtr RenderAudio(Node *node, ViewerOutput *context, const TimeRange &range, PlaybackCache *cache);
RenderTicketPtr RenderAudio(Node *node, ViewerOutput *context,
const TimeRange &range, PlaybackCache *cache);
void ConnectToNodeCache(Node *node);
void DisconnectFromNodeCache(Node *node);
void ConnectToNodeCache(Node *node);
void DisconnectFromNodeCache(Node *node);
void CancelQueuedSingleFrameRender();
void CancelQueuedSingleFrameRender();
void StartCachingRange(const TimeRange &range, TimeRangeList *range_list, RenderJobTracker *tracker);
void StartCachingVideoRange(ViewerOutput *context, PlaybackCache *cache, const TimeRange &range);
void StartCachingAudioRange(ViewerOutput *context, PlaybackCache *cache, const TimeRange &range);
void StartCachingRange(const TimeRange &range, TimeRangeList *range_list,
RenderJobTracker *tracker);
void StartCachingVideoRange(ViewerOutput *context, PlaybackCache *cache,
const TimeRange &range);
void StartCachingAudioRange(ViewerOutput *context, PlaybackCache *cache,
const TimeRange &range);
void VideoInvalidatedFromNode(ViewerOutput *context, PlaybackCache *cache, const olive::TimeRange &range);
void AudioInvalidatedFromNode(ViewerOutput *context, PlaybackCache *cache, const olive::TimeRange &range);
void VideoInvalidatedFromNode(ViewerOutput *context, PlaybackCache *cache,
const olive::TimeRange &range);
void AudioInvalidatedFromNode(ViewerOutput *context, PlaybackCache *cache,
const olive::TimeRange &range);
Project* project_;
Project *project_;
ProjectCopier *copier_;
ProjectCopier *copier_;
TimeRange cache_range_;
TimeRange cache_range_;
bool use_custom_range_;
TimeRange custom_autocache_range_;
bool use_custom_range_;
TimeRange custom_autocache_range_;
bool pause_renders_;
bool pause_thumbnails_;
bool pause_renders_;
bool pause_thumbnails_;
RenderTicketPtr single_frame_render_;
QMap<RenderTicketWatcher*, QVector<RenderTicketPtr> > video_immediate_passthroughs_;
RenderTicketPtr single_frame_render_;
QMap<RenderTicketWatcher *, QVector<RenderTicketPtr>>
video_immediate_passthroughs_;
QTimer delayed_requeue_timer_;
QTimer delayed_requeue_timer_;
JobTime last_conform_task_;
JobTime last_conform_task_;
QVector<RenderTicketWatcher*> running_video_tasks_;
QVector<RenderTicketWatcher*> running_audio_tasks_;
QVector<RenderTicketWatcher *> running_video_tasks_;
QVector<RenderTicketWatcher *> running_audio_tasks_;
ColorManager* copied_color_manager_;
ColorManager *copied_color_manager_;
struct VideoJob {
Node *node;
ViewerOutput *context;
PlaybackCache *cache;
TimeRange range;
TimeRangeListFrameIterator iterator;
};
struct VideoJob {
Node *node;
ViewerOutput *context;
PlaybackCache *cache;
TimeRange range;
TimeRangeListFrameIterator iterator;
};
struct VideoCacheData {
RenderJobTracker job_tracker;
};
struct VideoCacheData {
RenderJobTracker job_tracker;
};
struct AudioJob {
Node *node;
ViewerOutput *context;
PlaybackCache *cache;
TimeRange range;
};
struct AudioJob {
Node *node;
ViewerOutput *context;
PlaybackCache *cache;
TimeRange range;
};
struct AudioCacheData {
RenderJobTracker job_tracker;
TimeRangeList needs_conform;
};
struct AudioCacheData {
RenderJobTracker job_tracker;
TimeRangeList needs_conform;
};
std::list<VideoJob> pending_video_jobs_;
std::list<AudioJob> pending_audio_jobs_;
std::list<VideoJob> pending_video_jobs_;
std::list<AudioJob> pending_audio_jobs_;
QHash<PlaybackCache*, VideoCacheData> video_cache_data_;
QHash<PlaybackCache*, AudioCacheData> audio_cache_data_;
QHash<PlaybackCache *, VideoCacheData> video_cache_data_;
QHash<PlaybackCache *, AudioCacheData> audio_cache_data_;
ColorProcessorPtr display_color_processor_;
ColorProcessorPtr display_color_processor_;
MultiCamNode *multicam_;
MultiCamNode *multicam_;
bool ignore_cache_requests_;
bool ignore_cache_requests_;
private slots:
/**
/**
* @brief Handler for when the NodeGraph reports a video change over a certain time range
*/
void VideoInvalidatedFromCache(ViewerOutput *context, const olive::TimeRange &range);
void VideoInvalidatedFromCache(ViewerOutput *context,
const olive::TimeRange &range);
/**
/**
* @brief Handler for when the NodeGraph reports a audio change over a certain time range
*/
void AudioInvalidatedFromCache(ViewerOutput *context, const olive::TimeRange &range);
void AudioInvalidatedFromCache(ViewerOutput *context,
const olive::TimeRange &range);
void CancelForCache();
void CancelForCache();
/**
/**
* @brief Handler for when the RenderManager has returned rendered audio
*/
void AudioRendered();
void AudioRendered();
/**
/**
* @brief Handler for when the RenderManager has returned rendered video frames
*/
void VideoRendered();
void VideoRendered();
/**
/**
* @brief Generic function called whenever the frames to render need to be (re)queued
*/
//void RequeueFrames();
//void RequeueFrames();
void ConformFinished();
void CacheProxyTaskCancelled();
void ConformFinished();
void CacheProxyTaskCancelled();
};
}
+185 -154
View File
@@ -22,236 +22,263 @@
#include "node/group/group.h"
namespace olive {
ProjectCopier::ProjectCopier(QObject *parent) :
QObject(parent)
namespace olive
{
original_ = nullptr;
copy_ = new Project();
copy_->setParent(this);
ProjectCopier::ProjectCopier(QObject *parent)
: QObject(parent)
{
original_ = nullptr;
copy_ = new Project();
copy_->setParent(this);
}
void ProjectCopier::SetProject(Project *project)
{
if (original_) {
// Clear current project
qDeleteAll(created_nodes_);
created_nodes_.clear();
copy_map_.clear();
graph_update_queue_.clear();
if (original_) {
// Clear current project
qDeleteAll(created_nodes_);
created_nodes_.clear();
copy_map_.clear();
graph_update_queue_.clear();
disconnect(original_, &Project::NodeAdded, this, &ProjectCopier::QueueNodeAdd);
disconnect(original_, &Project::NodeRemoved, this, &ProjectCopier::QueueNodeRemove);
disconnect(original_, &Project::InputConnected, this, &ProjectCopier::QueueEdgeAdd);
disconnect(original_, &Project::InputDisconnected, this, &ProjectCopier::QueueEdgeRemove);
disconnect(original_, &Project::ValueChanged, this, &ProjectCopier::QueueValueChange);
disconnect(original_, &Project::InputValueHintChanged, this, &ProjectCopier::QueueValueHintChange);
disconnect(original_, &Project::SettingChanged, this, &ProjectCopier::QueueProjectSettingChange);
}
disconnect(original_, &Project::NodeAdded, this,
&ProjectCopier::QueueNodeAdd);
disconnect(original_, &Project::NodeRemoved, this,
&ProjectCopier::QueueNodeRemove);
disconnect(original_, &Project::InputConnected, this,
&ProjectCopier::QueueEdgeAdd);
disconnect(original_, &Project::InputDisconnected, this,
&ProjectCopier::QueueEdgeRemove);
disconnect(original_, &Project::ValueChanged, this,
&ProjectCopier::QueueValueChange);
disconnect(original_, &Project::InputValueHintChanged, this,
&ProjectCopier::QueueValueHintChange);
disconnect(original_, &Project::SettingChanged, this,
&ProjectCopier::QueueProjectSettingChange);
}
original_ = project;
original_ = project;
if (original_) {
// Add all nodes
for (int i=0; i<copy_->nodes().size(); i++) {
InsertIntoCopyMap(original_->nodes().at(i), copy_->nodes().at(i));
}
if (original_) {
// Add all nodes
for (int i = 0; i < copy_->nodes().size(); i++) {
InsertIntoCopyMap(original_->nodes().at(i), copy_->nodes().at(i));
}
for (int i=copy_->nodes().size(); i<original_->nodes().size(); i++) {
DoNodeAdd(original_->nodes().at(i));
}
for (int i = copy_->nodes().size(); i < original_->nodes().size();
i++) {
DoNodeAdd(original_->nodes().at(i));
}
// Add all connections
foreach (Node* node, original_->nodes()) {
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
DoEdgeAdd(it->second, it->first);
}
}
// Add all connections
foreach (Node *node, original_->nodes()) {
for (auto it = node->input_connections().cbegin();
it != node->input_connections().cend(); it++) {
DoEdgeAdd(it->second, it->first);
}
}
// Copy project settings
Project::CopySettings(original_, copy_);
// Copy project settings
Project::CopySettings(original_, copy_);
// Ensure graph change value is just before the sync value
UpdateGraphChangeValue();
UpdateLastSyncedValue();
// Ensure graph change value is just before the sync value
UpdateGraphChangeValue();
UpdateLastSyncedValue();
// Connect signals for future node additions/deletions
connect(original_, &Project::NodeAdded, this, &ProjectCopier::QueueNodeAdd, Qt::DirectConnection);
connect(original_, &Project::NodeRemoved, this, &ProjectCopier::QueueNodeRemove, Qt::DirectConnection);
connect(original_, &Project::InputConnected, this, &ProjectCopier::QueueEdgeAdd, Qt::DirectConnection);
connect(original_, &Project::InputDisconnected, this, &ProjectCopier::QueueEdgeRemove, Qt::DirectConnection);
connect(original_, &Project::ValueChanged, this, &ProjectCopier::QueueValueChange, Qt::DirectConnection);
connect(original_, &Project::InputValueHintChanged, this, &ProjectCopier::QueueValueHintChange, Qt::DirectConnection);
connect(original_, &Project::SettingChanged, this, &ProjectCopier::QueueProjectSettingChange, Qt::DirectConnection);
}
// Connect signals for future node additions/deletions
connect(original_, &Project::NodeAdded, this,
&ProjectCopier::QueueNodeAdd, Qt::DirectConnection);
connect(original_, &Project::NodeRemoved, this,
&ProjectCopier::QueueNodeRemove, Qt::DirectConnection);
connect(original_, &Project::InputConnected, this,
&ProjectCopier::QueueEdgeAdd, Qt::DirectConnection);
connect(original_, &Project::InputDisconnected, this,
&ProjectCopier::QueueEdgeRemove, Qt::DirectConnection);
connect(original_, &Project::ValueChanged, this,
&ProjectCopier::QueueValueChange, Qt::DirectConnection);
connect(original_, &Project::InputValueHintChanged, this,
&ProjectCopier::QueueValueHintChange, Qt::DirectConnection);
connect(original_, &Project::SettingChanged, this,
&ProjectCopier::QueueProjectSettingChange,
Qt::DirectConnection);
}
}
void ProjectCopier::ProcessUpdateQueue()
{
// Iterate everything that happened to the graph and do the same thing on our end
while (!graph_update_queue_.empty()) {
QueuedJob job = graph_update_queue_.front();
graph_update_queue_.pop_front();
// Iterate everything that happened to the graph and do the same thing on our end
while (!graph_update_queue_.empty()) {
QueuedJob job = graph_update_queue_.front();
graph_update_queue_.pop_front();
switch (job.type) {
case QueuedJob::kNodeAdded:
DoNodeAdd(job.node);
break;
case QueuedJob::kNodeRemoved:
DoNodeRemove(job.node);
break;
case QueuedJob::kEdgeAdded:
DoEdgeAdd(job.output, job.input);
break;
case QueuedJob::kEdgeRemoved:
DoEdgeRemove(job.output, job.input);
break;
case QueuedJob::kValueChanged:
DoValueChange(job.input);
break;
case QueuedJob::kValueHintChanged:
DoValueHintChange(job.input);
break;
case QueuedJob::kProjectSettingChanged:
DoProjectSettingChange(job.key, job.value);
break;
}
}
switch (job.type) {
case QueuedJob::kNodeAdded:
DoNodeAdd(job.node);
break;
case QueuedJob::kNodeRemoved:
DoNodeRemove(job.node);
break;
case QueuedJob::kEdgeAdded:
DoEdgeAdd(job.output, job.input);
break;
case QueuedJob::kEdgeRemoved:
DoEdgeRemove(job.output, job.input);
break;
case QueuedJob::kValueChanged:
DoValueChange(job.input);
break;
case QueuedJob::kValueHintChanged:
DoValueHintChange(job.input);
break;
case QueuedJob::kProjectSettingChanged:
DoProjectSettingChange(job.key, job.value);
break;
}
}
// Indicate that we have synchronized to this point, which is compared with the graph change
// time to see if our copied graph is up to date
UpdateLastSyncedValue();
// Indicate that we have synchronized to this point, which is compared with the graph change
// time to see if our copied graph is up to date
UpdateLastSyncedValue();
}
void ProjectCopier::DoNodeAdd(Node *node)
{
if (dynamic_cast<NodeGroup*>(node)) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
if (dynamic_cast<NodeGroup *>(node)) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy node
Node* copy = node->copy();
// Copy node
Node *copy = node->copy();
// Add to project
copy->setParent(copy_);
// Add to project
copy->setParent(copy_);
// Disable caches for copy
copy->SetCachesEnabled(false);
// Disable caches for copy
copy->SetCachesEnabled(false);
// Copy cache UUIDs
copy->CopyCacheUuidsFrom(node);
// Copy cache UUIDs
copy->CopyCacheUuidsFrom(node);
// Insert into map
InsertIntoCopyMap(node, copy);
// Insert into map
InsertIntoCopyMap(node, copy);
// Keep track of our nodes
created_nodes_.append(copy);
// Keep track of our nodes
created_nodes_.append(copy);
}
void ProjectCopier::DoNodeRemove(Node *node)
{
// Find our copy and remove it
Node* copy = copy_map_.take(node);
// Find our copy and remove it
Node *copy = copy_map_.take(node);
// Disconnect from node's caches
emit RemovedNode(node);
// Disconnect from node's caches
emit RemovedNode(node);
// Remove from created list
created_nodes_.removeOne(copy);
// Remove from created list
created_nodes_.removeOne(copy);
// Delete it
delete copy;
// Delete it
delete copy;
}
void ProjectCopier::DoEdgeAdd(Node *output, const NodeInput &input)
{
// Create same connection with our copied graph
Node* our_output = copy_map_.value(output);
Node* our_input = copy_map_.value(input.node());
// Create same connection with our copied graph
Node *our_output = copy_map_.value(output);
Node *our_input = copy_map_.value(input.node());
Node::ConnectEdge(our_output, NodeInput(our_input, input.input(), input.element()));
Node::ConnectEdge(our_output,
NodeInput(our_input, input.input(), input.element()));
}
void ProjectCopier::DoEdgeRemove(Node *output, const NodeInput &input)
{
// Remove same connection with our copied graph
Node* our_output = copy_map_.value(output);
Node* our_input = copy_map_.value(input.node());
// Remove same connection with our copied graph
Node *our_output = copy_map_.value(output);
Node *our_input = copy_map_.value(input.node());
Node::DisconnectEdge(our_output, NodeInput(our_input, input.input(), input.element()));
Node::DisconnectEdge(our_output,
NodeInput(our_input, input.input(), input.element()));
}
void ProjectCopier::DoValueChange(const NodeInput &input)
{
if (dynamic_cast<NodeGroup*>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
if (dynamic_cast<NodeGroup *>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy all values to our graph
Node* our_input = copy_map_.value(input.node());
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
// Copy all values to our graph
Node *our_input = copy_map_.value(input.node());
Node::CopyValuesOfElement(input.node(), our_input, input.input(),
input.element());
}
void ProjectCopier::DoValueHintChange(const NodeInput &input)
{
if (dynamic_cast<NodeGroup*>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
if (dynamic_cast<NodeGroup *>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy value hint to our graph
Node* our_input = copy_map_.value(input.node());
Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element());
our_input->SetValueHintForInput(input.input(), hint, input.element());
// Copy value hint to our graph
Node *our_input = copy_map_.value(input.node());
Node::ValueHint hint =
input.node()->GetValueHintForInput(input.input(), input.element());
our_input->SetValueHintForInput(input.input(), hint, input.element());
}
void ProjectCopier::DoProjectSettingChange(const QString &key, const QString &value)
void ProjectCopier::DoProjectSettingChange(const QString &key,
const QString &value)
{
copy_->SetSetting(key, value);
copy_->SetSetting(key, value);
}
void ProjectCopier::InsertIntoCopyMap(Node *node, Node *copy)
{
// Insert into map
copy_map_.insert(node, copy);
// Insert into map
copy_map_.insert(node, copy);
// Copy parameters
Node::CopyInputs(node, copy, false);
// Copy parameters
Node::CopyInputs(node, copy, false);
// Connect to node's cache
emit AddedNode(node);
// Connect to node's cache
emit AddedNode(node);
}
void ProjectCopier::QueueNodeAdd(Node *node)
{
graph_update_queue_.push_back({QueuedJob::kNodeAdded, node, NodeInput(), nullptr, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kNodeAdded, node, NodeInput(),
nullptr, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueNodeRemove(Node *node)
{
graph_update_queue_.push_back({QueuedJob::kNodeRemoved, node, NodeInput(), nullptr, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kNodeRemoved, node, NodeInput(),
nullptr, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueEdgeAdd(Node *output, const NodeInput &input)
{
graph_update_queue_.push_back({QueuedJob::kEdgeAdded, nullptr, input, output, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kEdgeAdded, nullptr, input,
output, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueEdgeRemove(Node *output, const NodeInput &input)
{
graph_update_queue_.push_back({QueuedJob::kEdgeRemoved, nullptr, input, output, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kEdgeRemoved, nullptr, input,
output, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueValueChange(const NodeInput &input)
{
/*for (auto it = graph_update_queue_.begin(); it != graph_update_queue_.end(); ) {
/*for (auto it = graph_update_queue_.begin(); it != graph_update_queue_.end(); ) {
if (it->type == QueuedJob::kValueChanged && it->input == input) {
it = graph_update_queue_.erase(it);
} else {
@@ -259,30 +286,34 @@ void ProjectCopier::QueueValueChange(const NodeInput &input)
}
}*/
graph_update_queue_.push_back({QueuedJob::kValueChanged, nullptr, input, nullptr, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kValueChanged, nullptr, input,
nullptr, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueValueHintChange(const NodeInput &input)
{
graph_update_queue_.push_back({QueuedJob::kValueHintChanged, nullptr, input, nullptr, QString(), QString()});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kValueHintChanged, nullptr,
input, nullptr, QString(), QString() });
UpdateGraphChangeValue();
}
void ProjectCopier::QueueProjectSettingChange(const QString &key, const QString &value)
void ProjectCopier::QueueProjectSettingChange(const QString &key,
const QString &value)
{
graph_update_queue_.push_back({QueuedJob::kProjectSettingChanged, nullptr, NodeInput(), nullptr, key, value});
UpdateGraphChangeValue();
graph_update_queue_.push_back({ QueuedJob::kProjectSettingChanged, nullptr,
NodeInput(), nullptr, key, value });
UpdateGraphChangeValue();
}
void ProjectCopier::UpdateGraphChangeValue()
{
graph_changed_time_.Acquire();
graph_changed_time_.Acquire();
}
void ProjectCopier::UpdateLastSyncedValue()
{
last_update_time_.Acquire();
last_update_time_.Acquire();
}
}
+81 -69
View File
@@ -23,110 +23,122 @@
#include "node/project.h"
namespace olive {
class ProjectCopier : public QObject
namespace olive
{
Q_OBJECT
class ProjectCopier : public QObject {
Q_OBJECT
public:
ProjectCopier(QObject *parent = nullptr);
ProjectCopier(QObject *parent = nullptr);
void SetProject(Project *project);
void SetProject(Project *project);
template <typename T>
T *GetCopy(T *original)
{
return static_cast<T*>(copy_map_.value(original));
}
template <typename T> T *GetCopy(T *original)
{
return static_cast<T *>(copy_map_.value(original));
}
template <typename T>
T *GetOriginal(T *copy)
{
return static_cast<T*>(copy_map_.key(copy));
}
template <typename T> T *GetOriginal(T *copy)
{
return static_cast<T *>(copy_map_.key(copy));
}
Project *GetCopiedProject() const { return copy_; }
Project *GetCopiedProject() const
{
return copy_;
}
const QHash<Node*, Node*> &GetNodeMap() const { return copy_map_; }
const QHash<Node *, Node *> &GetNodeMap() const
{
return copy_map_;
}
const JobTime &GetGraphChangeTime() const { return graph_changed_time_; }
const JobTime &GetLastUpdateTime() const { return last_update_time_; }
const JobTime &GetGraphChangeTime() const
{
return graph_changed_time_;
}
const JobTime &GetLastUpdateTime() const
{
return last_update_time_;
}
bool HasUpdatesInQueue() const { return !graph_update_queue_.empty(); }
bool HasUpdatesInQueue() const
{
return !graph_update_queue_.empty();
}
/**
/**
* @brief Process all changes to internal NodeGraph copy
*
* PreviewAutoCacher staggers updates to its internal NodeGraph copy, only applying them when the
* RenderManager is not reading from it. This function is called when such an opportunity arises.
*/
void ProcessUpdateQueue();
void ProcessUpdateQueue();
signals:
void AddedNode(Node *n);
void RemovedNode(Node *n);
void AddedNode(Node *n);
void RemovedNode(Node *n);
private:
void DoNodeAdd(Node* node);
void DoNodeRemove(Node* node);
void DoEdgeAdd(Node *output, const NodeInput& input);
void DoEdgeRemove(Node *output, const NodeInput& input);
void DoValueChange(const NodeInput& input);
void DoValueHintChange(const NodeInput &input);
void DoProjectSettingChange(const QString &key, const QString &value);
void DoNodeAdd(Node *node);
void DoNodeRemove(Node *node);
void DoEdgeAdd(Node *output, const NodeInput &input);
void DoEdgeRemove(Node *output, const NodeInput &input);
void DoValueChange(const NodeInput &input);
void DoValueHintChange(const NodeInput &input);
void DoProjectSettingChange(const QString &key, const QString &value);
void InsertIntoCopyMap(Node* node, Node* copy);
void InsertIntoCopyMap(Node *node, Node *copy);
void UpdateGraphChangeValue();
void UpdateLastSyncedValue();
void UpdateGraphChangeValue();
void UpdateLastSyncedValue();
Project *original_;
Project *copy_;
Project *original_;
Project *copy_;
class QueuedJob {
public:
enum Type {
kNodeAdded,
kNodeRemoved,
kEdgeAdded,
kEdgeRemoved,
kValueChanged,
kValueHintChanged,
kProjectSettingChanged
};
class QueuedJob {
public:
enum Type {
kNodeAdded,
kNodeRemoved,
kEdgeAdded,
kEdgeRemoved,
kValueChanged,
kValueHintChanged,
kProjectSettingChanged
};
Type type;
Node* node;
NodeInput input;
Node *output;
Type type;
Node *node;
NodeInput input;
Node *output;
QString key;
QString value;
};
QString key;
QString value;
};
std::list<QueuedJob> graph_update_queue_;
QHash<Node*, Node*> copy_map_;
QHash<Project*, Project*> graph_map_;
QVector<Node*> created_nodes_;
std::list<QueuedJob> graph_update_queue_;
QHash<Node *, Node *> copy_map_;
QHash<Project *, Project *> graph_map_;
QVector<Node *> created_nodes_;
JobTime graph_changed_time_;
JobTime last_update_time_;
JobTime graph_changed_time_;
JobTime last_update_time_;
private slots:
void QueueNodeAdd(Node* node);
void QueueNodeAdd(Node *node);
void QueueNodeRemove(Node* node);
void QueueNodeRemove(Node *node);
void QueueEdgeAdd(Node *output, const NodeInput& input);
void QueueEdgeAdd(Node *output, const NodeInput &input);
void QueueEdgeRemove(Node *output, const NodeInput& input);
void QueueEdgeRemove(Node *output, const NodeInput &input);
void QueueValueChange(const NodeInput& input);
void QueueValueChange(const NodeInput &input);
void QueueValueHintChange(const NodeInput &input);
void QueueProjectSettingChange(const QString &key, const QString &value);
void QueueValueHintChange(const NodeInput &input);
void QueueProjectSettingChange(const QString &key, const QString &value);
};
}
+11 -14
View File
@@ -23,26 +23,23 @@
#include "codec/decoder.h"
namespace olive {
template <typename K, typename V>
class RenderCache : public QHash<K, V>
namespace olive
{
template <typename K, typename V> class RenderCache : public QHash<K, V> {
public:
QMutex *mutex()
{
return &mutex_;
}
QMutex *mutex()
{
return &mutex_;
}
private:
QMutex mutex_;
QMutex mutex_;
};
struct DecoderPair
{
DecoderPtr decoder = nullptr;
qint64 last_modified = 0;
struct DecoderPair {
DecoderPtr decoder = nullptr;
qint64 last_modified = 0;
};
using DecoderCache = RenderCache<Decoder::CodecStream, DecoderPair>;
+265 -218
View File
@@ -25,294 +25,341 @@
#include <QTimer>
#include <QVector2D>
namespace olive {
namespace olive
{
Renderer::Renderer(QObject *parent) :
QObject(parent)
Renderer::Renderer(QObject *parent)
: QObject(parent)
{
}
TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data, int linesize)
TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data,
int linesize)
{
QVariant v;
QVariant v;
if (USE_TEXTURE_CACHE) {
QMutexLocker locker(&texture_cache_lock_);
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
if (it->width == params.effective_width()
&& it->height == params.effective_height()
&& it->depth == params.effective_depth()
&& it->format == params.format()
&& it->channel_count == params.channel_count()) {
v = it->handle;
texture_cache_.erase(it);
break;
}
}
}
if (USE_TEXTURE_CACHE) {
QMutexLocker locker(&texture_cache_lock_);
for (auto it = texture_cache_.begin(); it != texture_cache_.end();
it++) {
if (it->width == params.effective_width() &&
it->height == params.effective_height() &&
it->depth == params.effective_depth() &&
it->format == params.format() &&
it->channel_count == params.channel_count()) {
v = it->handle;
texture_cache_.erase(it);
break;
}
}
}
if (v.isNull()) {
v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(),
params.format(), params.channel_count(), data, linesize);
} else if (data) {
UploadToTexture(v, params, data, linesize);
} else {
this->Flush();
}
if (v.isNull()) {
v = CreateNativeTexture(params.effective_width(),
params.effective_height(),
params.effective_depth(), params.format(),
params.channel_count(), data, linesize);
} else if (data) {
UploadToTexture(v, params, data, linesize);
} else {
this->Flush();
}
return CreateTextureFromNativeHandle(v, params);
return CreateTextureFromNativeHandle(v, params);
}
void Renderer::DestroyTexture(Texture *texture)
{
if (USE_TEXTURE_CACHE) {
// HACK: Dirty, dirty hack. OpenGL uses "contexts" to store all of its data, and each context
// can only be used by the thread that created it. However there are also "shared contexts"
// where assets from one context can be used in another. We use shared contexts so that
// textures rendered in the background can be displayed on the screen, travelling from
// a background thread to the main UI thread. However, when that texture is destroyed, it
// comes back here to be placed in the texture cache. But that leads to a race condition
// because it will call the background thread's renderer in the main thread. Since all
// assets are shared, we could technically just get the texture to call "destroy" in the
// viewer's renderer instance, but that would mean all textures would end up stranded
// there unusable by the background renderer, negating the very advantage of the texture
// cache in the first place. Therefore, we simply allow the thread calling to happen, and
// use mutexes to prevent race conditions.
//
// Presumably Vulkan would not have this issue because it allows for application-wide
// instances and multithreading.
texture_cache_lock_.lock();
texture_cache_.push_back({texture->params().effective_width(),
texture->params().effective_height(),
texture->params().effective_depth(),
texture->params().format(),
texture->params().channel_count(),
texture->id(),
QDateTime::currentMSecsSinceEpoch()});
texture_cache_lock_.unlock();
if (USE_TEXTURE_CACHE) {
// HACK: Dirty, dirty hack. OpenGL uses "contexts" to store all of its data, and each context
// can only be used by the thread that created it. However there are also "shared contexts"
// where assets from one context can be used in another. We use shared contexts so that
// textures rendered in the background can be displayed on the screen, travelling from
// a background thread to the main UI thread. However, when that texture is destroyed, it
// comes back here to be placed in the texture cache. But that leads to a race condition
// because it will call the background thread's renderer in the main thread. Since all
// assets are shared, we could technically just get the texture to call "destroy" in the
// viewer's renderer instance, but that would mean all textures would end up stranded
// there unusable by the background renderer, negating the very advantage of the texture
// cache in the first place. Therefore, we simply allow the thread calling to happen, and
// use mutexes to prevent race conditions.
//
// Presumably Vulkan would not have this issue because it allows for application-wide
// instances and multithreading.
texture_cache_lock_.lock();
texture_cache_.push_back(
{ texture->params().effective_width(),
texture->params().effective_height(),
texture->params().effective_depth(), texture->params().format(),
texture->params().channel_count(), texture->id(),
QDateTime::currentMSecsSinceEpoch() });
texture_cache_lock_.unlock();
if (QThread::currentThread() == this->thread()) {
ClearOldTextures();
}
} else {
DestroyNativeTexture(texture->id());
}
if (QThread::currentThread() == this->thread()) {
ClearOldTextures();
}
} else {
DestroyNativeTexture(texture->id());
}
}
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params)
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom,
const VideoParams &params)
{
color_cache_mutex_.lock();
if (interlace_texture_.isNull()) {
interlace_texture_ = CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/interlace.frag"))));
}
color_cache_mutex_.unlock();
color_cache_mutex_.lock();
if (interlace_texture_.isNull()) {
interlace_texture_ =
CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/interlace.frag"))));
}
color_cache_mutex_.unlock();
ShaderJob job;
job.Insert(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
job.Insert(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height())));
ShaderJob job;
job.Insert(QStringLiteral("top_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
job.Insert(QStringLiteral("bottom_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
job.Insert(QStringLiteral("resolution_in"),
NodeValue(NodeValue::kVec2,
QVector2D(params.effective_width(),
params.effective_height())));
TexturePtr output = CreateTexture(params);
TexturePtr output = CreateTexture(params);
BlitToTexture(interlace_texture_, job, output.get());
BlitToTexture(interlace_texture_, job, output.get());
return output;
return output;
}
QVariant Renderer::GetDefaultShader()
{
QMutexLocker locker(&color_cache_mutex_);
QMutexLocker locker(&color_cache_mutex_);
if (default_shader_.isNull()) {
default_shader_ = CreateNativeShader(ShaderCode(QString(), QString()));
}
if (default_shader_.isNull()) {
default_shader_ = CreateNativeShader(ShaderCode(QString(), QString()));
}
return default_shader_;
return default_shader_;
}
void Renderer::Destroy()
{
if (!default_shader_.isNull()) {
DestroyNativeShader(default_shader_);
default_shader_.clear();
}
if (!default_shader_.isNull()) {
DestroyNativeShader(default_shader_);
default_shader_.clear();
}
color_cache_.clear();
color_cache_.clear();
if (!interlace_texture_.isNull()) {
DestroyNativeShader(interlace_texture_);
interlace_texture_.clear();
}
if (!interlace_texture_.isNull()) {
DestroyNativeShader(interlace_texture_);
interlace_texture_.clear();
}
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
DestroyNativeTexture(it->handle);
}
texture_cache_.clear();
for (auto it = texture_cache_.begin(); it != texture_cache_.end(); it++) {
DestroyNativeTexture(it->handle);
}
texture_cache_.clear();
DestroyInternal();
DestroyInternal();
}
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params)
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v,
const VideoParams &params)
{
if (v.isNull()) {
return nullptr;
}
if (v.isNull()) {
return nullptr;
}
return std::make_shared<Texture>(this, v, params);
return std::make_shared<Texture>(this, v, params);
}
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
bool Renderer::GetColorContext(const ColorTransformJob &color_job,
Renderer::ColorContext *ctx)
{
QMutexLocker locker(&color_cache_mutex_);
QMutexLocker locker(&color_cache_mutex_);
ColorContext& color_ctx = *ctx;
ColorContext &color_ctx = *ctx;
QString proc_id = color_job.id();
QString proc_id = color_job.id();
if (color_cache_.contains(proc_id)) {
color_ctx = color_cache_.value(proc_id);
return true;
} else {
// Create shader description
QString ocio_func_name;
if (color_job.GetFunctionName().isEmpty()) {
ocio_func_name = "OCIODisplay";
} else {
ocio_func_name = color_job.GetFunctionName();
}
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
shader_desc->setFunctionName(ocio_func_name.toUtf8());
shader_desc->setResourcePrefix("ocio_");
if (color_cache_.contains(proc_id)) {
color_ctx = color_cache_.value(proc_id);
return true;
} else {
// Create shader description
QString ocio_func_name;
if (color_job.GetFunctionName().isEmpty()) {
ocio_func_name = "OCIODisplay";
} else {
ocio_func_name = color_job.GetFunctionName();
}
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
shader_desc->setFunctionName(ocio_func_name.toUtf8());
shader_desc->setResourcePrefix("ocio_");
// Generate shader
color_job.GetColorProcessor()->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
// Generate shader
color_job.GetColorProcessor()
->GetProcessor()
->getDefaultGPUProcessor()
->extractGpuShaderInfo(shader_desc);
ShaderCode code;
if (const Node *shader_src = color_job.CustomShaderSource()) {
// Use shader code from associated node
code = shader_src->GetShaderCode({color_job.CustomShaderID(), shader_desc->getShaderText()});
} else {
// Generate shader code using OCIO stub and our auto-generated name
code = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag"));
code.set_frag_code(code.frag_code().arg(shader_desc->getShaderText()));
}
ShaderCode code;
if (const Node *shader_src = color_job.CustomShaderSource()) {
// Use shader code from associated node
code = shader_src->GetShaderCode(
{ color_job.CustomShaderID(), shader_desc->getShaderText() });
} else {
// Generate shader code using OCIO stub and our auto-generated name
code = FileFunctions::ReadFileAsString(
QStringLiteral(":shaders/colormanage.frag"));
code.set_frag_code(
code.frag_code().arg(shader_desc->getShaderText()));
}
// Try to compile shader
color_ctx.compiled_shader = CreateNativeShader(code);
// Try to compile shader
color_ctx.compiled_shader = CreateNativeShader(code);
if (color_ctx.compiled_shader.isNull()) {
return false;
}
if (color_ctx.compiled_shader.isNull()) {
return false;
}
color_ctx.lut3d_textures.resize(shader_desc->getNum3DTextures());
for (unsigned int i=0; i<shader_desc->getNum3DTextures(); i++) {
const char* tex_name = nullptr;
const char* sampler_name = nullptr;
unsigned int edge_len = 0;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
color_ctx.lut3d_textures.resize(shader_desc->getNum3DTextures());
for (unsigned int i = 0; i < shader_desc->getNum3DTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int edge_len = 0;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
shader_desc->get3DTexture(i, tex_name, sampler_name, edge_len, interpolation);
shader_desc->get3DTexture(i, tex_name, sampler_name, edge_len,
interpolation);
if (!tex_name || !*tex_name
|| !sampler_name || !*sampler_name
|| !edge_len) {
qCritical() << "3D LUT texture data is corrupted";
return false;
}
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!edge_len) {
qCritical() << "3D LUT texture data is corrupted";
return false;
}
const float* values = nullptr;
shader_desc->get3DTextureValues(i, values);
if (!values) {
qCritical() << "3D LUT texture values are missing";
return false;
}
const float *values = nullptr;
shader_desc->get3DTextureValues(i, values);
if (!values) {
qCritical() << "3D LUT texture values are missing";
return false;
}
// Allocate 3D LUT
color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, PixelFormat::F32, VideoParams::kRGBChannelCount), values);
color_ctx.lut3d_textures[i].name = sampler_name;
color_ctx.lut3d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
}
// Allocate 3D LUT
color_ctx.lut3d_textures[i].texture = CreateTexture(
VideoParams(edge_len, edge_len, edge_len, PixelFormat::F32,
VideoParams::kRGBChannelCount),
values);
color_ctx.lut3d_textures[i].name = sampler_name;
color_ctx.lut3d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_ctx.lut1d_textures.resize(shader_desc->getNumTextures());
for (unsigned int i=0; i<shader_desc->getNumTextures(); i++) {
const char* tex_name = nullptr;
const char* sampler_name = nullptr;
unsigned int width = 0, height = 0;
OCIO::GpuShaderDesc::TextureType channel = OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
OCIO::GpuShaderDesc::TextureDimensions dimensions=OCIO::GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(i, tex_name, sampler_name, width, height, channel, dimensions, interpolation);
color_ctx.lut1d_textures.resize(shader_desc->getNumTextures());
for (unsigned int i = 0; i < shader_desc->getNumTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int width = 0, height = 0;
OCIO::GpuShaderDesc::TextureType channel =
OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
OCIO::GpuShaderDesc::TextureDimensions dimensions =
OCIO::GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
channel, dimensions, interpolation);
if (!tex_name || !*tex_name
|| !sampler_name || !*sampler_name
|| !width) {
qCritical() << "1D LUT texture data is corrupted";
return false;
}
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!width) {
qCritical() << "1D LUT texture data is corrupted";
return false;
}
const float* values = nullptr;
shader_desc->getTextureValues(i, values);
if (!values) {
qCritical() << "1D LUT texture values are missing";
return false;
}
const float *values = nullptr;
shader_desc->getTextureValues(i, values);
if (!values) {
qCritical() << "1D LUT texture values are missing";
return false;
}
// Allocate 1D LUT
color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, PixelFormat::F32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount), values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
}
// Allocate 1D LUT
color_ctx.lut1d_textures[i].texture = CreateTexture(
VideoParams(width, height, PixelFormat::F32,
(channel ==
OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
1 :
VideoParams::kRGBChannelCount),
values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_cache_.insert(proc_id, color_ctx);
color_cache_.insert(proc_id, color_ctx);
return true;
}
return true;
}
}
void Renderer::ClearOldTextures()
{
QMutexLocker locker(&texture_cache_lock_);
QMutexLocker locker(&texture_cache_lock_);
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) {
if (it->accessed < QDateTime::currentMSecsSinceEpoch() - MAX_TEXTURE_LIFE) {
DestroyNativeTexture(it->handle);
it = texture_cache_.erase(it);
} else {
it++;
}
}
for (auto it = texture_cache_.begin(); it != texture_cache_.end();) {
if (it->accessed <
QDateTime::currentMSecsSinceEpoch() - MAX_TEXTURE_LIFE) {
DestroyNativeTexture(it->handle);
it = texture_cache_.erase(it);
} else {
it++;
}
}
}
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams &params)
void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
Texture *destination, const VideoParams &params)
{
ColorContext color_ctx;
if (!GetColorContext(color_job, &color_ctx)) {
return;
}
ColorContext color_ctx;
if (!GetColorContext(color_job, &color_ctx)) {
return;
}
ShaderJob job;
job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture());
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, color_job.GetCropMatrix().inverted()));
job.Insert(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, int(color_job.GetInputAlphaAssociation())));
job.Insert(QStringLiteral("ove_force_opaque"), NodeValue(NodeValue::kBoolean, color_job.GetForceOpaque()));
job.Insert(color_job.GetValues());
ShaderJob job;
job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture());
job.Insert(QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
job.Insert(QStringLiteral("ove_cropmatrix"),
NodeValue(NodeValue::kMatrix,
color_job.GetCropMatrix().inverted()));
job.Insert(QStringLiteral("ove_maintex_alpha"),
NodeValue(NodeValue::kInt,
int(color_job.GetInputAlphaAssociation())));
job.Insert(QStringLiteral("ove_force_opaque"),
NodeValue(NodeValue::kBoolean, color_job.GetForceOpaque()));
job.Insert(color_job.GetValues());
foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT& l, color_ctx.lut1d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT &l, color_ctx.lut3d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT &l, color_ctx.lut1d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
if (destination) {
BlitToTexture(color_ctx.compiled_shader, job, destination, color_job.IsClearDestinationEnabled());
} else {
Blit(color_ctx.compiled_shader, job, params, color_job.IsClearDestinationEnabled());
}
if (destination) {
BlitToTexture(color_ctx.compiled_shader, job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(color_ctx.compiled_shader, job, params,
color_job.IsClearDestinationEnabled());
}
}
}
+93 -84
View File
@@ -32,130 +32,139 @@
#include "render/videoparams.h"
#include "texture.h"
namespace olive {
namespace olive
{
class ShaderJob;
class Renderer : public QObject
{
Q_OBJECT
class Renderer : public QObject {
Q_OBJECT
public:
Renderer(QObject* parent = nullptr);
Renderer(QObject *parent = nullptr);
virtual bool Init() = 0;
virtual bool Init() = 0;
TexturePtr CreateTexture(const VideoParams& params, const void *data = nullptr, int linesize = 0);
TexturePtr CreateTexture(const VideoParams &params,
const void *data = nullptr, int linesize = 0);
void DestroyTexture(Texture *texture);
void DestroyTexture(Texture *texture);
void BlitToTexture(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
bool clear_destination = true)
{
Blit(shader, job, destination, destination->params(), clear_destination);
}
void BlitToTexture(QVariant shader, olive::ShaderJob job,
olive::Texture *destination,
bool clear_destination = true)
{
Blit(shader, job, destination, destination->params(),
clear_destination);
}
void Blit(QVariant shader,
olive::ShaderJob job,
olive::VideoParams params,
bool clear_destination = true)
{
Blit(shader, job, nullptr, params, clear_destination);
}
void Blit(QVariant shader, olive::ShaderJob job, olive::VideoParams params,
bool clear_destination = true)
{
Blit(shader, job, nullptr, params, clear_destination);
}
void BlitColorManaged(const ColorTransformJob &color_job, Texture* destination, const VideoParams &params);
void BlitColorManaged(const ColorTransformJob &job, Texture* destination)
{
BlitColorManaged(job, destination, destination->params());
}
void BlitColorManaged(const ColorTransformJob &job, const VideoParams &params)
{
BlitColorManaged(job, nullptr, params);
}
void BlitColorManaged(const ColorTransformJob &color_job,
Texture *destination, const VideoParams &params);
void BlitColorManaged(const ColorTransformJob &job, Texture *destination)
{
BlitColorManaged(job, destination, destination->params());
}
void BlitColorManaged(const ColorTransformJob &job,
const VideoParams &params)
{
BlitColorManaged(job, nullptr, params);
}
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params);
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom,
const VideoParams &params);
QVariant GetDefaultShader();
QVariant GetDefaultShader();
void Destroy();
void Destroy();
virtual void PostDestroy() = 0;
virtual void PostDestroy() = 0;
virtual void PostInit() = 0;
virtual void PostInit() = 0;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 1.0) = 0;
virtual void ClearDestination(olive::Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 1.0) = 0;
virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0;
virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0;
virtual void DestroyNativeShader(QVariant shader) = 0;
virtual void DestroyNativeShader(QVariant shader) = 0;
virtual void UploadToTexture(const QVariant &handle, const VideoParams &params, const void* data, int linesize) = 0;
virtual void UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize) = 0;
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams &params, void* data, int linesize) = 0;
virtual void DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize) = 0;
virtual void Flush() = 0;
virtual void Flush() = 0;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0;
virtual Color GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt) = 0;
protected:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
olive::VideoParams destination_params,
bool clear_destination) = 0;
virtual void Blit(QVariant shader, olive::ShaderJob job,
olive::Texture *destination,
olive::VideoParams destination_params,
bool clear_destination) = 0;
virtual QVariant CreateNativeTexture(int width, int height, int depth, PixelFormat format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
virtual QVariant CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data = nullptr,
int linesize = 0) = 0;
virtual void DestroyNativeTexture(QVariant texture) = 0;
virtual void DestroyNativeTexture(QVariant texture) = 0;
virtual void DestroyInternal() = 0;
virtual void DestroyInternal() = 0;
private:
struct ColorContext {
struct LUT {
TexturePtr texture;
Texture::Interpolation interpolation;
QString name;
};
struct ColorContext {
struct LUT {
TexturePtr texture;
Texture::Interpolation interpolation;
QString name;
};
QVariant compiled_shader;
QVector<LUT> lut3d_textures;
QVector<LUT> lut1d_textures;
QVariant compiled_shader;
QVector<LUT> lut3d_textures;
QVector<LUT> lut1d_textures;
};
};
TexturePtr CreateTextureFromNativeHandle(const QVariant &v,
const VideoParams &params);
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params);
bool GetColorContext(const ColorTransformJob &color_job, ColorContext *ctx);
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
void ClearOldTextures();
void ClearOldTextures();
QHash<QString, ColorContext> color_cache_;
QHash<QString, ColorContext> color_cache_;
struct CachedTexture {
int width;
int height;
int depth;
PixelFormat format;
int channel_count;
QVariant handle;
qint64 accessed;
};
struct CachedTexture
{
int width;
int height;
int depth;
PixelFormat format;
int channel_count;
QVariant handle;
qint64 accessed;
};
static const int MAX_TEXTURE_LIFE = 5000;
static const bool USE_TEXTURE_CACHE = true;
std::list<CachedTexture> texture_cache_;
static const int MAX_TEXTURE_LIFE = 5000;
static const bool USE_TEXTURE_CACHE = true;
std::list<CachedTexture> texture_cache_;
QMutex color_cache_mutex_;
QMutex color_cache_mutex_;
QVariant default_shader_;
QVariant default_shader_;
QVariant interlace_texture_;
QMutex texture_cache_lock_;
QVariant interlace_texture_;
QMutex texture_cache_lock_;
};
}
+27 -24
View File
@@ -20,52 +20,55 @@
#include "renderjobtracker.h"
namespace olive {
namespace olive
{
void RenderJobTracker::insert(const TimeRange &range, JobTime job_time)
{
// First remove any ranges with this (code copied
TimeRangeList::util_remove(&jobs_, range);
// First remove any ranges with this (code copied
TimeRangeList::util_remove(&jobs_, range);
// Now append the job
TimeRangeWithJob job(range, job_time);
jobs_.push_back(job);
// Now append the job
TimeRangeWithJob job(range, job_time);
jobs_.push_back(job);
}
void RenderJobTracker::insert(const TimeRangeList &ranges, JobTime job_time)
{
foreach (const TimeRange &r, ranges) {
insert(r, job_time);
}
foreach (const TimeRange &r, ranges) {
insert(r, job_time);
}
}
void RenderJobTracker::clear()
{
jobs_.clear();
jobs_.clear();
}
bool RenderJobTracker::isCurrent(const rational &time, JobTime job_time) const
{
for (auto it=jobs_.crbegin(); it!=jobs_.crend(); it++) {
if (it->Contains(time)) {
return job_time >= it->GetJobTime();
}
}
for (auto it = jobs_.crbegin(); it != jobs_.crend(); it++) {
if (it->Contains(time)) {
return job_time >= it->GetJobTime();
}
}
return false;
return false;
}
TimeRangeList RenderJobTracker::getCurrentSubRanges(const TimeRange &range, const JobTime &job_time) const
TimeRangeList
RenderJobTracker::getCurrentSubRanges(const TimeRange &range,
const JobTime &job_time) const
{
TimeRangeList current_ranges;
TimeRangeList current_ranges;
for (auto it=jobs_.crbegin(); it!=jobs_.crend(); it++) {
if (job_time >= it->GetJobTime() && it->OverlapsWith(range)) {
current_ranges.insert(it->Intersected(range));
}
}
for (auto it = jobs_.crbegin(); it != jobs_.crend(); it++) {
if (job_time >= it->GetJobTime() && it->OverlapsWith(range)) {
current_ranges.insert(it->Intersected(range));
}
}
return current_ranges;
return current_ranges;
}
}
+30 -26
View File
@@ -25,45 +25,49 @@
#include "common/jobtime.h"
namespace olive {
namespace olive
{
using namespace core;
class RenderJobTracker
{
class RenderJobTracker {
public:
RenderJobTracker() = default;
RenderJobTracker() = default;
void insert(const TimeRange &range, JobTime job_time);
void insert(const TimeRangeList &ranges, JobTime job_time);
void insert(const TimeRange &range, JobTime job_time);
void insert(const TimeRangeList &ranges, JobTime job_time);
void clear();
void clear();
bool isCurrent(const rational &time, JobTime job_time) const;
bool isCurrent(const rational &time, JobTime job_time) const;
TimeRangeList getCurrentSubRanges(const TimeRange &range, const JobTime &job_time) const;
TimeRangeList getCurrentSubRanges(const TimeRange &range,
const JobTime &job_time) const;
private:
class TimeRangeWithJob : public TimeRange
{
public:
TimeRangeWithJob() = default;
TimeRangeWithJob(const TimeRange &range, const JobTime &job_time)
{
set_range(range.in(), range.out());
job_time_ = job_time;
}
class TimeRangeWithJob : public TimeRange {
public:
TimeRangeWithJob() = default;
TimeRangeWithJob(const TimeRange &range, const JobTime &job_time)
{
set_range(range.in(), range.out());
job_time_ = job_time;
}
JobTime GetJobTime() const {return job_time_;}
void SetJobTime(JobTime jt) {job_time_ = jt;}
JobTime GetJobTime() const
{
return job_time_;
}
void SetJobTime(JobTime jt)
{
job_time_ = jt;
}
private:
JobTime job_time_;
};
std::vector<TimeRangeWithJob> jobs_;
private:
JobTime job_time_;
};
std::vector<TimeRangeWithJob> jobs_;
};
}
+172 -163
View File
@@ -32,248 +32,257 @@
#include "task/taskmanager.h"
#include "window/mainwindow/mainwindow.h"
namespace olive {
namespace olive
{
RenderManager* RenderManager::instance_ = nullptr;
RenderManager *RenderManager::instance_ = nullptr;
const rational RenderManager::kDryRunInterval = rational(10);
RenderManager::RenderManager(QObject *parent) :
backend_(kOpenGL),
aggressive_gc_(0)
RenderManager::RenderManager(QObject *parent)
: backend_(kOpenGL)
, aggressive_gc_(0)
{
if (backend_ == kOpenGL) {
context_ = new OpenGLRenderer();
decoder_cache_ = new DecoderCache();
shader_cache_ = new ShaderCache();
} else {
qCritical() << "Tried to initialize unknown graphics backend";
context_ = nullptr;
decoder_cache_ = nullptr;
}
if (backend_ == kOpenGL) {
context_ = new OpenGLRenderer();
decoder_cache_ = new DecoderCache();
shader_cache_ = new ShaderCache();
} else {
qCritical() << "Tried to initialize unknown graphics backend";
context_ = nullptr;
decoder_cache_ = nullptr;
}
if (context_) {
video_thread_ = CreateThread(context_);
dry_run_thread_ = CreateThread();
audio_thread_ = CreateThread();
if (context_) {
video_thread_ = CreateThread(context_);
dry_run_thread_ = CreateThread();
audio_thread_ = CreateThread();
waveform_threads_.resize(QThread::idealThreadCount());
for (size_t i=0; i<waveform_threads_.size(); i++) {
waveform_threads_[i] = CreateThread();
}
waveform_threads_.resize(QThread::idealThreadCount());
for (size_t i = 0; i < waveform_threads_.size(); i++) {
waveform_threads_[i] = CreateThread();
}
auto_cacher_ = new PreviewAutoCacher(this);
}
auto_cacher_ = new PreviewAutoCacher(this);
}
decoder_clear_timer_ = new QTimer(this);
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
connect(decoder_clear_timer_, &QTimer::timeout, this, &RenderManager::ClearOldDecoders);
decoder_clear_timer_->start();
decoder_clear_timer_ = new QTimer(this);
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
connect(decoder_clear_timer_, &QTimer::timeout, this,
&RenderManager::ClearOldDecoders);
decoder_clear_timer_->start();
}
RenderManager::~RenderManager()
{
if (context_) {
delete shader_cache_;
delete decoder_cache_;
if (context_) {
delete shader_cache_;
delete decoder_cache_;
for (RenderThread *rt : render_threads_) {
rt->quit();
rt->wait();
}
for (RenderThread *rt : render_threads_) {
rt->quit();
rt->wait();
}
context_->PostDestroy();
delete context_;
}
context_->PostDestroy();
delete context_;
}
}
RenderThread *RenderManager::CreateThread(Renderer *renderer)
{
auto t = new RenderThread(renderer, decoder_cache_, shader_cache_, this);
render_threads_.push_back(t);
t->start(QThread::IdlePriority);
return t;
auto t = new RenderThread(renderer, decoder_cache_, shader_cache_, this);
render_threads_.push_back(t);
t->start(QThread::IdlePriority);
return t;
}
RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams &params)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
ticket->setProperty("node", QtUtils::PtrToValue(params.node));
ticket->setProperty("time", QVariant::fromValue(params.time));
ticket->setProperty("size", params.force_size);
ticket->setProperty("matrix", params.force_matrix);
ticket->setProperty("format", static_cast<PixelFormat::Format>(params.force_format));
ticket->setProperty("usecache", params.use_cache);
ticket->setProperty("channelcount", params.force_channel_count);
ticket->setProperty("mode", params.mode);
ticket->setProperty("type", kTypeVideo);
ticket->setProperty("colormanager", QtUtils::PtrToValue(params.color_manager));
ticket->setProperty("coloroutput", QVariant::fromValue(params.force_color_output));
Q_ASSERT(params.video_params.is_valid());
ticket->setProperty("vparam", QVariant::fromValue(params.video_params));
ticket->setProperty("aparam", QVariant::fromValue(params.audio_params));
ticket->setProperty("return", params.return_type);
ticket->setProperty("cache", params.cache_dir);
ticket->setProperty("cachetimebase", QVariant::fromValue(params.cache_timebase));
ticket->setProperty("cacheid", QVariant::fromValue(params.cache_id));
ticket->setProperty("multicam", QtUtils::PtrToValue(params.multicam));
ticket->setProperty("node", QtUtils::PtrToValue(params.node));
ticket->setProperty("time", QVariant::fromValue(params.time));
ticket->setProperty("size", params.force_size);
ticket->setProperty("matrix", params.force_matrix);
ticket->setProperty("format",
static_cast<PixelFormat::Format>(params.force_format));
ticket->setProperty("usecache", params.use_cache);
ticket->setProperty("channelcount", params.force_channel_count);
ticket->setProperty("mode", params.mode);
ticket->setProperty("type", kTypeVideo);
ticket->setProperty("colormanager",
QtUtils::PtrToValue(params.color_manager));
ticket->setProperty("coloroutput",
QVariant::fromValue(params.force_color_output));
Q_ASSERT(params.video_params.is_valid());
ticket->setProperty("vparam", QVariant::fromValue(params.video_params));
ticket->setProperty("aparam", QVariant::fromValue(params.audio_params));
ticket->setProperty("return", params.return_type);
ticket->setProperty("cache", params.cache_dir);
ticket->setProperty("cachetimebase",
QVariant::fromValue(params.cache_timebase));
ticket->setProperty("cacheid", QVariant::fromValue(params.cache_id));
ticket->setProperty("multicam", QtUtils::PtrToValue(params.multicam));
if (params.return_type == ReturnType::kNull) {
dry_run_thread_->AddTicket(ticket);
} else {
video_thread_->AddTicket(ticket);
}
if (params.return_type == ReturnType::kNull) {
dry_run_thread_->AddTicket(ticket);
} else {
video_thread_->AddTicket(ticket);
}
return ticket;
return ticket;
}
RenderTicketPtr RenderManager::RenderAudio(const RenderAudioParams &params)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
ticket->setProperty("node", QtUtils::PtrToValue(params.node));
ticket->setProperty("time", QVariant::fromValue(params.range));
ticket->setProperty("type", kTypeAudio);
ticket->setProperty("enablewaveforms", params.generate_waveforms);
ticket->setProperty("clamp", params.clamp);
ticket->setProperty("aparam", QVariant::fromValue(params.audio_params));
ticket->setProperty("mode", params.mode);
ticket->setProperty("node", QtUtils::PtrToValue(params.node));
ticket->setProperty("time", QVariant::fromValue(params.range));
ticket->setProperty("type", kTypeAudio);
ticket->setProperty("enablewaveforms", params.generate_waveforms);
ticket->setProperty("clamp", params.clamp);
ticket->setProperty("aparam", QVariant::fromValue(params.audio_params));
ticket->setProperty("mode", params.mode);
if (params.generate_waveforms) {
size_t thread_index = last_waveform_thread_%waveform_threads_.size();
RenderThread *thread = waveform_threads_[thread_index];
thread->AddTicket(ticket);
last_waveform_thread_++;
} else {
audio_thread_->AddTicket(ticket);
}
if (params.generate_waveforms) {
size_t thread_index = last_waveform_thread_ % waveform_threads_.size();
RenderThread *thread = waveform_threads_[thread_index];
thread->AddTicket(ticket);
last_waveform_thread_++;
} else {
audio_thread_->AddTicket(ticket);
}
return ticket;
return ticket;
}
bool RenderManager::RemoveTicket(RenderTicketPtr ticket)
{
for (RenderThread *rt : render_threads_) {
if (rt->RemoveTicket(ticket)) {
return true;
}
}
for (RenderThread *rt : render_threads_) {
if (rt->RemoveTicket(ticket)) {
return true;
}
}
return false;
return false;
}
void RenderManager::SetAggressiveGarbageCollection(bool enabled)
{
aggressive_gc_ += enabled ? 1 : -1;
aggressive_gc_ += enabled ? 1 : -1;
if (aggressive_gc_ > 0) {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivityAggressive);
} else {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
}
if (aggressive_gc_ > 0) {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivityAggressive);
} else {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
}
}
void RenderManager::ClearOldDecoders()
{
QMutexLocker locker(decoder_cache_->mutex());
QMutexLocker locker(decoder_cache_->mutex());
qint64 min_age = QDateTime::currentMSecsSinceEpoch() - kDecoderMaximumInactivity;
qint64 min_age =
QDateTime::currentMSecsSinceEpoch() - kDecoderMaximumInactivity;
for (auto it=decoder_cache_->begin(); it!=decoder_cache_->end(); ) {
DecoderPair decoder = it.value();
for (auto it = decoder_cache_->begin(); it != decoder_cache_->end();) {
DecoderPair decoder = it.value();
if (decoder.decoder->GetLastAccessedTime() < min_age) {
decoder.decoder->Close();
it = decoder_cache_->erase(it);
} else {
it++;
}
}
if (decoder.decoder->GetLastAccessedTime() < min_age) {
decoder.decoder->Close();
it = decoder_cache_->erase(it);
} else {
it++;
}
}
}
RenderThread::RenderThread(Renderer *renderer, DecoderCache *decoder_cache, ShaderCache *shader_cache, QObject *parent) :
QThread(parent),
cancelled_(false),
context_(renderer),
decoder_cache_(decoder_cache),
shader_cache_(shader_cache)
RenderThread::RenderThread(Renderer *renderer, DecoderCache *decoder_cache,
ShaderCache *shader_cache, QObject *parent)
: QThread(parent)
, cancelled_(false)
, context_(renderer)
, decoder_cache_(decoder_cache)
, shader_cache_(shader_cache)
{
if (context_) {
context_->Init();
context_->moveToThread(this);
}
if (context_) {
context_->Init();
context_->moveToThread(this);
}
}
void RenderThread::AddTicket(RenderTicketPtr ticket)
{
QMutexLocker locker(&mutex_);
ticket->moveToThread(this);
queue_.push_back(ticket);
wait_.wakeOne();
QMutexLocker locker(&mutex_);
ticket->moveToThread(this);
queue_.push_back(ticket);
wait_.wakeOne();
}
bool RenderThread::RemoveTicket(RenderTicketPtr ticket)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
auto it = std::find(queue_.begin(), queue_.end(), ticket);
if (it == queue_.end()) {
return false;
}
auto it = std::find(queue_.begin(), queue_.end(), ticket);
if (it == queue_.end()) {
return false;
}
queue_.erase(it);
return true;
queue_.erase(it);
return true;
}
void RenderThread::quit()
{
QMutexLocker locker(&mutex_);
cancelled_ = true;
wait_.wakeOne();
QMutexLocker locker(&mutex_);
cancelled_ = true;
wait_.wakeOne();
}
void RenderThread::run()
{
if (context_) {
context_->PostInit();
}
if (context_) {
context_->PostInit();
}
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
while (!cancelled_) {
if (queue_.empty()) {
wait_.wait(&mutex_);
}
while (!cancelled_) {
if (queue_.empty()) {
wait_.wait(&mutex_);
}
if (cancelled_) {
break;
}
if (cancelled_) {
break;
}
if (!queue_.empty()) {
RenderTicketPtr ticket = queue_.front();
queue_.pop_front();
if (!queue_.empty()) {
RenderTicketPtr ticket = queue_.front();
queue_.pop_front();
locker.unlock();
locker.unlock();
// Setup the ticket for ::Process
ticket->Start();
// Setup the ticket for ::Process
ticket->Start();
if (ticket->IsCancelled()) {
ticket->Finish();
} else {
RenderProcessor::Process(ticket, context_, decoder_cache_, shader_cache_);
}
if (ticket->IsCancelled()) {
ticket->Finish();
} else {
RenderProcessor::Process(ticket, context_, decoder_cache_,
shader_cache_);
}
locker.relock();
}
}
locker.relock();
}
}
if (context_) {
context_->Destroy();
context_->moveToThread(this->thread());
}
if (context_) {
context_->Destroy();
context_->moveToThread(this->thread());
}
}
}
+140 -147
View File
@@ -34,124 +34,120 @@
#include "render/renderticket.h"
#include "rendercache.h"
namespace olive {
class RenderThread : public QThread
namespace olive
{
Q_OBJECT
class RenderThread : public QThread {
Q_OBJECT
public:
RenderThread(Renderer *renderer, DecoderCache *decoder_cache, ShaderCache *shader_cache, QObject *parent = nullptr);
RenderThread(Renderer *renderer, DecoderCache *decoder_cache,
ShaderCache *shader_cache, QObject *parent = nullptr);
void AddTicket(RenderTicketPtr ticket);
void AddTicket(RenderTicketPtr ticket);
bool RemoveTicket(RenderTicketPtr ticket);
bool RemoveTicket(RenderTicketPtr ticket);
void quit();
void quit();
protected:
virtual void run() override;
virtual void run() override;
private:
QMutex mutex_;
QMutex mutex_;
QWaitCondition wait_;
QWaitCondition wait_;
std::list<RenderTicketPtr> queue_;
std::list<RenderTicketPtr> queue_;
bool cancelled_;
bool cancelled_;
Renderer *context_;
Renderer *context_;
DecoderCache *decoder_cache_;
ShaderCache *shader_cache_;
DecoderCache *decoder_cache_;
ShaderCache *shader_cache_;
};
class RenderManager : public QObject
{
Q_OBJECT
class RenderManager : public QObject {
Q_OBJECT
public:
enum Backend {
/// Graphics acceleration provided by OpenGL
kOpenGL,
enum Backend {
/// Graphics acceleration provided by OpenGL
kOpenGL,
/// No graphics rendering - used to test core threading logic
kDummy
};
/// No graphics rendering - used to test core threading logic
kDummy
};
static void CreateInstance()
{
instance_ = new RenderManager();
}
static void CreateInstance()
{
instance_ = new RenderManager();
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static RenderManager* instance()
{
return instance_;
}
static RenderManager *instance()
{
return instance_;
}
enum ReturnType {
kTexture,
kFrame,
kNull
};
enum ReturnType { kTexture, kFrame, kNull };
struct RenderVideoParams {
RenderVideoParams(Node *n, const VideoParams &vparam, const AudioParams &aparam, const rational &t,
ColorManager *colorman, RenderMode::Mode m)
{
node = n;
video_params = vparam;
audio_params = aparam;
time = t;
color_manager = colorman;
use_cache = false;
return_type = kFrame;
force_format = PixelFormat::INVALID;
force_color_output = nullptr;
force_size = QSize(0, 0);
force_channel_count = 0;
mode = m;
multicam = nullptr;
}
struct RenderVideoParams {
RenderVideoParams(Node *n, const VideoParams &vparam,
const AudioParams &aparam, const rational &t,
ColorManager *colorman, RenderMode::Mode m)
{
node = n;
video_params = vparam;
audio_params = aparam;
time = t;
color_manager = colorman;
use_cache = false;
return_type = kFrame;
force_format = PixelFormat::INVALID;
force_color_output = nullptr;
force_size = QSize(0, 0);
force_channel_count = 0;
mode = m;
multicam = nullptr;
}
void AddCache(FrameHashCache *cache)
{
cache_dir = cache->GetCacheDirectory();
cache_timebase = cache->GetTimebase();
cache_id = cache->GetUuid().toString();
}
void AddCache(FrameHashCache *cache)
{
cache_dir = cache->GetCacheDirectory();
cache_timebase = cache->GetTimebase();
cache_id = cache->GetUuid().toString();
}
Node *node;
VideoParams video_params;
AudioParams audio_params;
rational time;
ColorManager *color_manager;
bool use_cache;
ReturnType return_type;
RenderMode::Mode mode;
MultiCamNode *multicam;
Node *node;
VideoParams video_params;
AudioParams audio_params;
rational time;
ColorManager *color_manager;
bool use_cache;
ReturnType return_type;
RenderMode::Mode mode;
MultiCamNode *multicam;
QString cache_dir;
rational cache_timebase;
QString cache_id;
QString cache_dir;
rational cache_timebase;
QString cache_id;
QSize force_size;
int force_channel_count;
QMatrix4x4 force_matrix;
PixelFormat force_format;
ColorProcessorPtr force_color_output;
};
QSize force_size;
int force_channel_count;
QMatrix4x4 force_matrix;
PixelFormat force_format;
ColorProcessorPtr force_color_output;
};
static const rational kDryRunInterval;
static const rational kDryRunInterval;
/**
/**
* @brief Asynchronously generate a frame at a given time
*
* The ticket from this function will return a FramePtr - the rendered frame in reference color
@@ -159,101 +155,98 @@ public:
*
* This function is thread-safe.
*/
RenderTicketPtr RenderFrame(const RenderVideoParams &params);
RenderTicketPtr RenderFrame(const RenderVideoParams &params);
struct RenderAudioParams {
RenderAudioParams(Node *n, const TimeRange &time, const AudioParams &aparam, RenderMode::Mode m)
{
node = n;
range = time;
audio_params = aparam;
generate_waveforms = false;
clamp = true;
mode = m;
}
struct RenderAudioParams {
RenderAudioParams(Node *n, const TimeRange &time,
const AudioParams &aparam, RenderMode::Mode m)
{
node = n;
range = time;
audio_params = aparam;
generate_waveforms = false;
clamp = true;
mode = m;
}
Node *node;
TimeRange range;
AudioParams audio_params;
bool generate_waveforms;
bool clamp;
RenderMode::Mode mode;
};
Node *node;
TimeRange range;
AudioParams audio_params;
bool generate_waveforms;
bool clamp;
RenderMode::Mode mode;
};
/**
/**
* @brief Asynchronously generate a chunk of audio
*
* The ticket from this function will return a SampleBufferPtr - the rendered audio.
*
* This function is thread-safe.
*/
RenderTicketPtr RenderAudio(const RenderAudioParams &params);
RenderTicketPtr RenderAudio(const RenderAudioParams &params);
bool RemoveTicket(RenderTicketPtr ticket);
bool RemoveTicket(RenderTicketPtr ticket);
enum TicketType {
kTypeVideo,
kTypeAudio
};
enum TicketType { kTypeVideo, kTypeAudio };
Backend backend() const
{
return backend_;
}
Backend backend() const
{
return backend_;
}
PreviewAutoCacher *GetCacher() const
{
return auto_cacher_;
}
PreviewAutoCacher *GetCacher() const
{
return auto_cacher_;
}
void SetProject(Project *p)
{
auto_cacher_->SetProject(p);
}
void SetProject(Project *p)
{
auto_cacher_->SetProject(p);
}
public slots:
void SetAggressiveGarbageCollection(bool enabled);
void SetAggressiveGarbageCollection(bool enabled);
signals:
private:
RenderManager(QObject* parent = nullptr);
RenderManager(QObject *parent = nullptr);
virtual ~RenderManager() override;
virtual ~RenderManager() override;
RenderThread *CreateThread(Renderer *renderer = nullptr);
RenderThread *CreateThread(Renderer *renderer = nullptr);
static RenderManager* instance_;
static RenderManager *instance_;
Renderer* context_;
Renderer *context_;
Backend backend_;
Backend backend_;
DecoderCache* decoder_cache_;
DecoderCache *decoder_cache_;
ShaderCache* shader_cache_;
ShaderCache *shader_cache_;
static constexpr auto kDecoderMaximumInactivityAggressive = 1000;
static constexpr auto kDecoderMaximumInactivity = 5000;
static constexpr auto kDecoderMaximumInactivityAggressive = 1000;
static constexpr auto kDecoderMaximumInactivity = 5000;
int aggressive_gc_;
int aggressive_gc_;
QTimer *decoder_clear_timer_;
QTimer *decoder_clear_timer_;
RenderThread *video_thread_;
RenderThread *dry_run_thread_;
RenderThread *audio_thread_;
RenderThread *video_thread_;
RenderThread *dry_run_thread_;
RenderThread *audio_thread_;
std::vector<RenderThread *> waveform_threads_;
size_t last_waveform_thread_;
std::vector<RenderThread *> waveform_threads_;
size_t last_waveform_thread_;
std::list<RenderThread *> render_threads_;
std::list<RenderThread *> render_threads_;
PreviewAutoCacher *auto_cacher_;
PreviewAutoCacher *auto_cacher_;
private slots:
void ClearOldDecoders();
void ClearOldDecoders();
};
}
+9 -8
View File
@@ -23,26 +23,27 @@
#include "common/define.h"
namespace olive {
namespace olive
{
class RenderMode {
public:
/**
/**
* @brief The primary different "modes" the renderer can function in
*/
enum Mode {
/**
enum Mode {
/**
* This render is for realtime preview ONLY and does not need to be "perfect". Nodes can use lower-accuracy functions
* to save performance when possible.
*/
kOffline,
kOffline,
/**
/**
* This render is some sort of export or master copy and Nodes should take time/bandwidth/system resources to produce
* a higher accuracy version.
*/
kOnline
};
kOnline
};
};
}
File diff suppressed because it is too large Load Diff
+52 -35
View File
@@ -27,67 +27,84 @@
#include "rendercache.h"
#include "renderticket.h"
namespace olive {
class RenderProcessor : public NodeTraverser
namespace olive
{
class RenderProcessor : public NodeTraverser {
public:
virtual NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range) override;
virtual NodeValueDatabase GenerateDatabase(const Node *node,
const TimeRange &range) override;
static void Process(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache);
static void Process(RenderTicketPtr ticket, Renderer *render_ctx,
DecoderCache *decoder_cache, ShaderCache *shader_cache);
struct RenderedWaveform {
const ClipBlock* block;
AudioVisualWaveform waveform;
TimeRange range;
bool silence;
};
struct RenderedWaveform {
const ClipBlock *block;
AudioVisualWaveform waveform;
TimeRange range;
bool silence;
};
protected:
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time) override;
virtual void ProcessVideoFootage(TexturePtr destination,
const FootageJob *stream,
const rational &input_time) override;
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time) override;
virtual void ProcessAudioFootage(SampleBuffer &destination,
const FootageJob *stream,
const TimeRange &input_time) override;
virtual void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job) override;
virtual void ProcessShader(TexturePtr destination, const Node *node,
const ShaderJob *job) override;
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
virtual void ProcessSamples(SampleBuffer &destination, const Node *node,
const TimeRange &range,
const SampleJob &job) override;
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) override;
virtual void ProcessColorTransform(TexturePtr destination, const Node *node,
const ColorTransformJob *job) override;
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job) override;
virtual void ProcessFrameGeneration(TexturePtr destination,
const Node *node,
const GenerateJob *job) override;
virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val) override;
virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val) override;
virtual TexturePtr CreateTexture(const VideoParams &p) override;
virtual TexturePtr CreateTexture(const VideoParams &p) override;
virtual SampleBuffer CreateSampleBuffer(const AudioParams &params, int sample_count) override
{
return SampleBuffer(params, sample_count);
}
virtual SampleBuffer CreateSampleBuffer(const AudioParams &params,
int sample_count) override
{
return SampleBuffer(params, sample_count);
}
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
virtual void ConvertToReferenceSpace(TexturePtr destination,
TexturePtr source,
const QString &input_cs) override;
virtual bool UseCache() const override;
virtual bool UseCache() const override;
private:
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache);
RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx,
DecoderCache *decoder_cache, ShaderCache *shader_cache);
TexturePtr GenerateTexture(const rational& time, const rational& frame_length);
TexturePtr GenerateTexture(const rational &time,
const rational &frame_length);
FramePtr GenerateFrame(TexturePtr texture, const rational &time);
FramePtr GenerateFrame(TexturePtr texture, const rational &time);
void Run();
void Run();
DecoderPtr ResolveDecoderFromInput(const QString &decoder_id, const Decoder::CodecStream& stream);
DecoderPtr ResolveDecoderFromInput(const QString &decoder_id,
const Decoder::CodecStream &stream);
RenderTicketPtr ticket_;
RenderTicketPtr ticket_;
Renderer* render_ctx_;
Renderer *render_ctx_;
DecoderCache* decoder_cache_;
ShaderCache* shader_cache_;
DecoderCache *decoder_cache_;
ShaderCache *shader_cache_;
};
}
+94 -92
View File
@@ -20,190 +20,192 @@
#include "renderticket.h"
namespace olive {
namespace olive
{
RenderTicket::RenderTicket() :
is_running_(false),
has_result_(false),
finish_count_(0)
RenderTicket::RenderTicket()
: is_running_(false)
, has_result_(false)
, finish_count_(0)
{
}
void RenderTicket::WaitForFinished(QMutex *mutex)
{
if (is_running_) {
wait_.wait(mutex);
}
if (is_running_) {
wait_.wait(mutex);
}
}
void RenderTicket::Start()
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
is_running_ = true;
has_result_ = false;
result_.clear();
is_running_ = true;
has_result_ = false;
result_.clear();
}
void RenderTicket::Finish()
{
FinishInternal(false, QVariant());
FinishInternal(false, QVariant());
}
void RenderTicket::Finish(QVariant result)
{
FinishInternal(true, result);
FinishInternal(true, result);
}
QVariant RenderTicket::Get()
{
WaitForFinished();
WaitForFinished();
// We don't have to mutex around this because there is no way to write to `result_` after
// the ticket has finished and the above function blocks the calling thread until it is finished
return result_;
// We don't have to mutex around this because there is no way to write to `result_` after
// the ticket has finished and the above function blocks the calling thread until it is finished
return result_;
}
void RenderTicket::WaitForFinished()
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
WaitForFinished(&lock_);
WaitForFinished(&lock_);
}
bool RenderTicket::IsRunning(bool lock)
{
if (lock) {
lock_.lock();
}
if (lock) {
lock_.lock();
}
bool running = is_running_;
bool running = is_running_;
if (lock) {
lock_.unlock();
}
if (lock) {
lock_.unlock();
}
return running;
return running;
}
int RenderTicket::GetFinishCount(bool lock)
{
if (lock) {
lock_.lock();
}
if (lock) {
lock_.lock();
}
int count = finish_count_;
int count = finish_count_;
if (lock) {
lock_.unlock();
}
if (lock) {
lock_.unlock();
}
return count;
return count;
}
bool RenderTicket::HasResult()
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
return has_result_;
return has_result_;
}
void RenderTicket::FinishInternal(bool has_result, QVariant result)
{
QMutexLocker locker(&lock_);
QMutexLocker locker(&lock_);
if (!is_running_) {
qWarning() << "Tried to finish ticket that wasn't running";
} else {
is_running_ = false;
has_result_ = has_result;
result_ = result;
finish_count_++;
if (!is_running_) {
qWarning() << "Tried to finish ticket that wasn't running";
} else {
is_running_ = false;
has_result_ = has_result;
result_ = result;
finish_count_++;
wait_.wakeAll();
wait_.wakeAll();
locker.unlock();
locker.unlock();
emit Finished();
}
emit Finished();
}
}
RenderTicketWatcher::RenderTicketWatcher(QObject *parent) :
QObject(parent),
ticket_(nullptr)
RenderTicketWatcher::RenderTicketWatcher(QObject *parent)
: QObject(parent)
, ticket_(nullptr)
{
}
void RenderTicketWatcher::SetTicket(RenderTicketPtr ticket)
{
if (ticket_) {
qCritical() << "Tried to set a ticket on a RenderTicketWatcher twice";
return;
}
if (ticket_) {
qCritical() << "Tried to set a ticket on a RenderTicketWatcher twice";
return;
}
if (!ticket) {
qCritical() << "Tried to set a null ticket on a RenderTicketWatcher";
return;
}
if (!ticket) {
qCritical() << "Tried to set a null ticket on a RenderTicketWatcher";
return;
}
ticket_ = ticket;
ticket_ = ticket;
// Lock ticket so we can query if it's already finished by the time this code runs
QMutexLocker locker(ticket->lock());
// Lock ticket so we can query if it's already finished by the time this code runs
QMutexLocker locker(ticket->lock());
connect(ticket_.get(), &RenderTicket::Finished, this, &RenderTicketWatcher::TicketFinished);
connect(ticket_.get(), &RenderTicket::Finished, this,
&RenderTicketWatcher::TicketFinished);
if (!ticket_->IsRunning(false) && ticket_->GetFinishCount(false) > 0) {
// Ticket has already finished before, so we emit a signal
locker.unlock();
TicketFinished();
}
if (!ticket_->IsRunning(false) && ticket_->GetFinishCount(false) > 0) {
// Ticket has already finished before, so we emit a signal
locker.unlock();
TicketFinished();
}
}
bool RenderTicketWatcher::IsRunning()
{
if (ticket_) {
return ticket_->IsRunning();
} else {
return false;
}
if (ticket_) {
return ticket_->IsRunning();
} else {
return false;
}
}
void RenderTicketWatcher::WaitForFinished()
{
if (ticket_) {
ticket_->WaitForFinished();
}
if (ticket_) {
ticket_->WaitForFinished();
}
}
QVariant RenderTicketWatcher::Get()
{
if (ticket_) {
return ticket_->Get();
} else {
return QVariant();
}
if (ticket_) {
return ticket_->Get();
} else {
return QVariant();
}
}
bool RenderTicketWatcher::HasResult()
{
if (ticket_) {
return ticket_->HasResult();
} else {
return false;
}
if (ticket_) {
return ticket_->HasResult();
} else {
return false;
}
}
void RenderTicketWatcher::Cancel()
{
if (ticket_) {
ticket_->Cancel();
}
if (ticket_) {
ticket_->Cancel();
}
}
void RenderTicketWatcher::TicketFinished()
{
emit Finished(this);
emit Finished(this);
}
}
+53 -56
View File
@@ -29,139 +29,136 @@
#include "common/cancelableobject.h"
#include "node/output/viewer/viewer.h"
namespace olive {
class RenderTicket : public QObject, public CancelableObject
namespace olive
{
Q_OBJECT
public:
RenderTicket();
/**
class RenderTicket : public QObject, public CancelableObject {
Q_OBJECT
public:
RenderTicket();
/**
* @brief Get the ticket's current state
*
* This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
* of locking the mutex before and unlocking after this function is called.
*/
bool IsRunning(bool lock = true);
bool IsRunning(bool lock = true);
/**
/**
* @brief Determine how many times ticket has been finished
*
* This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
* of locking the mutex before and unlocking after this function is called.
*/
int GetFinishCount(bool lock = true);
int GetFinishCount(bool lock = true);
/**
/**
* @brief Check if this ticket has a result
*
* If this ticket is running, this will always return false.
*/
bool HasResult();
bool HasResult();
/**
/**
* @brief Get value, if any
*/
QVariant Get();
QVariant Get();
/**
/**
* @brief Wait for ticket to be finished
*
* If this ticket is not running, this function returns immediately.
*/
void WaitForFinished();
void WaitForFinished(QMutex* mutex);
void WaitForFinished();
void WaitForFinished(QMutex *mutex);
/**
/**
* @brief Access this ticket's mutex
*
* Use if you're doing several operations on a ticket and need to ensure thread safety while
* doing so. Most of the time this isn't necessary since all functions are thread safe by default.
*/
QMutex* lock()
{
return &lock_;
}
QMutex *lock()
{
return &lock_;
}
/**
/**
* @brief Signal to the ticket that it is running
*
* If any value is set, it is cleared.
*/
void Start();
void Start();
/**
/**
* @brief Finish ticket with no value
*
* Sets ticket to no longer running and assume it has received no result.
*/
void Finish();
void Finish();
/**
/**
* @brief Finish ticket with value
*
* Sets ticket to no longer running and provide a value generated by the operation requested.
*/
void Finish(QVariant result);
void Finish(QVariant result);
signals:
/**
/**
* @brief Emitted when finish has been called by any means (either cancelled or with a result)
*/
void Finished();
void Finished();
private:
void FinishInternal(bool has_result, QVariant result);
void FinishInternal(bool has_result, QVariant result);
bool is_running_;
bool is_running_;
QVariant result_;
QVariant result_;
bool has_result_;
bool has_result_;
int finish_count_;
int finish_count_;
QMutex lock_;
QWaitCondition wait_;
QMutex lock_;
QWaitCondition wait_;
};
using RenderTicketPtr = std::shared_ptr<RenderTicket>;
class RenderTicketWatcher : public QObject
{
Q_OBJECT
class RenderTicketWatcher : public QObject {
Q_OBJECT
public:
RenderTicketWatcher(QObject* parent = nullptr);
RenderTicketWatcher(QObject *parent = nullptr);
RenderTicketPtr GetTicket() const
{
return ticket_;
}
RenderTicketPtr GetTicket() const
{
return ticket_;
}
void SetTicket(RenderTicketPtr ticket);
void SetTicket(RenderTicketPtr ticket);
bool IsRunning();
bool IsRunning();
void WaitForFinished();
void WaitForFinished();
QVariant Get();
QVariant Get();
bool HasResult();
bool HasResult();
void Cancel();
void Cancel();
signals:
void Finished(RenderTicketWatcher* watcher);
void Finished(RenderTicketWatcher *watcher);
private:
RenderTicketPtr ticket_;
RenderTicketPtr ticket_;
private slots:
void TicketFinished();
void TicketFinished();
};
}
+26 -13
View File
@@ -23,27 +23,40 @@
#include "common/filefunctions.h"
namespace olive {
namespace olive
{
class ShaderCode {
public:
ShaderCode(const QString& frag_code = QString(), const QString& vert_code = QString()) :
frag_code_(frag_code),
vert_code_(vert_code)
{
}
ShaderCode(const QString &frag_code = QString(),
const QString &vert_code = QString())
: frag_code_(frag_code)
, vert_code_(vert_code)
{
}
const QString& frag_code() const { return frag_code_; }
void set_frag_code(const QString &f) { frag_code_ = f; }
const QString &frag_code() const
{
return frag_code_;
}
void set_frag_code(const QString &f)
{
frag_code_ = f;
}
const QString& vert_code() const { return vert_code_; }
void set_vert_code(const QString &v) { vert_code_ = v; }
const QString &vert_code() const
{
return vert_code_;
}
void set_vert_code(const QString &v)
{
vert_code_ = v;
}
private:
QString frag_code_;
QString vert_code_;
QString frag_code_;
QString vert_code_;
};
}
+125 -105
View File
@@ -24,140 +24,160 @@
#include "common/xmlutils.h"
namespace olive {
namespace olive
{
QString SubtitleParams::GenerateASSHeader()
{
// NOTE: We'll probably implement more customization as we support ASS better. Right now, we only
// natively support SRT and only make this header because FFmpeg requires it.
static const int kAssDefaultPlayResX = 384;
static const int kAssDefaultPlayResY = 288;
static const QString kAssDefaultFont = QStringLiteral("Arial");
static const int kAssDefaultFontSize = 16;
static const int kAssDefaultPrimaryColor = 0xFFFFFF; // White
static const int kAssDefaultSecondaryColor = 0xFFFFFF; // White
static const int kAssDefaultOutlineColor = 0x000000; // Black
static const int kAssDefaultBackColor = 0x000000; // Black
static const int kAssBold = 0;
static const int kAssItalic = 0;
static const int kAssUnderline = 0;
static const int kAssStrike = 0;
static const int kAssBorderStyle = 1;
static const int kAssAlignment = 2;
// NOTE: We'll probably implement more customization as we support ASS better. Right now, we only
// natively support SRT and only make this header because FFmpeg requires it.
static const int kAssDefaultPlayResX = 384;
static const int kAssDefaultPlayResY = 288;
static const QString kAssDefaultFont = QStringLiteral("Arial");
static const int kAssDefaultFontSize = 16;
static const int kAssDefaultPrimaryColor = 0xFFFFFF; // White
static const int kAssDefaultSecondaryColor = 0xFFFFFF; // White
static const int kAssDefaultOutlineColor = 0x000000; // Black
static const int kAssDefaultBackColor = 0x000000; // Black
static const int kAssBold = 0;
static const int kAssItalic = 0;
static const int kAssUnderline = 0;
static const int kAssStrike = 0;
static const int kAssBorderStyle = 1;
static const int kAssAlignment = 2;
QString ass_code;
QString ass_code;
// Header info
ass_code.append(QStringLiteral("[Script Info]\r\n"));
ass_code.append(QStringLiteral("; Script generated by %1 %2\r\n").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()));
ass_code.append(QStringLiteral("ScriptType: v4.00+\r\n"));
ass_code.append(QStringLiteral("PlayResX: %1\r\n").arg(QString::number(kAssDefaultPlayResX)));
ass_code.append(QStringLiteral("PlayResY: %1\r\n").arg(QString::number(kAssDefaultPlayResY)));
ass_code.append(QStringLiteral("ScaledBorderAndShadow: yes\r\n"));
ass_code.append(QStringLiteral("\r\n"));
// Header info
ass_code.append(QStringLiteral("[Script Info]\r\n"));
ass_code.append(QStringLiteral("; Script generated by %1 %2\r\n")
.arg(QCoreApplication::applicationName(),
QCoreApplication::applicationVersion()));
ass_code.append(QStringLiteral("ScriptType: v4.00+\r\n"));
ass_code.append(QStringLiteral("PlayResX: %1\r\n")
.arg(QString::number(kAssDefaultPlayResX)));
ass_code.append(QStringLiteral("PlayResY: %1\r\n")
.arg(QString::number(kAssDefaultPlayResY)));
ass_code.append(QStringLiteral("ScaledBorderAndShadow: yes\r\n"));
ass_code.append(QStringLiteral("\r\n"));
// ASSv4 header
ass_code.append(QStringLiteral("[V4+ Styles]\r\n"));
ass_code.append(QStringLiteral("Format: Name, "));
ass_code.append(QStringLiteral("Fontname, Fontsize, "));
ass_code.append(QStringLiteral("PrimaryColour, SecondaryColour, OutlineColour, BackColour, "));
ass_code.append(QStringLiteral("Bold, Italic, Underline, StrikeOut, "));
ass_code.append(QStringLiteral("ScaleX, ScaleY, "));
ass_code.append(QStringLiteral("Spacing, Angle, "));
ass_code.append(QStringLiteral("BorderStyle, Outline, Shadow, "));
ass_code.append(QStringLiteral("Alignment, MarginL, MarginR, MarginV, "));
ass_code.append(QStringLiteral("Encoding\r\n"));
ass_code.append(QStringLiteral("Style: "));
// ASSv4 header
ass_code.append(QStringLiteral("[V4+ Styles]\r\n"));
ass_code.append(QStringLiteral("Format: Name, "));
ass_code.append(QStringLiteral("Fontname, Fontsize, "));
ass_code.append(QStringLiteral(
"PrimaryColour, SecondaryColour, OutlineColour, BackColour, "));
ass_code.append(QStringLiteral("Bold, Italic, Underline, StrikeOut, "));
ass_code.append(QStringLiteral("ScaleX, ScaleY, "));
ass_code.append(QStringLiteral("Spacing, Angle, "));
ass_code.append(QStringLiteral("BorderStyle, Outline, Shadow, "));
ass_code.append(QStringLiteral("Alignment, MarginL, MarginR, MarginV, "));
ass_code.append(QStringLiteral("Encoding\r\n"));
ass_code.append(QStringLiteral("Style: "));
// Name
ass_code.append(QStringLiteral("Default,"));
// Name
ass_code.append(QStringLiteral("Default,"));
// Font{name,size}
ass_code.append(QStringLiteral("%1,%2,").arg(kAssDefaultFont, QString::number(kAssDefaultFontSize)));
// Font{name,size}
ass_code.append(QStringLiteral("%1,%2,").arg(
kAssDefaultFont, QString::number(kAssDefaultFontSize)));
// {Primary,Secondary,Outline,Back}Colour
ass_code.append(QStringLiteral("&H%1,&H%2,&H%3,&H%4,").arg(QString::number(kAssDefaultPrimaryColor, 16),
QString::number(kAssDefaultSecondaryColor, 16),
QString::number(kAssDefaultOutlineColor, 16),
QString::number(kAssDefaultBackColor, 16)));
// {Primary,Secondary,Outline,Back}Colour
ass_code.append(QStringLiteral("&H%1,&H%2,&H%3,&H%4,")
.arg(QString::number(kAssDefaultPrimaryColor, 16),
QString::number(kAssDefaultSecondaryColor, 16),
QString::number(kAssDefaultOutlineColor, 16),
QString::number(kAssDefaultBackColor, 16)));
// Bold, Italic, Underline, StrikeOut
ass_code.append(QStringLiteral("%1,%2,%3,%4,").arg(QString::number(kAssBold),
QString::number(kAssItalic),
QString::number(kAssUnderline),
QString::number(kAssStrike)));
// Bold, Italic, Underline, StrikeOut
ass_code.append(
QStringLiteral("%1,%2,%3,%4,")
.arg(QString::number(kAssBold), QString::number(kAssItalic),
QString::number(kAssUnderline), QString::number(kAssStrike)));
// Scale{X,Y}
ass_code.append(QStringLiteral("100,100,"));
// Scale{X,Y}
ass_code.append(QStringLiteral("100,100,"));
// Spacing, Angle
ass_code.append(QStringLiteral("0,0,"));
// Spacing, Angle
ass_code.append(QStringLiteral("0,0,"));
// BorderStyle, Outline, Shadow
ass_code.append(QStringLiteral("%1,1,0,").arg(QString::number(kAssBorderStyle)));
// BorderStyle, Outline, Shadow
ass_code.append(
QStringLiteral("%1,1,0,").arg(QString::number(kAssBorderStyle)));
// Alignment, Margin[LRV]
ass_code.append(QStringLiteral("%1,10,10,10,").arg(QString::number(kAssAlignment)));
// Alignment, Margin[LRV]
ass_code.append(
QStringLiteral("%1,10,10,10,").arg(QString::number(kAssAlignment)));
// Encoding
ass_code.append(QStringLiteral("0\r\n"));
ass_code.append(QStringLiteral("\r\n"));
ass_code.append(QStringLiteral("[Events]\r\n"));
ass_code.append(QStringLiteral("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n"));
// Encoding
ass_code.append(QStringLiteral("0\r\n"));
ass_code.append(QStringLiteral("\r\n"));
ass_code.append(QStringLiteral("[Events]\r\n"));
ass_code.append(QStringLiteral(
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n"));
return ass_code;
return ass_code;
}
void SubtitleParams::Load(QXmlStreamReader *reader)
{
this->clear();
this->clear();
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("streamindex")) {
set_stream_index(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("enabled")) {
set_enabled(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("subtitles")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("subtitle")) {
rational in, out;
QString text;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("streamindex")) {
set_stream_index(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("enabled")) {
set_enabled(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("subtitles")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("subtitle")) {
rational in, out;
QString text;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("in")) {
in = rational::fromString(attr.value().toString().toStdString());
} else if (attr.name() == QStringLiteral("out")) {
out = rational::fromString(attr.value().toString().toStdString());
}
}
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("in")) {
in = rational::fromString(
attr.value().toString().toStdString());
} else if (attr.name() == QStringLiteral("out")) {
out = rational::fromString(
attr.value().toString().toStdString());
}
}
text = reader->readElementText();
text = reader->readElementText();
this->push_back(Subtitle(TimeRange(in, out), text));
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
this->push_back(Subtitle(TimeRange(in, out), text));
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
}
void SubtitleParams::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index_));
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
writer->writeTextElement(QStringLiteral("streamindex"),
QString::number(stream_index_));
writer->writeTextElement(QStringLiteral("enabled"),
QString::number(enabled_));
writer->writeStartElement(QStringLiteral("subtitles"));
for (auto it=this->cbegin(); it!=this->cend(); it++) {
writer->writeStartElement(QStringLiteral("subtitle"));
writer->writeAttribute(QStringLiteral("in"), QString::fromStdString(it->time().in().toString()));
writer->writeAttribute(QStringLiteral("out"), QString::fromStdString(it->time().out().toString()));
writer->writeCharacters(it->text());
writer->writeEndElement(); // subtitle
}
writer->writeEndElement(); // subtitles
writer->writeStartElement(QStringLiteral("subtitles"));
for (auto it = this->cbegin(); it != this->cend(); it++) {
writer->writeStartElement(QStringLiteral("subtitle"));
writer->writeAttribute(
QStringLiteral("in"),
QString::fromStdString(it->time().in().toString()));
writer->writeAttribute(
QStringLiteral("out"),
QString::fromStdString(it->time().out().toString()));
writer->writeCharacters(it->text());
writer->writeEndElement(); // subtitle
}
writer->writeEndElement(); // subtitles
}
}
+66 -45
View File
@@ -29,72 +29,93 @@
using namespace olive::core;
namespace olive {
class Subtitle
namespace olive
{
class Subtitle {
public:
Subtitle() = default;
Subtitle() = default;
Subtitle(const TimeRange &time, const QString &text) :
range_(time),
text_(text)
{
}
Subtitle(const TimeRange &time, const QString &text)
: range_(time)
, text_(text)
{
}
const TimeRange &time() const { return range_; }
void set_time(const TimeRange &t) { range_ = t; }
const TimeRange &time() const
{
return range_;
}
void set_time(const TimeRange &t)
{
range_ = t;
}
const QString &text() const { return text_; }
void set_text(const QString &t) { text_ = t; }
const QString &text() const
{
return text_;
}
void set_text(const QString &t)
{
text_ = t;
}
private:
TimeRange range_;
QString text_;
TimeRange range_;
QString text_;
};
class SubtitleParams : public std::vector<Subtitle>
{
class SubtitleParams : public std::vector<Subtitle> {
public:
SubtitleParams()
{
stream_index_ = 0;
enabled_ = true;
}
SubtitleParams()
{
stream_index_ = 0;
enabled_ = true;
}
static QString GenerateASSHeader();
static QString GenerateASSHeader();
void Load(QXmlStreamReader* reader);
void Load(QXmlStreamReader *reader);
void Save(QXmlStreamWriter* writer) const;
void Save(QXmlStreamWriter *writer) const;
bool is_valid() const
{
return !this->empty();
}
bool is_valid() const
{
return !this->empty();
}
rational duration() const
{
if (this->empty()) {
return 0;
} else {
return back().time().out();
}
}
rational duration() const
{
if (this->empty()) {
return 0;
} else {
return back().time().out();
}
}
int stream_index() const { return stream_index_; }
void set_stream_index(int i) { stream_index_ = i; }
int stream_index() const
{
return stream_index_;
}
void set_stream_index(int i)
{
stream_index_ = i;
}
bool enabled() const { return enabled_; }
void set_enabled(bool e) { enabled_ = e; }
bool enabled() const
{
return enabled_;
}
void set_enabled(bool e)
{
enabled_ = e;
}
private:
int stream_index_;
bool enabled_;
int stream_index_;
bool enabled_;
};
}
+17 -14
View File
@@ -22,33 +22,36 @@
#include "renderer.h"
namespace olive {
namespace olive
{
const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappedLinear;
const Texture::Interpolation Texture::kDefaultInterpolation =
Texture::kMipmappedLinear;
Texture::~Texture()
{
if (renderer_) {
renderer_->DestroyTexture(this);
}
if (renderer_) {
renderer_->DestroyTexture(this);
}
if (job_) {
delete job_;
}
if (job_) {
delete job_;
}
}
void Texture::Upload(void *data, int linesize)
{
if (renderer_) {
renderer_->UploadToTexture(this->id(), this->params(), data, linesize);
}
if (renderer_) {
renderer_->UploadToTexture(this->id(), this->params(), data, linesize);
}
}
void Texture::Download(void *data, int linesize)
{
if (renderer_) {
renderer_->DownloadFromTexture(this->id(), this->params(), data, linesize);
}
if (renderer_) {
renderer_->DownloadFromTexture(this->id(), this->params(), data,
linesize);
}
}
}
+95 -94
View File
@@ -26,7 +26,8 @@
#include "render/videoparams.h"
namespace olive {
namespace olive
{
class AcceleratedJob;
class Renderer;
@@ -34,130 +35,130 @@ class Renderer;
class Texture;
using TexturePtr = std::shared_ptr<Texture>;
class Texture
{
class Texture {
public:
enum Interpolation {
kNearest,
kLinear,
kMipmappedLinear
};
enum Interpolation { kNearest, kLinear, kMipmappedLinear };
static const Interpolation kDefaultInterpolation;
static const Interpolation kDefaultInterpolation;
/**
/**
* @brief Construct a dummy texture with no renderer backend
*/
Texture(const VideoParams& param) :
renderer_(nullptr),
params_(param),
job_(nullptr)
{
}
Texture(const VideoParams &param)
: renderer_(nullptr)
, params_(param)
, job_(nullptr)
{
}
template <typename T>
Texture(const VideoParams &p, const T &j) :
Texture(p)
{
job_ = new T(j);
}
template <typename T>
Texture(const VideoParams &p, const T &j)
: Texture(p)
{
job_ = new T(j);
}
/**
/**
* @brief Construct a real texture linked to a renderer backend
*/
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) :
renderer_(renderer),
params_(param),
id_(native),
job_(nullptr)
{
}
Texture(Renderer *renderer, const QVariant &native,
const VideoParams &param)
: renderer_(renderer)
, params_(param)
, id_(native)
, job_(nullptr)
{
}
~Texture();
~Texture();
QVariant id() const
{
return id_;
}
QVariant id() const
{
return id_;
}
const VideoParams& params() const
{
return params_;
}
const VideoParams &params() const
{
return params_;
}
template <typename T>
static TexturePtr Job(const VideoParams &p, const T &j)
{
return std::make_shared<Texture>(p, j);
}
template <typename T>
static TexturePtr Job(const VideoParams &p, const T &j)
{
return std::make_shared<Texture>(p, j);
}
template <typename T>
TexturePtr toJob(const T &job)
{
return Texture::Job(params_, job);
}
template <typename T> TexturePtr toJob(const T &job)
{
return Texture::Job(params_, job);
}
void Upload(void* data, int linesize);
void Upload(void *data, int linesize);
void Download(void* data, int linesize);
void Download(void *data, int linesize);
bool IsDummy() const
{
return !renderer_;
}
bool IsDummy() const
{
return !renderer_;
}
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();
}
QVector2D virtual_resolution() const
{
return QVector2D(params_.square_pixel_width(), params_.height());
}
QVector2D virtual_resolution() const
{
return QVector2D(params_.square_pixel_width(), params_.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();
}
int divider() const
{
return params_.divider();
}
int divider() const
{
return params_.divider();
}
const rational& pixel_aspect_ratio() const
{
return params_.pixel_aspect_ratio();
}
const rational &pixel_aspect_ratio() const
{
return params_.pixel_aspect_ratio();
}
Renderer* renderer() const
{
return renderer_;
}
Renderer *renderer() const
{
return renderer_;
}
bool IsJob() const { return job_; }
AcceleratedJob *job() const { return job_; }
bool IsJob() const
{
return job_;
}
AcceleratedJob *job() const
{
return job_;
}
private:
Renderer* renderer_;
Renderer *renderer_;
VideoParams params_;
VideoParams params_;
QVariant id_;
AcceleratedJob *job_;
QVariant id_;
AcceleratedJob *job_;
};
}
+292 -257
View File
@@ -29,7 +29,8 @@ extern "C" {
#include "core.h"
namespace olive {
namespace olive
{
const int VideoParams::kInternalChannelCount = kRGBAChannelCount;
@@ -41,370 +42,404 @@ const rational VideoParams::kPixelAspectPALWidescreen(64, 45);
const rational VideoParams::kPixelAspect1080Anamorphic(4, 3);
const QVector<rational> VideoParams::kSupportedFrameRates = {
rational(10, 1), // 10 FPS
rational(15, 1), // 15 FPS
rational(24000, 1001), // 23.976 FPS
rational(24, 1), // 24 FPS
rational(25, 1), // 25 FPS
rational(30000, 1001), // 29.97 FPS
rational(30, 1), // 30 FPS
rational(48000, 1001), // 47.952 FPS
rational(48, 1), // 48 FPS
rational(50, 1), // 50 FPS
rational(60000, 1001), // 59.94 FPS
rational(60, 1) // 60 FPS
rational(10, 1), // 10 FPS
rational(15, 1), // 15 FPS
rational(24000, 1001), // 23.976 FPS
rational(24, 1), // 24 FPS
rational(25, 1), // 25 FPS
rational(30000, 1001), // 29.97 FPS
rational(30, 1), // 30 FPS
rational(48000, 1001), // 47.952 FPS
rational(48, 1), // 48 FPS
rational(50, 1), // 50 FPS
rational(60000, 1001), // 59.94 FPS
rational(60, 1) // 60 FPS
};
const QVector<int> VideoParams::kSupportedDividers = {1, 2, 3, 4, 6, 8, 12, 16};
const QVector<int> VideoParams::kSupportedDividers = {
1, 2, 3, 4, 6, 8, 12, 16
};
const QVector<rational> VideoParams::kStandardPixelAspects = {
VideoParams::kPixelAspectSquare,
VideoParams::kPixelAspectNTSCStandard,
VideoParams::kPixelAspectNTSCWidescreen,
VideoParams::kPixelAspectPALStandard,
VideoParams::kPixelAspectPALWidescreen,
VideoParams::kPixelAspect1080Anamorphic
VideoParams::kPixelAspectSquare,
VideoParams::kPixelAspectNTSCStandard,
VideoParams::kPixelAspectNTSCWidescreen,
VideoParams::kPixelAspectPALStandard,
VideoParams::kPixelAspectPALWidescreen,
VideoParams::kPixelAspect1080Anamorphic
};
VideoParams::VideoParams() :
width_(0),
height_(0),
depth_(0),
format_(PixelFormat::INVALID),
channel_count_(0),
interlacing_(Interlacing::kInterlaceNone),
divider_(1)
VideoParams::VideoParams()
: width_(0)
, height_(0)
, depth_(0)
, format_(PixelFormat::INVALID)
, channel_count_(0)
, interlacing_(Interlacing::kInterlaceNone)
, divider_(1)
{
set_defaults_for_footage();
set_defaults_for_footage();
}
VideoParams::VideoParams(int width, int height, PixelFormat format, int nb_channels, const rational& pixel_aspect_ratio, Interlacing interlacing, int divider) :
width_(width),
height_(height),
depth_(1),
format_(format),
channel_count_(nb_channels),
pixel_aspect_ratio_(pixel_aspect_ratio),
interlacing_(interlacing),
divider_(divider)
VideoParams::VideoParams(int width, int height, PixelFormat format,
int nb_channels, const rational &pixel_aspect_ratio,
Interlacing interlacing, int divider)
: width_(width)
, height_(height)
, depth_(1)
, format_(format)
, channel_count_(nb_channels)
, pixel_aspect_ratio_(pixel_aspect_ratio)
, interlacing_(interlacing)
, divider_(divider)
{
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
}
VideoParams::VideoParams(int width, int height, int depth, PixelFormat format, int nb_channels, const rational &pixel_aspect_ratio, VideoParams::Interlacing interlacing, int divider) :
width_(width),
height_(height),
depth_(depth),
format_(format),
channel_count_(nb_channels),
pixel_aspect_ratio_(pixel_aspect_ratio),
interlacing_(interlacing),
divider_(divider)
VideoParams::VideoParams(int width, int height, int depth, PixelFormat format,
int nb_channels, const rational &pixel_aspect_ratio,
VideoParams::Interlacing interlacing, int divider)
: width_(width)
, height_(height)
, depth_(depth)
, format_(format)
, channel_count_(nb_channels)
, pixel_aspect_ratio_(pixel_aspect_ratio)
, interlacing_(interlacing)
, divider_(divider)
{
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
}
VideoParams::VideoParams(int width, int height, const rational &time_base, PixelFormat format, int nb_channels, const rational& pixel_aspect_ratio, Interlacing interlacing, int divider) :
width_(width),
height_(height),
depth_(1),
time_base_(time_base),
format_(format),
channel_count_(nb_channels),
pixel_aspect_ratio_(pixel_aspect_ratio),
interlacing_(interlacing),
divider_(divider),
frame_rate_(time_base.flipped())
VideoParams::VideoParams(int width, int height, const rational &time_base,
PixelFormat format, int nb_channels,
const rational &pixel_aspect_ratio,
Interlacing interlacing, int divider)
: width_(width)
, height_(height)
, depth_(1)
, time_base_(time_base)
, format_(format)
, channel_count_(nb_channels)
, pixel_aspect_ratio_(pixel_aspect_ratio)
, interlacing_(interlacing)
, divider_(divider)
, frame_rate_(time_base.flipped())
{
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
calculate_effective_size();
validate_pixel_aspect_ratio();
set_defaults_for_footage();
}
int VideoParams::generate_auto_divider(qint64 width, qint64 height)
{
const int target_res = 1280*720;
const int target_res = 1280 * 720;
qint64 megapixels = width * height;
qint64 megapixels = width * height;
double squared_divider = double(megapixels) / double(target_res);
double divider = qSqrt(squared_divider);
double squared_divider = double(megapixels) / double(target_res);
double divider = qSqrt(squared_divider);
if (divider <= kSupportedDividers.first()) {
return kSupportedDividers.first();
} else if (divider >= kSupportedDividers.last()) {
return kSupportedDividers.last();
} else {
for (int i=1; i<kSupportedDividers.size(); i++) {
int prev_divider = kSupportedDividers.at(i-1);
int next_divider = kSupportedDividers.at(i);
if (divider <= kSupportedDividers.first()) {
return kSupportedDividers.first();
} else if (divider >= kSupportedDividers.last()) {
return kSupportedDividers.last();
} else {
for (int i = 1; i < kSupportedDividers.size(); i++) {
int prev_divider = kSupportedDividers.at(i - 1);
int next_divider = kSupportedDividers.at(i);
if (divider >= prev_divider && divider <= next_divider) {
double prev_diff = qAbs(prev_divider - divider);
double next_diff = qAbs(next_divider - divider);
if (divider >= prev_divider && divider <= next_divider) {
double prev_diff = qAbs(prev_divider - divider);
double next_diff = qAbs(next_divider - divider);
if (prev_diff < next_diff) {
return prev_divider;
} else {
return next_divider;
}
}
}
if (prev_diff < next_diff) {
return prev_divider;
} else {
return next_divider;
}
}
}
// Fallback
return 1;
}
// Fallback
return 1;
}
}
bool VideoParams::operator==(const VideoParams &rhs) const
{
return width() == rhs.width()
&& height() == rhs.height()
&& depth() == rhs.depth()
&& interlacing() == rhs.interlacing()
&& time_base() == rhs.time_base()
&& format() == rhs.format()
&& pixel_aspect_ratio() == rhs.pixel_aspect_ratio()
&& divider() == rhs.divider()
&& channel_count() == rhs.channel_count();
return width() == rhs.width() && height() == rhs.height() &&
depth() == rhs.depth() && interlacing() == rhs.interlacing() &&
time_base() == rhs.time_base() && format() == rhs.format() &&
pixel_aspect_ratio() == rhs.pixel_aspect_ratio() &&
divider() == rhs.divider() && channel_count() == rhs.channel_count();
}
bool VideoParams::operator!=(const VideoParams &rhs) const
{
return !(*this == rhs);
return !(*this == rhs);
}
int VideoParams::GetBytesPerChannel(PixelFormat format)
{
switch (format) {
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
case PixelFormat::U8:
return 1;
case PixelFormat::U16:
case PixelFormat::F16:
return 2;
case PixelFormat::F32:
return 4;
}
switch (format) {
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
case PixelFormat::U8:
return 1;
case PixelFormat::U16:
case PixelFormat::F16:
return 2;
case PixelFormat::F32:
return 4;
}
return 0;
return 0;
}
int VideoParams::GetBytesPerPixel(PixelFormat format, int channels)
{
return GetBytesPerChannel(format) * channels;
return GetBytesPerChannel(format) * channels;
}
QString VideoParams::GetNameForDivider(int div)
{
if (div == 1) {
return QCoreApplication::translate("VideoParams", "Full");
} else {
return QCoreApplication::translate("VideoParams", "1/%1").arg(div);
}
if (div == 1) {
return QCoreApplication::translate("VideoParams", "Full");
} else {
return QCoreApplication::translate("VideoParams", "1/%1").arg(div);
}
}
QString VideoParams::GetFormatName(PixelFormat format)
{
switch (format) {
case PixelFormat::U8:
return QCoreApplication::translate("VideoParams", "8-bit");
case PixelFormat::U16:
return QCoreApplication::translate("VideoParams", "16-bit Integer");
case PixelFormat::F16:
return QCoreApplication::translate("VideoParams", "Half-Float (16-bit)");
case PixelFormat::F32:
return QCoreApplication::translate("VideoParams", "Full-Float (32-bit)");
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
switch (format) {
case PixelFormat::U8:
return QCoreApplication::translate("VideoParams", "8-bit");
case PixelFormat::U16:
return QCoreApplication::translate("VideoParams", "16-bit Integer");
case PixelFormat::F16:
return QCoreApplication::translate("VideoParams",
"Half-Float (16-bit)");
case PixelFormat::F32:
return QCoreApplication::translate("VideoParams",
"Full-Float (32-bit)");
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
return QCoreApplication::translate("VideoParams", "Unknown (0x%1)").arg(format, 0, 16);
return QCoreApplication::translate("VideoParams", "Unknown (0x%1)")
.arg(format, 0, 16);
}
int VideoParams::GetDividerForTargetResolution(int src_width, int src_height, int dst_width, int dst_height)
int VideoParams::GetDividerForTargetResolution(int src_width, int src_height,
int dst_width, int dst_height)
{
int divider = 0;
int test_width, test_height;
int divider = 0;
int test_width, test_height;
do {
divider++;
do {
divider++;
test_width = VideoParams::GetScaledDimension(src_width, divider);
test_height = VideoParams::GetScaledDimension(src_height, divider);
} while (test_width > dst_width || test_height > dst_height);
test_width = VideoParams::GetScaledDimension(src_width, divider);
test_height = VideoParams::GetScaledDimension(src_height, divider);
} while (test_width > dst_width || test_height > dst_height);
return divider;
return divider;
}
void VideoParams::calculate_effective_size()
{
effective_width_ = GetScaledDimension(width(), divider_);
effective_height_ = GetScaledDimension(height(), divider_);
effective_depth_ = (depth() == 1) ? depth() : GetScaledDimension(depth(), divider_);
calculate_square_pixel_width();
effective_width_ = GetScaledDimension(width(), divider_);
effective_height_ = GetScaledDimension(height(), divider_);
effective_depth_ = (depth() == 1) ? depth() :
GetScaledDimension(depth(), divider_);
calculate_square_pixel_width();
}
void VideoParams::validate_pixel_aspect_ratio()
{
if (pixel_aspect_ratio_.isNull()) {
pixel_aspect_ratio_ = 1;
}
calculate_square_pixel_width();
if (pixel_aspect_ratio_.isNull()) {
pixel_aspect_ratio_ = 1;
}
calculate_square_pixel_width();
}
void VideoParams::set_defaults_for_footage()
{
enabled_ = true;
stream_index_ = 0;
video_type_ = kVideoTypeVideo;
start_time_ = 0;
duration_ = 0;
premultiplied_alpha_ = false;
x_ = 0;
y_ = 0;
color_range_ = kColorRangeDefault;
enabled_ = true;
stream_index_ = 0;
video_type_ = kVideoTypeVideo;
start_time_ = 0;
duration_ = 0;
premultiplied_alpha_ = false;
x_ = 0;
y_ = 0;
color_range_ = kColorRangeDefault;
}
void VideoParams::calculate_square_pixel_width()
{
if (pixel_aspect_ratio_.denominator() != 0) {
par_width_ = qRound(width_ * pixel_aspect_ratio_.toDouble());
} else {
par_width_ = width_;
}
if (pixel_aspect_ratio_.denominator() != 0) {
par_width_ = qRound(width_ * pixel_aspect_ratio_.toDouble());
} else {
par_width_ = width_;
}
}
bool VideoParams::is_valid() const
{
return (width() > 0
&& height() > 0
&& !pixel_aspect_ratio_.isNull()
&& format_ > PixelFormat::INVALID && format_ < PixelFormat::COUNT
&& channel_count_ > 0);
return (width() > 0 && height() > 0 && !pixel_aspect_ratio_.isNull() &&
format_ > PixelFormat::INVALID && format_ < PixelFormat::COUNT &&
channel_count_ > 0);
}
QString VideoParams::FrameRateToString(const rational &frame_rate)
{
return QCoreApplication::translate("VideoParams", "%1 FPS").arg(frame_rate.toDouble());
return QCoreApplication::translate("VideoParams", "%1 FPS")
.arg(frame_rate.toDouble());
}
QStringList VideoParams::GetStandardPixelAspectRatioNames()
{
QStringList strings = {
QCoreApplication::translate("VideoParams", "Square Pixels (%1)"),
QCoreApplication::translate("VideoParams", "NTSC Standard (%1)"),
QCoreApplication::translate("VideoParams", "NTSC Widescreen (%1)"),
QCoreApplication::translate("VideoParams", "PAL Standard (%1)"),
QCoreApplication::translate("VideoParams", "PAL Widescreen (%1)"),
QCoreApplication::translate("VideoParams", "HD Anamorphic 1080 (%1)")
};
QStringList strings = {
QCoreApplication::translate("VideoParams", "Square Pixels (%1)"),
QCoreApplication::translate("VideoParams", "NTSC Standard (%1)"),
QCoreApplication::translate("VideoParams", "NTSC Widescreen (%1)"),
QCoreApplication::translate("VideoParams", "PAL Standard (%1)"),
QCoreApplication::translate("VideoParams", "PAL Widescreen (%1)"),
QCoreApplication::translate("VideoParams", "HD Anamorphic 1080 (%1)")
};
// Format each
for (int i=0; i<strings.size(); i++) {
strings.replace(i, FormatPixelAspectRatioString(strings.at(i), kStandardPixelAspects.at(i)));
}
// Format each
for (int i = 0; i < strings.size(); i++) {
strings.replace(i, FormatPixelAspectRatioString(
strings.at(i), kStandardPixelAspects.at(i)));
}
return strings;
return strings;
}
QString VideoParams::FormatPixelAspectRatioString(const QString &format, const rational &ratio)
QString VideoParams::FormatPixelAspectRatioString(const QString &format,
const rational &ratio)
{
return format.arg(QString::number(ratio.toDouble(), 'f', 4));
return format.arg(QString::number(ratio.toDouble(), 'f', 4));
}
int VideoParams::GetScaledDimension(int dim, int divider)
{
return dim / divider;
return dim / divider;
}
int64_t VideoParams::get_time_in_timebase_units(const rational &time) const
{
if (time_base_.isNull()) {
return AV_NOPTS_VALUE;
}
if (time_base_.isNull()) {
return AV_NOPTS_VALUE;
}
return Timecode::time_to_timestamp(time, time_base_) + start_time_;
return Timecode::time_to_timestamp(time, time_base_) + start_time_;
}
void VideoParams::Load(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("width")) {
set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("depth")) {
set_depth(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("timebase")) {
set_time_base(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("format")) {
set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("channelcount")) {
set_channel_count(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("pixelaspectratio")) {
set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("interlacing")) {
set_interlacing(static_cast<VideoParams::Interlacing>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("divider")) {
set_divider(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("enabled")) {
set_enabled(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("x")) {
set_x(reader->readElementText().toFloat());
} else if (reader->name() == QStringLiteral("y")) {
set_y(reader->readElementText().toFloat());
} else if (reader->name() == QStringLiteral("streamindex")) {
set_stream_index(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("videotype")) {
set_video_type(static_cast<VideoParams::Type>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("framerate")) {
set_frame_rate(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("starttime")) {
set_start_time(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("duration")) {
set_duration(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("premultipliedalpha")) {
set_premultiplied_alpha(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("colorspace")) {
set_colorspace(reader->readElementText());
} else if (reader->name() == QStringLiteral("colorrange")) {
set_color_range(static_cast<ColorRange>(reader->readElementText().toInt()));
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("width")) {
set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("depth")) {
set_depth(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("timebase")) {
set_time_base(
rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("format")) {
set_format(static_cast<PixelFormat::Format>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("channelcount")) {
set_channel_count(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("pixelaspectratio")) {
set_pixel_aspect_ratio(
rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("interlacing")) {
set_interlacing(static_cast<VideoParams::Interlacing>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("divider")) {
set_divider(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("enabled")) {
set_enabled(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("x")) {
set_x(reader->readElementText().toFloat());
} else if (reader->name() == QStringLiteral("y")) {
set_y(reader->readElementText().toFloat());
} else if (reader->name() == QStringLiteral("streamindex")) {
set_stream_index(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("videotype")) {
set_video_type(static_cast<VideoParams::Type>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("framerate")) {
set_frame_rate(
rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("starttime")) {
set_start_time(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("duration")) {
set_duration(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("premultipliedalpha")) {
set_premultiplied_alpha(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("colorspace")) {
set_colorspace(reader->readElementText());
} else if (reader->name() == QStringLiteral("colorrange")) {
set_color_range(
static_cast<ColorRange>(reader->readElementText().toInt()));
} else {
reader->skipCurrentElement();
}
}
}
void VideoParams::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
writer->writeTextElement(QStringLiteral("height"), QString::number(height_));
writer->writeTextElement(QStringLiteral("depth"), QString::number(depth_));
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(time_base_.toString()));
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_));
writer->writeTextElement(QStringLiteral("pixelaspectratio"), QString::fromStdString(pixel_aspect_ratio_.toString()));
writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_));
writer->writeTextElement(QStringLiteral("divider"), QString::number(divider_));
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
writer->writeTextElement(QStringLiteral("x"), QString::number(x_));
writer->writeTextElement(QStringLiteral("y"), QString::number(y_));
writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index_));
writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_));
writer->writeTextElement(QStringLiteral("framerate"), QString::fromStdString(frame_rate_.toString()));
writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_));
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_));
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeTextElement(QStringLiteral("colorrange"), QString::number(color_range_));
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
writer->writeTextElement(QStringLiteral("height"),
QString::number(height_));
writer->writeTextElement(QStringLiteral("depth"), QString::number(depth_));
writer->writeTextElement(QStringLiteral("timebase"),
QString::fromStdString(time_base_.toString()));
writer->writeTextElement(QStringLiteral("format"),
QString::number(format_));
writer->writeTextElement(QStringLiteral("channelcount"),
QString::number(channel_count_));
writer->writeTextElement(
QStringLiteral("pixelaspectratio"),
QString::fromStdString(pixel_aspect_ratio_.toString()));
writer->writeTextElement(QStringLiteral("interlacing"),
QString::number(interlacing_));
writer->writeTextElement(QStringLiteral("divider"),
QString::number(divider_));
writer->writeTextElement(QStringLiteral("enabled"),
QString::number(enabled_));
writer->writeTextElement(QStringLiteral("x"), QString::number(x_));
writer->writeTextElement(QStringLiteral("y"), QString::number(y_));
writer->writeTextElement(QStringLiteral("streamindex"),
QString::number(stream_index_));
writer->writeTextElement(QStringLiteral("videotype"),
QString::number(video_type_));
writer->writeTextElement(QStringLiteral("framerate"),
QString::fromStdString(frame_rate_.toString()));
writer->writeTextElement(QStringLiteral("starttime"),
QString::number(start_time_));
writer->writeTextElement(QStringLiteral("duration"),
QString::number(duration_));
writer->writeTextElement(QStringLiteral("premultipliedalpha"),
QString::number(premultiplied_alpha_));
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeTextElement(QStringLiteral("colorrange"),
QString::number(color_range_));
}
}
+308 -288
View File
@@ -26,383 +26,403 @@
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
namespace olive {
namespace olive
{
using namespace core;
class VideoParams {
public:
enum Interlacing {
kInterlaceNone,
kInterlacedTopFirst,
kInterlacedBottomFirst
};
enum Interlacing {
kInterlaceNone,
kInterlacedTopFirst,
kInterlacedBottomFirst
};
enum Type {
kVideoTypeVideo,
kVideoTypeStill,
kVideoTypeImageSequence
};
enum ColorRange
{
kColorRangeLimited, // 16_235
kColorRangeFull, // 0-255
enum Type { kVideoTypeVideo, kVideoTypeStill, kVideoTypeImageSequence };
enum ColorRange {
kColorRangeLimited, // 16_235
kColorRangeFull, // 0-255
kColorRangeDefault = kColorRangeLimited
};
kColorRangeDefault = kColorRangeLimited
};
VideoParams();
VideoParams(int width, int height, PixelFormat format, int nb_channels,
const rational &pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
VideoParams(int width, int height, int depth, PixelFormat format,
int nb_channels, const rational &pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
VideoParams(int width, int height, const rational &time_base,
PixelFormat format, int nb_channels,
const rational &pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
VideoParams();
VideoParams(int width, int height, PixelFormat format, int nb_channels,
const rational& pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
VideoParams(int width, int height, int depth,
PixelFormat format, int nb_channels,
const rational& pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
VideoParams(int width, int height, const rational& time_base,
PixelFormat format, int nb_channels,
const rational& pixel_aspect_ratio = 1,
Interlacing interlacing = kInterlaceNone, int divider = 1);
int width() const
{
return width_;
}
int width() const
{
return width_;
}
void set_width(int width)
{
width_ = width;
calculate_effective_size();
}
void set_width(int width)
{
width_ = width;
calculate_effective_size();
}
/**
/**
* @brief Returns width multiplied by pixel aspect ratio where applicable
*/
int square_pixel_width() const
{
return par_width_;
}
int square_pixel_width() const
{
return par_width_;
}
QVector2D resolution() const
{
return QVector2D(width_, height_);
}
QVector2D resolution() const
{
return QVector2D(width_, height_);
}
QVector2D square_resolution() const
{
return QVector2D(par_width_, height_);
}
QVector2D square_resolution() const
{
return QVector2D(par_width_, height_);
}
int height() const
{
return height_;
}
int height() const
{
return height_;
}
void set_height(int height)
{
height_ = height;
calculate_effective_size();
}
void set_height(int height)
{
height_ = height;
calculate_effective_size();
}
int depth() const
{
return depth_;
}
int depth() const
{
return depth_;
}
void set_depth(int depth)
{
depth_ = depth;
calculate_effective_size();
}
void set_depth(int depth)
{
depth_ = depth;
calculate_effective_size();
}
bool is_3d() const { return depth_ > 1; }
bool is_3d() const
{
return depth_ > 1;
}
const rational& time_base() const
{
return time_base_;
}
const rational &time_base() const
{
return time_base_;
}
void set_time_base(const rational& r)
{
time_base_ = r;
}
void set_time_base(const rational &r)
{
time_base_ = r;
}
rational frame_rate_as_time_base() const
{
return frame_rate_.flipped();
}
rational frame_rate_as_time_base() const
{
return frame_rate_.flipped();
}
int divider() const
{
return divider_;
}
int divider() const
{
return divider_;
}
void set_divider(int d)
{
divider_ = d;
calculate_effective_size();
}
void set_divider(int d)
{
divider_ = d;
calculate_effective_size();
}
int effective_width() const
{
return effective_width_;
}
int effective_width() const
{
return effective_width_;
}
int effective_height() const
{
return effective_height_;
}
int effective_height() const
{
return effective_height_;
}
int effective_depth() const
{
return effective_depth_;
}
int effective_depth() const
{
return effective_depth_;
}
PixelFormat format() const
{
return format_;
}
PixelFormat format() const
{
return format_;
}
void set_format(PixelFormat f)
{
format_ = f;
}
void set_format(PixelFormat f)
{
format_ = f;
}
int channel_count() const
{
return channel_count_;
}
int channel_count() const
{
return channel_count_;
}
void set_channel_count(int c)
{
channel_count_ = c;
}
void set_channel_count(int c)
{
channel_count_ = c;
}
const rational& pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
}
const rational &pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
}
void set_pixel_aspect_ratio(const rational& r)
{
pixel_aspect_ratio_ = r;
validate_pixel_aspect_ratio();
}
void set_pixel_aspect_ratio(const rational &r)
{
pixel_aspect_ratio_ = r;
validate_pixel_aspect_ratio();
}
Interlacing interlacing() const
{
return interlacing_;
}
Interlacing interlacing() const
{
return interlacing_;
}
void set_interlacing(Interlacing i)
{
interlacing_ = i;
}
void set_interlacing(Interlacing i)
{
interlacing_ = i;
}
static int generate_auto_divider(qint64 width, qint64 height);
static int generate_auto_divider(qint64 width, qint64 height);
bool is_valid() const;
bool is_valid() const;
bool operator==(const VideoParams& rhs) const;
bool operator!=(const VideoParams& rhs) const;
bool operator==(const VideoParams &rhs) const;
bool operator!=(const VideoParams &rhs) const;
static int GetBytesPerChannel(PixelFormat format);
int GetBytesPerChannel() const
{
return GetBytesPerChannel(format_);
}
static int GetBytesPerChannel(PixelFormat format);
int GetBytesPerChannel() const
{
return GetBytesPerChannel(format_);
}
static int GetBytesPerPixel(PixelFormat format, int channels);
int GetBytesPerPixel() const
{
return GetBytesPerPixel(format_, channel_count_);
}
static int GetBytesPerPixel(PixelFormat format, int channels);
int GetBytesPerPixel() const
{
return GetBytesPerPixel(format_, channel_count_);
}
static int GetBufferSize(int width, int height, PixelFormat format, int channels)
{
return width * height * GetBytesPerPixel(format, channels);
}
int GetBufferSize() const
{
return GetBufferSize(width_, height_, format_, channel_count_);
}
static int GetBufferSize(int width, int height, PixelFormat format,
int channels)
{
return width * height * GetBytesPerPixel(format, channels);
}
int GetBufferSize() const
{
return GetBufferSize(width_, height_, format_, channel_count_);
}
static QString GetNameForDivider(int div);
static QString GetNameForDivider(int div);
static bool FormatIsFloat(PixelFormat format)
{
return format.is_float();
}
static bool FormatIsFloat(PixelFormat format)
{
return format.is_float();
}
static QString GetFormatName(PixelFormat format);
static QString GetFormatName(PixelFormat format);
static int GetDividerForTargetResolution(int src_width, int src_height, int dst_width, int dst_height);
static int GetDividerForTargetResolution(int src_width, int src_height,
int dst_width, int dst_height);
static const int kInternalChannelCount;
static const int kInternalChannelCount;
static const rational kPixelAspectSquare;
static const rational kPixelAspectNTSCStandard;
static const rational kPixelAspectNTSCWidescreen;
static const rational kPixelAspectPALStandard;
static const rational kPixelAspectPALWidescreen;
static const rational kPixelAspect1080Anamorphic;
static const rational kPixelAspectSquare;
static const rational kPixelAspectNTSCStandard;
static const rational kPixelAspectNTSCWidescreen;
static const rational kPixelAspectPALStandard;
static const rational kPixelAspectPALWidescreen;
static const rational kPixelAspect1080Anamorphic;
static const QVector<rational> kSupportedFrameRates;
static const QVector<rational> kStandardPixelAspects;
static const QVector<int> kSupportedDividers;
static const QVector<rational> kSupportedFrameRates;
static const QVector<rational> kStandardPixelAspects;
static const QVector<int> kSupportedDividers;
static const int kHSVChannelCount = 3;
static const int kRGBChannelCount = 3;
static const int kRGBAChannelCount = 4;
static const int kHSVChannelCount = 3;
static const int kRGBChannelCount = 3;
static const int kRGBAChannelCount = 4;
/**
/**
* @brief Convert rational frame rate (i.e. flipped timebase) to a user-friendly string
*/
static QString FrameRateToString(const rational& frame_rate);
static QString FrameRateToString(const rational &frame_rate);
static QStringList GetStandardPixelAspectRatioNames();
static QString FormatPixelAspectRatioString(const QString& format, const rational& ratio);
static QStringList GetStandardPixelAspectRatioNames();
static QString FormatPixelAspectRatioString(const QString &format,
const rational &ratio);
static int GetScaledDimension(int dim, int divider);
static int GetScaledDimension(int dim, int divider);
bool enabled() const
{
return enabled_;
}
bool enabled() const
{
return enabled_;
}
void set_enabled(bool e)
{
enabled_ = e;
}
void set_enabled(bool e)
{
enabled_ = e;
}
float x() const { return x_; }
void set_x(float x) { x_ = x; }
float y() const { return y_; }
void set_y(float y) { y_ = y; }
QVector2D offset() const { return QVector2D(x_, y_); }
float x() const
{
return x_;
}
void set_x(float x)
{
x_ = x;
}
float y() const
{
return y_;
}
void set_y(float y)
{
y_ = y;
}
QVector2D offset() const
{
return QVector2D(x_, y_);
}
int stream_index() const
{
return stream_index_;
}
int stream_index() const
{
return stream_index_;
}
void set_stream_index(int s)
{
stream_index_ = s;
}
void set_stream_index(int s)
{
stream_index_ = s;
}
Type video_type() const
{
return video_type_;
}
Type video_type() const
{
return video_type_;
}
void set_video_type(Type t)
{
video_type_ = t;
}
void set_video_type(Type t)
{
video_type_ = t;
}
const rational& frame_rate() const
{
return frame_rate_;
}
const rational &frame_rate() const
{
return frame_rate_;
}
void set_frame_rate(const rational& frame_rate)
{
frame_rate_ = frame_rate;
}
void set_frame_rate(const rational &frame_rate)
{
frame_rate_ = frame_rate;
}
int64_t start_time() const
{
return start_time_;
}
int64_t start_time() const
{
return start_time_;
}
void set_start_time(int64_t start_time)
{
start_time_ = start_time;
}
void set_start_time(int64_t start_time)
{
start_time_ = start_time;
}
int64_t duration() const
{
return duration_;
}
int64_t duration() const
{
return duration_;
}
void set_duration(int64_t duration)
{
duration_ = duration;
}
void set_duration(int64_t duration)
{
duration_ = duration;
}
bool premultiplied_alpha() const
{
return premultiplied_alpha_;
}
bool premultiplied_alpha() const
{
return premultiplied_alpha_;
}
void set_premultiplied_alpha(bool premultiplied_alpha)
{
premultiplied_alpha_ = premultiplied_alpha;
}
void set_premultiplied_alpha(bool premultiplied_alpha)
{
premultiplied_alpha_ = premultiplied_alpha;
}
const QString& colorspace() const
{
return colorspace_;
}
const QString &colorspace() const
{
return colorspace_;
}
void set_colorspace(const QString& c)
{
colorspace_ = c;
}
void set_colorspace(const QString &c)
{
colorspace_ = c;
}
const ColorRange &color_range() const { return color_range_; }
void set_color_range(const ColorRange &color_range) { color_range_ = color_range; }
const ColorRange &color_range() const
{
return color_range_;
}
void set_color_range(const ColorRange &color_range)
{
color_range_ = color_range;
}
int64_t get_time_in_timebase_units(const rational& time) const;
int64_t get_time_in_timebase_units(const rational &time) const;
void Load(QXmlStreamReader* reader);
void Load(QXmlStreamReader *reader);
void Save(QXmlStreamWriter* writer) const;
void Save(QXmlStreamWriter *writer) const;
private:
void calculate_effective_size();
void calculate_effective_size();
void validate_pixel_aspect_ratio();
void validate_pixel_aspect_ratio();
void set_defaults_for_footage();
void set_defaults_for_footage();
void calculate_square_pixel_width();
void calculate_square_pixel_width();
int width_;
int height_;
int depth_;
rational time_base_;
int width_;
int height_;
int depth_;
rational time_base_;
PixelFormat format_;
PixelFormat format_;
int channel_count_;
int channel_count_;
rational pixel_aspect_ratio_;
rational pixel_aspect_ratio_;
Interlacing interlacing_;
Interlacing interlacing_;
int divider_;
int divider_;
// Cached values
int effective_width_;
int effective_height_;
int effective_depth_;
int par_width_;
bool enabled_;
int stream_index_;
Type video_type_;
rational frame_rate_;
int64_t start_time_;
int64_t duration_;
bool premultiplied_alpha_;
QString colorspace_;
float x_;
float y_;
ColorRange color_range_;
// Cached values
int effective_width_;
int effective_height_;
int effective_depth_;
int par_width_;
bool enabled_;
int stream_index_;
Type video_type_;
rational frame_rate_;
int64_t start_time_;
int64_t duration_;
bool premultiplied_alpha_;
QString colorspace_;
float x_;
float y_;
ColorRange color_range_;
};
}