change: change code style to Linux style except indent.
This commit is contained in:
@@ -21,13 +21,10 @@
|
||||
#ifndef ALPHAASSOC_H
|
||||
#define ALPHAASSOC_H
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
enum AlphaAssociated {
|
||||
kAlphaNone,
|
||||
kAlphaUnassociated,
|
||||
kAlphaAssociated
|
||||
};
|
||||
enum AlphaAssociated { kAlphaNone, kAlphaUnassociated, kAlphaAssociated };
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ¶ms)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 ¶ms);
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>;
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using ColorProcessorCache = QHash<QString, ColorProcessorPtr>;
|
||||
|
||||
|
||||
+44
-39
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "acceleratedjob.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 ¶ms, const void* data, int linesize) override;
|
||||
virtual void UploadToTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) override;
|
||||
|
||||
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) override;
|
||||
virtual void DownloadFromTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, 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
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+530
-492
File diff suppressed because it is too large
Load Diff
+115
-97
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 ¶ms, const void *data, int linesize)
|
||||
TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, 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 ¶ms)
|
||||
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom,
|
||||
const VideoParams ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v,
|
||||
const VideoParams ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
|
||||
Texture *destination, const VideoParams ¶ms)
|
||||
{
|
||||
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
@@ -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 ¶ms,
|
||||
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 ¶ms);
|
||||
void BlitColorManaged(const ColorTransformJob &job, Texture* destination)
|
||||
{
|
||||
BlitColorManaged(job, destination, destination->params());
|
||||
}
|
||||
void BlitColorManaged(const ColorTransformJob &job, const VideoParams ¶ms)
|
||||
{
|
||||
BlitColorManaged(job, nullptr, params);
|
||||
}
|
||||
void BlitColorManaged(const ColorTransformJob &color_job,
|
||||
Texture *destination, const VideoParams ¶ms);
|
||||
void BlitColorManaged(const ColorTransformJob &job, Texture *destination)
|
||||
{
|
||||
BlitColorManaged(job, destination, destination->params());
|
||||
}
|
||||
void BlitColorManaged(const ColorTransformJob &job,
|
||||
const VideoParams ¶ms)
|
||||
{
|
||||
BlitColorManaged(job, nullptr, params);
|
||||
}
|
||||
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms);
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom,
|
||||
const VideoParams ¶ms);
|
||||
|
||||
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 ¶ms, const void* data, int linesize) = 0;
|
||||
virtual void UploadToTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) = 0;
|
||||
|
||||
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) = 0;
|
||||
virtual void DownloadFromTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, 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 ¶ms);
|
||||
|
||||
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms);
|
||||
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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 ¶ms)
|
||||
{
|
||||
// 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 ¶ms)
|
||||
{
|
||||
// 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
@@ -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 ¶ms);
|
||||
RenderTicketPtr RenderFrame(const RenderVideoParams ¶ms);
|
||||
|
||||
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 ¶ms);
|
||||
RenderTicketPtr RenderAudio(const RenderAudioParams ¶ms);
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+458
-383
File diff suppressed because it is too large
Load Diff
@@ -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 ¶ms, int sample_count) override
|
||||
{
|
||||
return SampleBuffer(params, sample_count);
|
||||
}
|
||||
virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms,
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 ¶m)
|
||||
: 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 ¶m)
|
||||
: 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 ¶ms() 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
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user