giant rendering overhaul and many small fixes
This commit is contained in:
+409
-142
@@ -20,12 +20,14 @@
|
||||
|
||||
#include "clip.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "project/transition.h"
|
||||
#include "project/footage.h"
|
||||
#include "io/config.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/cacher.h"
|
||||
#include "rendering/cacher.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/sequence.h"
|
||||
#include "panels/timeline.h"
|
||||
@@ -34,86 +36,133 @@
|
||||
#include "undo.h"
|
||||
#include "debug.h"
|
||||
|
||||
const int kRGBAComponentCount = 4;
|
||||
|
||||
Clip::Clip(SequencePtr s) :
|
||||
sequence(s),
|
||||
enabled(true),
|
||||
clip_in(0),
|
||||
timeline_in(0),
|
||||
timeline_out(0),
|
||||
track(0),
|
||||
media(nullptr),
|
||||
speed(1.0),
|
||||
reverse(false),
|
||||
maintain_audio_pitch(false),
|
||||
autoscale(olive::CurrentConfig.autoscale_by_default),
|
||||
opening_transition(nullptr),
|
||||
closing_transition(nullptr),
|
||||
undeletable(false),
|
||||
replaced(false),
|
||||
ignore_reverse(false),
|
||||
use_existing_frame(false),
|
||||
filter_graph(nullptr),
|
||||
fbo(nullptr),
|
||||
opts(nullptr)
|
||||
cacher(ClipPtr(this))
|
||||
{
|
||||
pkt = av_packet_alloc();
|
||||
enabled_ = true;
|
||||
clip_in_ = 0;
|
||||
timeline_in_ = 0;
|
||||
timeline_out_ = 0;
|
||||
track_ = 0;
|
||||
media_ = nullptr;
|
||||
speed_.value = 1.0;
|
||||
speed_.maintain_audio_pitch = false;
|
||||
reverse_ = false;
|
||||
autoscale_ = olive::CurrentConfig.autoscale_by_default;
|
||||
opening_transition = nullptr;
|
||||
closing_transition = nullptr;
|
||||
undeletable = false;
|
||||
replaced = false;
|
||||
fbo = nullptr;
|
||||
open_ = false;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
ClipPtr Clip::copy(SequencePtr s) {
|
||||
ClipPtr copy(new Clip(s));
|
||||
|
||||
copy->enabled = enabled;
|
||||
copy->name = QString(name);
|
||||
copy->clip_in = clip_in;
|
||||
copy->timeline_in = timeline_in;
|
||||
copy->timeline_out = timeline_out;
|
||||
copy->track = track;
|
||||
copy->color_r = color_r;
|
||||
copy->color_g = color_g;
|
||||
copy->color_b = color_b;
|
||||
copy->media = media;
|
||||
copy->media_stream = media_stream;
|
||||
copy->autoscale = autoscale;
|
||||
copy->speed = speed;
|
||||
copy->maintain_audio_pitch = maintain_audio_pitch;
|
||||
copy->reverse = reverse;
|
||||
copy->set_enabled(enabled());
|
||||
copy->set_name(name());
|
||||
copy->set_clip_in(clip_in());
|
||||
copy->set_timeline_in(timeline_in());
|
||||
copy->set_timeline_out(timeline_out());
|
||||
copy->set_track(track());
|
||||
copy->set_color(color());
|
||||
copy->set_media(media(), media_stream_index());
|
||||
copy->set_autoscaled(autoscaled());
|
||||
copy->set_speed(speed());
|
||||
copy->set_reversed(reversed());
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
copy->effects.append(effects.at(i)->copy(copy));
|
||||
}
|
||||
|
||||
copy->cached_fr = (this->sequence == nullptr) ? cached_fr : this->sequence->frame_rate;
|
||||
copy->set_cached_frame_rate((this->sequence == nullptr) ? cached_frame_rate() : this->sequence->frame_rate);
|
||||
|
||||
copy->recalculateMaxLength();
|
||||
copy->refresh();
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool Clip::IsActiveAt(long timecode)
|
||||
{
|
||||
// these buffers allow clips to be opened and prepared well before they're displayed
|
||||
// as well as closed a little after they're not needed anymore
|
||||
int open_buffer = qCeil(this->sequence->frame_rate*2);
|
||||
int close_buffer = qCeil(this->sequence->frame_rate);
|
||||
|
||||
|
||||
return enabled()
|
||||
&& timeline_in(true) < timecode + open_buffer
|
||||
&& timeline_out(true) > timecode - close_buffer
|
||||
&& timecode - timeline_in(true) + clip_in(true) < media_length();
|
||||
}
|
||||
|
||||
const QColor &Clip::color()
|
||||
{
|
||||
return color_;
|
||||
}
|
||||
|
||||
void Clip::set_color(int r, int g, int b)
|
||||
{
|
||||
color_.setRed(r);
|
||||
color_.setGreen(g);
|
||||
color_.setBlue(b);
|
||||
}
|
||||
|
||||
void Clip::set_color(const QColor &c)
|
||||
{
|
||||
color_ = c;
|
||||
}
|
||||
|
||||
Media *Clip::media()
|
||||
{
|
||||
return media_;
|
||||
}
|
||||
|
||||
FootageStream *Clip::media_stream()
|
||||
{
|
||||
if (media()->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
return media()->to_footage()->get_stream_from_file_index(track() < 0, media_stream_index());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Clip::media_stream_index()
|
||||
{
|
||||
return media_stream_;
|
||||
}
|
||||
|
||||
void Clip::set_media(Media *m, int s)
|
||||
{
|
||||
media_ = m;
|
||||
media_stream_ = s;
|
||||
}
|
||||
|
||||
bool Clip::enabled()
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void Clip::set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
void Clip::reset() {
|
||||
audio_just_reset = false;
|
||||
open = false;
|
||||
finished_opening = false;
|
||||
pkt_written = false;
|
||||
audio_reset = false;
|
||||
frame_sample_index = -1;
|
||||
audio_buffer_write = false;
|
||||
texture_frame = -1;
|
||||
formatCtx = nullptr;
|
||||
stream = nullptr;
|
||||
codec = nullptr;
|
||||
codecCtx = nullptr;
|
||||
texture = nullptr;
|
||||
last_invalid_ts = -1;
|
||||
}
|
||||
|
||||
void Clip::reset_audio() {
|
||||
if (media == nullptr || media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
audio_reset = true;
|
||||
frame_sample_index = -1;
|
||||
audio_buffer_write = 0;
|
||||
} else if (media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
SequencePtr nested_sequence = media->to_sequence();
|
||||
if (UsesCacher()) {
|
||||
cacher.ResetAudio();
|
||||
}
|
||||
if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
SequencePtr nested_sequence = media()->to_sequence();
|
||||
for (int i=0;i<nested_sequence->clips.size();i++) {
|
||||
ClipPtr c = nested_sequence->clips.at(i);
|
||||
if (c != nullptr) c->reset_audio();
|
||||
@@ -123,13 +172,13 @@ void Clip::reset_audio() {
|
||||
|
||||
void Clip::refresh() {
|
||||
// validates media if it was replaced
|
||||
if (replaced && media != nullptr && media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
FootagePtr m = media->to_footage();
|
||||
if (replaced && media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
FootagePtr m = media()->to_footage();
|
||||
|
||||
if (track < 0 && m->video_tracks.size() > 0) {
|
||||
media_stream = m->video_tracks.at(0).file_index;
|
||||
} else if (track >= 0 && m->audio_tracks.size() > 0) {
|
||||
media_stream = m->audio_tracks.at(0).file_index;
|
||||
if (track() < 0 && m->video_tracks.size() > 0) {
|
||||
set_media(media(), m->video_tracks.at(0).file_index);
|
||||
} else if (track() >= 0 && m->audio_tracks.size() > 0) {
|
||||
set_media(media(), m->audio_tracks.at(0).file_index);
|
||||
}
|
||||
}
|
||||
replaced = false;
|
||||
@@ -138,153 +187,206 @@ void Clip::refresh() {
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
effects.at(i)->refresh();
|
||||
}
|
||||
|
||||
recalculateMaxLength();
|
||||
}
|
||||
|
||||
void Clip::queue_clear() {
|
||||
while (queue.size() > 0) {
|
||||
av_frame_free(&queue.first());
|
||||
queue.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::queue_remove_earliest() {
|
||||
int earliest_frame = 0;
|
||||
for (int i=1;i<queue.size();i++) {
|
||||
// TODO/NOTE: this will not work on sped up AND reversed video
|
||||
if (queue.at(i)->pts < queue.at(earliest_frame)->pts) {
|
||||
earliest_frame = i;
|
||||
}
|
||||
}
|
||||
av_frame_free(&queue[earliest_frame]);
|
||||
queue.removeAt(earliest_frame);
|
||||
}
|
||||
|
||||
QVector<Marker> &Clip::get_markers() {
|
||||
if (media != nullptr) {
|
||||
return media->get_markers();
|
||||
if (media() != nullptr) {
|
||||
return media()->get_markers();
|
||||
}
|
||||
return markers;
|
||||
}
|
||||
|
||||
Clip::~Clip() {
|
||||
if (open) {
|
||||
close_clip(ClipPtr(this), true);
|
||||
if (IsOpen()) {
|
||||
Close(true);
|
||||
}
|
||||
|
||||
effects.clear();
|
||||
av_packet_free(&pkt);
|
||||
}
|
||||
|
||||
long Clip::get_clip_in_with_transition() {
|
||||
if (opening_transition != nullptr && opening_transition->secondary_clip != nullptr) {
|
||||
long Clip::clip_in(bool with_transition) {
|
||||
if (with_transition && opening_transition != nullptr && opening_transition->secondary_clip != nullptr) {
|
||||
// we must be the secondary clip, so return (timeline in - length)
|
||||
return clip_in - opening_transition->get_true_length();
|
||||
return clip_in_ - opening_transition->get_true_length();
|
||||
}
|
||||
return clip_in;
|
||||
return clip_in_;
|
||||
}
|
||||
|
||||
long Clip::get_timeline_in_with_transition() {
|
||||
if (opening_transition != nullptr && opening_transition->secondary_clip != nullptr) {
|
||||
void Clip::set_clip_in(long c)
|
||||
{
|
||||
clip_in_ = c;
|
||||
}
|
||||
|
||||
long Clip::timeline_in(bool with_transition) {
|
||||
if (with_transition && opening_transition != nullptr && opening_transition->secondary_clip != nullptr) {
|
||||
// we must be the secondary clip, so return (timeline in - length)
|
||||
return timeline_in - opening_transition->get_true_length();
|
||||
return timeline_in_ - opening_transition->get_true_length();
|
||||
}
|
||||
return timeline_in;
|
||||
return timeline_in_;
|
||||
}
|
||||
|
||||
long Clip::get_timeline_out_with_transition() {
|
||||
if (closing_transition != nullptr && closing_transition->secondary_clip != nullptr) {
|
||||
void Clip::set_timeline_in(long t)
|
||||
{
|
||||
timeline_in_ = t;
|
||||
}
|
||||
|
||||
long Clip::timeline_out(bool with_transitions) {
|
||||
if (with_transitions && closing_transition != nullptr && closing_transition->secondary_clip != nullptr) {
|
||||
// we must be the primary clip, so return (timeline out + length2)
|
||||
return timeline_out + closing_transition->get_true_length();
|
||||
return timeline_out_ + closing_transition->get_true_length();
|
||||
} else {
|
||||
return timeline_out;
|
||||
return timeline_out_;
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::set_timeline_out(long t)
|
||||
{
|
||||
timeline_out_ = t;
|
||||
}
|
||||
|
||||
bool Clip::reversed()
|
||||
{
|
||||
return reverse_;
|
||||
}
|
||||
|
||||
void Clip::set_reversed(bool r)
|
||||
{
|
||||
reverse_ = r;
|
||||
}
|
||||
|
||||
bool Clip::autoscaled()
|
||||
{
|
||||
return autoscale_;
|
||||
}
|
||||
|
||||
void Clip::set_autoscaled(bool b)
|
||||
{
|
||||
autoscale_ = b;
|
||||
}
|
||||
|
||||
double Clip::cached_frame_rate()
|
||||
{
|
||||
return cached_fr_;
|
||||
}
|
||||
|
||||
void Clip::set_cached_frame_rate(double d)
|
||||
{
|
||||
cached_fr_ = d;
|
||||
}
|
||||
|
||||
const QString &Clip::name()
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
void Clip::set_name(const QString &s)
|
||||
{
|
||||
name_ = s;
|
||||
}
|
||||
|
||||
const ClipSpeed& Clip::speed()
|
||||
{
|
||||
return speed_;
|
||||
}
|
||||
|
||||
void Clip::set_speed(const ClipSpeed& d)
|
||||
{
|
||||
speed_ = d;
|
||||
}
|
||||
|
||||
AVRational Clip::time_base()
|
||||
{
|
||||
return cacher.media_time_base();
|
||||
}
|
||||
|
||||
int Clip::track()
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
|
||||
void Clip::set_track(int t)
|
||||
{
|
||||
track_ = t;
|
||||
}
|
||||
|
||||
// timeline functions
|
||||
long Clip::getLength() {
|
||||
return timeline_out - timeline_in;
|
||||
long Clip::length() {
|
||||
return timeline_out_ - timeline_in_;
|
||||
}
|
||||
|
||||
double Clip::getMediaFrameRate() {
|
||||
Q_ASSERT(track < 0);
|
||||
if (media != nullptr) {
|
||||
double rate = media->get_frame_rate(media_stream);
|
||||
double Clip::media_frame_rate() {
|
||||
Q_ASSERT(track_ < 0);
|
||||
if (media_ != nullptr) {
|
||||
double rate = media_->get_frame_rate(media_stream_index());
|
||||
if (!qIsNaN(rate)) return rate;
|
||||
}
|
||||
if (sequence != nullptr) return sequence->frame_rate;
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
void Clip::recalculateMaxLength() {
|
||||
long Clip::media_length() {
|
||||
if (this->sequence != nullptr) {
|
||||
double fr = this->sequence->frame_rate;
|
||||
|
||||
fr /= speed;
|
||||
fr /= speed_.value;
|
||||
|
||||
calculated_length = LONG_MAX;
|
||||
|
||||
if (media != nullptr) {
|
||||
switch (media->get_type()) {
|
||||
if (media_ == nullptr) {
|
||||
return LONG_MAX;
|
||||
} else {
|
||||
switch (media_->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
FootagePtr m = media->to_footage();
|
||||
const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream);
|
||||
FootagePtr m = media_->to_footage();
|
||||
const FootageStream* ms = m->get_stream_from_file_index(track_ < 0, media_stream_index());
|
||||
if (ms != nullptr && ms->infinite_length) {
|
||||
calculated_length = LONG_MAX;
|
||||
return LONG_MAX;
|
||||
} else {
|
||||
calculated_length = m->get_length_in_frames(fr);
|
||||
return m->get_length_in_frames(fr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
SequencePtr s = media->to_sequence();
|
||||
calculated_length = refactor_frame_number(s->getEndFrame(), s->frame_rate, fr);
|
||||
SequencePtr s = media_->to_sequence();
|
||||
return rescale_frame_number(s->getEndFrame(), s->frame_rate, fr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long Clip::getMaximumLength() {
|
||||
return calculated_length;
|
||||
}
|
||||
|
||||
int Clip::getWidth() {
|
||||
if (media == nullptr && sequence != nullptr) return sequence->width;
|
||||
switch (media->get_type()) {
|
||||
int Clip::media_width() {
|
||||
if (media_ == nullptr && sequence != nullptr) return sequence->width;
|
||||
switch (media_->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
const FootageStream* ms = media_stream();
|
||||
if (ms != nullptr) return ms->video_width;
|
||||
if (sequence != nullptr) return sequence->width;
|
||||
break;
|
||||
}
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
SequencePtr s = media->to_sequence();
|
||||
SequencePtr s = media_->to_sequence();
|
||||
return s->width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Clip::getHeight() {
|
||||
if (media == nullptr && sequence != nullptr) return sequence->height;
|
||||
switch (media->get_type()) {
|
||||
int Clip::media_height() {
|
||||
if (media_ == nullptr && sequence != nullptr) return sequence->height;
|
||||
switch (media_->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
const FootageStream* ms = media_stream();
|
||||
if (ms != nullptr) return ms->video_height;
|
||||
if (sequence != nullptr) return sequence->height;
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
SequencePtr s = media->to_sequence();
|
||||
SequencePtr s = media_->to_sequence();
|
||||
return s->height;
|
||||
}
|
||||
}
|
||||
@@ -294,10 +396,10 @@ int Clip::getHeight() {
|
||||
void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points) {
|
||||
if (change_timeline_points) {
|
||||
move_clip(ca, ClipPtr(this),
|
||||
qRound((double) timeline_in * multiplier),
|
||||
qRound((double) timeline_out * multiplier),
|
||||
qRound((double) clip_in * multiplier),
|
||||
track);
|
||||
qRound(double(timeline_in_) * multiplier),
|
||||
qRound(double(timeline_out_) * multiplier),
|
||||
qRound(double(clip_in_) * multiplier),
|
||||
track_);
|
||||
}
|
||||
|
||||
// move keyframes
|
||||
@@ -308,9 +410,174 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t
|
||||
for (int l=0;l<r->fieldCount();l++) {
|
||||
EffectField* f = r->field(l);
|
||||
for (int k=0;k<f->keyframes.size();k++) {
|
||||
ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, f->keyframes[k].time * multiplier));
|
||||
ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, qRound(f->keyframes[k].time * multiplier)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::Open() {
|
||||
if (!open_ && state_change_lock.tryLock()) {
|
||||
open_ = true;
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
effects.at(i)->open();
|
||||
}
|
||||
|
||||
// reset variable used to optimize uploading frame data
|
||||
texture_frame = -1;
|
||||
|
||||
if (UsesCacher()) {
|
||||
// cacher will unlock open_lock
|
||||
cacher.Open();
|
||||
} else {
|
||||
// this media doesn't use a cacher, so we unlock here
|
||||
state_change_lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::Close(bool wait) {
|
||||
// thread safety, prevents Close() running from two separate threads simultaneously
|
||||
if (open_ && state_change_lock.tryLock()) {
|
||||
open_ = false;
|
||||
|
||||
if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
close_active_clips(media()->to_sequence());
|
||||
}
|
||||
|
||||
// destroy opengl texture in main thread
|
||||
delete texture;
|
||||
texture = nullptr;
|
||||
|
||||
// close all effects
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i)->is_open()) {
|
||||
effects.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
// delete framebuffers
|
||||
if (fbo != nullptr) {
|
||||
// delete 3 fbos for nested sequences, 2 for most clips
|
||||
int fbo_count = (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) ? 3 : 2;
|
||||
|
||||
for (int j=0;j<fbo_count;j++) {
|
||||
delete fbo[j];
|
||||
}
|
||||
|
||||
delete [] fbo;
|
||||
|
||||
fbo = nullptr;
|
||||
}
|
||||
|
||||
if (UsesCacher()) {
|
||||
cacher.Close(wait);
|
||||
} else {
|
||||
state_change_lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Clip::IsOpen()
|
||||
{
|
||||
return open_;
|
||||
}
|
||||
|
||||
void Clip::Cache(long playhead, bool scrubbing, QVector<ClipPtr>& nests, int playback_speed) {
|
||||
// qint64 time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
cacher.Cache(playhead, scrubbing, nests, playback_speed);
|
||||
cacher_frame = playhead;
|
||||
|
||||
// qDebug() << "Clip::Cache took" << (QDateTime::currentMSecsSinceEpoch() - time);
|
||||
}
|
||||
|
||||
bool Clip::Retrieve()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (UsesCacher()) {
|
||||
|
||||
// qint64 time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
AVFrame* frame = cacher.Retrieve();
|
||||
|
||||
cacher.QueueLock();
|
||||
|
||||
if (frame != nullptr) {
|
||||
|
||||
// check if the opengl texture exists yet, create it if not
|
||||
if (texture == nullptr) {
|
||||
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
|
||||
// the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure
|
||||
// the texture is using the correct dimensions, but then treat it as if it's the original resolution in the
|
||||
// composition
|
||||
texture->setSize(cacher.media_width(), cacher.media_height());
|
||||
|
||||
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||
texture->setMipLevels(texture->maximumMipLevels());
|
||||
texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount);
|
||||
|
||||
// 2 data buffers to ping-pong between
|
||||
bool using_db_1 = true;
|
||||
uint8_t* data_buffer_1 = frame->data[0];
|
||||
uint8_t* data_buffer_2 = nullptr;
|
||||
|
||||
int frame_size = frame->linesize[0]*frame->height;
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
EffectPtr e = effects.at(i);
|
||||
if (e->enable_image && e->is_enabled()) {
|
||||
if (data_buffer_1 == frame->data[0]) {
|
||||
data_buffer_1 = new uint8_t[frame_size];
|
||||
data_buffer_2 = new uint8_t[frame_size];
|
||||
|
||||
memcpy(data_buffer_1, frame->data[0], frame_size);
|
||||
}
|
||||
|
||||
e->process_image(get_timecode(ClipPtr(this), cacher_frame),
|
||||
using_db_1 ? data_buffer_1 : data_buffer_2,
|
||||
using_db_1 ? data_buffer_2 : data_buffer_1,
|
||||
frame_size
|
||||
);
|
||||
|
||||
using_db_1 = !using_db_1;
|
||||
}
|
||||
}
|
||||
|
||||
texture->setData(QOpenGLTexture::RGBA,
|
||||
QOpenGLTexture::UInt8,
|
||||
const_cast<const uint8_t*>(using_db_1 ? data_buffer_1 : data_buffer_2));
|
||||
|
||||
if (data_buffer_1 != frame->data[0]) {
|
||||
qDebug() << data_buffer_1 << frame->data[0];
|
||||
delete [] data_buffer_1;
|
||||
delete [] data_buffer_2;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
// qDebug() << "Clip::Retrieve took" << (QDateTime::currentMSecsSinceEpoch() - time);
|
||||
|
||||
ret = true;
|
||||
} else {
|
||||
qCritical() << "Failed to retrieve frame for clip" << name();
|
||||
}
|
||||
|
||||
cacher.QueueUnlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Clip::UsesCacher()
|
||||
{
|
||||
return track() >= 0 || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user