giant rendering overhaul and many small fixes

This commit is contained in:
itsmattkc
2019-02-25 13:47:18 -08:00
parent b9d7efa0bc
commit c3373524b1
1429 changed files with 10234 additions and 194593 deletions
+409 -142
View File
@@ -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);
}
+86 -69
View File
@@ -28,7 +28,7 @@
#include <QOpenGLFramebufferObject>
#include <QOpenGLTexture>
#include "playback/cacher.h"
#include "rendering/cacher.h"
#include "project/effect.h"
#include "project/transition.h"
@@ -43,6 +43,11 @@ extern "C" {
#include <libavfilter/avfilter.h>
}
struct ClipSpeed {
double value;
bool maintain_audio_pitch;
};
using ClipPtr = std::shared_ptr<Clip>;
class Sequence;
@@ -53,43 +58,63 @@ public:
Clip(SequencePtr s);
~Clip();
ClipPtr copy(SequencePtr s);
bool IsActiveAt(long timecode);
const QColor& color();
void set_color(int r, int g, int b);
void set_color(const QColor& c);
Media* media();
FootageStream* media_stream();
int media_stream_index();
int media_width();
int media_height();
double media_frame_rate();
long media_length();
void set_media(Media* m, int s);
bool enabled();
void set_enabled(bool e);
long clip_in(bool with_transition = false);
void set_clip_in(long c);
long timeline_in(bool with_transition = false);
void set_timeline_in(long t);
long timeline_out(bool with_transition = false);
void set_timeline_out(long t);
int track();
void set_track(int t);
bool reversed();
void set_reversed(bool r);
bool autoscaled();
void set_autoscaled(bool b);
double cached_frame_rate();
void set_cached_frame_rate(double d);
const QString& name();
void set_name(const QString& s);
const ClipSpeed& speed();
void set_speed(const ClipSpeed& s);
AVRational time_base();
void reset_audio();
void reset();
void refresh();
long get_clip_in_with_transition();
long get_timeline_in_with_transition();
long get_timeline_out_with_transition();
long getLength();
double getMediaFrameRate();
long getMaximumLength();
void recalculateMaxLength();
int getWidth();
int getHeight();
long length();
void refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points);
SequencePtr sequence;
// queue functions
void queue_clear();
void queue_remove_earliest();
// timeline variables (should be copied in copy())
bool enabled;
long clip_in;
long timeline_in;
long timeline_out;
int track;
QString name;
quint8 color_r;
quint8 color_g;
quint8 color_b;
Media* media;
int media_stream;
double speed;
double cached_fr;
bool reverse;
bool maintain_audio_pitch;
bool autoscale;
// markers
QVector<Marker>& get_markers();
@@ -99,58 +124,50 @@ public:
TransitionPtr opening_transition;
TransitionPtr closing_transition;
// media handling
AVFormatContext* formatCtx;
AVStream* stream;
AVCodec* codec;
AVCodecContext* codecCtx;
AVPacket* pkt;
AVFrame* frame;
AVDictionary* opts;
long calculated_length;
// playback functions
void Open();
void Cache(long playhead, bool scrubbing, QVector<ClipPtr> &nests, int playback_speed);
bool Retrieve();
void Close(bool wait);
bool IsOpen();
bool UsesCacher();
// temporary variables
int load_id;
bool undeletable;
bool reached_end;
bool pkt_written;
bool open;
bool finished_opening;
bool replaced;
bool ignore_reverse;
int pix_fmt;
// caching functions
bool use_existing_frame;
bool multithreaded;
Cacher* cacher;
QWaitCondition can_cache;
QVector<AVFrame*> queue;
QMutex queue_lock;
QMutex render_lock;
QMutex lock;
QMutex open_lock;
int64_t last_invalid_ts;
// converters/filters
AVFilterGraph* filter_graph;
AVFilterContext* buffersink_ctx;
AVFilterContext* buffersrc_ctx;
QMutex state_change_lock;
QMutex cache_lock;
// video playback variables
QOpenGLFramebufferObject** fbo;
QOpenGLTexture* texture;
long texture_frame;
// audio playback variables
int64_t reverse_target;
int frame_sample_index;
qint64 audio_buffer_write;
bool audio_reset;
bool audio_just_reset;
long audio_target_frame;
private:
// timeline variables (should be copied in copy())
bool enabled_;
long clip_in_;
long timeline_in_;
long timeline_out_;
int track_;
QString name_;
Media* media_;
int media_stream_;
ClipSpeed speed_;
double cached_fr_;
bool reverse_;
bool autoscale_;
Cacher cacher;
long cacher_frame;
QVector<Marker> markers;
QColor color_;
bool open_;
};
#endif // CLIP_H
+5 -5
View File
@@ -862,7 +862,7 @@ EffectPtr Effect::copy(ClipPtr c) {
}
void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) {
glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight());
glslProgram->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height());
glslProgram->setUniformValue("time", GLfloat(timecode));
glslProgram->setUniformValue("iteration", iteration);
@@ -904,8 +904,8 @@ GLuint Effect::process_superimpose(double timecode) {
bool dimensions_changed = false;
bool redrew_image = false;
int width = parent_clip->getWidth();
int height = parent_clip->getHeight();
int width = parent_clip->media_width();
int height = parent_clip->media_height();
if (width != img.width() || height != img.height()) {
img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied);
@@ -1003,8 +1003,8 @@ void Effect::redraw(double) {
painter_wrapper.painter = &p;
jsEngine.globalObject().setProperty("painter", wrapper_obj);
jsEngine.globalObject().setProperty("width", parent_clip->getWidth());
jsEngine.globalObject().setProperty("height", parent_clip->getHeight());
jsEngine.globalObject().setProperty("width", parent_clip->media_width());
jsEngine.globalObject().setProperty("height", parent_clip->media_height());
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
+6 -4
View File
@@ -120,7 +120,7 @@ void EffectRow::goto_previous_key() {
for (int i=0;i<fieldCount();i++) {
EffectField* f = field(i);
for (int j=0;j<f->keyframes.size();j++) {
long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in;
long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in();
if (comp < olive::ActiveSequence->playhead) {
key = qMax(comp, key);
}
@@ -136,7 +136,7 @@ void EffectRow::toggle_key() {
for (int j=0;j<fieldCount();j++) {
EffectField* f = field(j);
for (int i=0;i<f->keyframes.size();i++) {
long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time;
long comp = c->timeline_in() - c->clip_in() + f->keyframes.at(i).time;
if (comp == olive::ActiveSequence->playhead) {
key_fields.append(f);
key_field_index.append(i);
@@ -163,7 +163,7 @@ void EffectRow::goto_next_key() {
for (int i=0;i<fieldCount();i++) {
EffectField* f = field(i);
for (int j=0;j<f->keyframes.size();j++) {
long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in;
long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in();
if (comp > olive::ActiveSequence->playhead) {
key = qMin(comp, key);
}
@@ -194,7 +194,9 @@ void EffectRow::add_widget(QWidget* w) {
}
void EffectRow::set_keyframe_now(ComboAction* ca) {
long time = olive::ActiveSequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in;
long time = olive::ActiveSequence->playhead
- parent_effect->parent_clip->timeline_in()
+ parent_effect->parent_clip->clip_in();
if (!just_made_unsafe_keyframe) {
EffectKeyframe key;
-4
View File
@@ -25,10 +25,6 @@
#include <QPainter>
#include "io/previewgenerator.h"
extern "C" {
#include <libavformat/avformat.h>
}
#include "project/clip.h"
Footage::Footage() :
+53 -49
View File
@@ -21,6 +21,10 @@
#ifndef FOOTAGE_H
#define FOOTAGE_H
extern "C" {
#include <libavformat/avformat.h>
}
#include <memory>
#include <QString>
#include <QVector>
@@ -33,9 +37,9 @@
#include "project/marker.h"
enum VideoInterlacingMode {
VIDEO_PROGRESSIVE,
VIDEO_TOP_FIELD_FIRST,
VIDEO_BOTTOM_FIELD_FIRST
VIDEO_PROGRESSIVE,
VIDEO_TOP_FIELD_FIRST,
VIDEO_BOTTOM_FIELD_FIRST
};
class Sequence;
@@ -43,62 +47,62 @@ class Clip;
class PreviewGenerator;
struct FootageStream {
int file_index;
int video_width;
int video_height;
bool infinite_length;
double video_frame_rate;
int video_interlacing;
int video_auto_interlacing;
int audio_channels;
int audio_layout;
int audio_frequency;
bool enabled;
int file_index;
int video_width;
int video_height;
bool infinite_length;
double video_frame_rate;
int video_interlacing;
int video_auto_interlacing;
int audio_channels;
int audio_layout;
int audio_frequency;
bool enabled;
// preview thumbnail/waveform
bool preview_done;
QImage video_preview;
QIcon video_preview_square;
QVector<char> audio_preview;
void make_square_thumb();
// preview thumbnail/waveform
bool preview_done;
QImage video_preview;
QIcon video_preview_square;
QVector<char> audio_preview;
void make_square_thumb();
};
struct Footage {
Footage();
~Footage();
Footage();
~Footage();
// footage metadata
QString url;
QString name;
int64_t length;
QVector<FootageStream> video_tracks;
QVector<FootageStream> audio_tracks;
int save_id;
bool ready;
bool invalid;
double speed;
bool alpha_is_premultiplied;
// footage metadata
QString url;
QString name;
int64_t length;
QVector<FootageStream> video_tracks;
QVector<FootageStream> audio_tracks;
int save_id;
bool ready;
bool invalid;
double speed;
bool alpha_is_premultiplied;
// proxy config
bool proxy;
QString proxy_path;
// proxy config
bool proxy;
QString proxy_path;
// thumbnail/waveform generation
PreviewGenerator* preview_gen;
QMutex ready_lock;
// thumbnail/waveform generation
PreviewGenerator* preview_gen;
QMutex ready_lock;
// in/out points
bool using_inout;
long in;
long out;
// in/out points
bool using_inout;
long in;
long out;
// markers
QVector<Marker> markers;
// markers
QVector<Marker> markers;
// functions
long get_length_in_frames(double frame_rate);
FootageStream *get_stream_from_file_index(bool video, int index);
void reset();
// functions
long get_length_in_frames(double frame_rate);
FootageStream *get_stream_from_file_index(bool video, int index);
void reset();
};
using FootagePtr = std::shared_ptr<Footage>;
+1 -1
View File
@@ -80,7 +80,7 @@ void set_marker_internal(SequencePtr seq, const QVector<int>& clips) {
foreach (int i, clips) {
ClipPtr c = seq->clips.at(i);
ca->append(new AddMarkerAction(&c->get_markers(),
seq->playhead - c->timeline_in + c->clip_in,
seq->playhead - c->timeline_in() + c->clip_in(),
marker_name));
}
+231 -231
View File
@@ -34,332 +34,332 @@
#include "debug.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
QString get_interlacing_name(int interlacing) {
switch (interlacing) {
case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)");
case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Top Field First");
case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Bottom Field First");
default: return QCoreApplication::translate("InterlacingName", "Invalid");
}
switch (interlacing) {
case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)");
case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Top Field First");
case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Bottom Field First");
default: return QCoreApplication::translate("InterlacingName", "Invalid");
}
}
QString get_channel_layout_name(int channels, uint64_t layout) {
switch (channels) {
case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid");
case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono");
case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo");
default: {
char buf[50];
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
return QString(buf);
}
}
switch (channels) {
case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid");
case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono");
case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo");
default: {
char buf[50];
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
return QString(buf);
}
}
}
Media::Media(Media* iparent) {
parent = iparent;
root = false;
type = -1;
parent = iparent;
root = false;
type = -1;
}
Media::~Media() {
for (int i=0;i<children.size();i++) {
delete children.at(i);
}
for (int i=0;i<children.size();i++) {
delete children.at(i);
}
}
FootagePtr Media::to_footage() {
return std::static_pointer_cast<Footage>(object);
return std::static_pointer_cast<Footage>(object);
}
SequencePtr Media::to_sequence() {
return std::static_pointer_cast<Sequence>(object);
return std::static_pointer_cast<Sequence>(object);
}
void Media::set_footage(FootagePtr f) {
type = MEDIA_TYPE_FOOTAGE;
object = VoidPtr(f);
type = MEDIA_TYPE_FOOTAGE;
object = VoidPtr(f);
}
void Media::set_sequence(SequencePtr s) {
set_icon(QIcon(":/icons/sequence.png"));
type = MEDIA_TYPE_SEQUENCE;
object = VoidPtr(s);
if (s != nullptr) update_tooltip();
set_icon(QIcon(":/icons/sequence.png"));
type = MEDIA_TYPE_SEQUENCE;
object = VoidPtr(s);
if (s != nullptr) update_tooltip();
}
void Media::set_folder() {
if (folder_name.isEmpty()) folder_name = QCoreApplication::translate("Media", "New Folder");
set_icon(QIcon(":/icons/folder.png"));
type = MEDIA_TYPE_FOLDER;
object = nullptr;
if (folder_name.isEmpty()) folder_name = QCoreApplication::translate("Media", "New Folder");
set_icon(QIcon(":/icons/folder.png"));
type = MEDIA_TYPE_FOLDER;
object = nullptr;
}
void Media::set_icon(const QIcon &ico) {
icon = ico;
icon = ico;
}
void Media::set_parent(Media *p) {
parent = p;
parent = p;
}
void Media::update_tooltip(const QString& error) {
switch (type) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
tooltip = QCoreApplication::translate("Media", "Name:") + " " + f->name + "\n" + QCoreApplication::translate("Media", "Filename:") + " " + f->url + "\n";
switch (type) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
tooltip = QCoreApplication::translate("Media", "Name:") + " " + f->name + "\n" + QCoreApplication::translate("Media", "Filename:") + " " + f->url + "\n";
if (error.isEmpty()) {
if (f->video_tracks.size() > 0) {
tooltip += QCoreApplication::translate("Media", "Video Dimensions:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height);
}
tooltip += "\n";
if (error.isEmpty()) {
if (f->video_tracks.size() > 0) {
tooltip += QCoreApplication::translate("Media", "Video Dimensions:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height);
}
tooltip += "\n";
if (!f->video_tracks.at(0).infinite_length) {
tooltip += QCoreApplication::translate("Media", "Frame Rate:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) {
tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed);
} else {
double adjusted_rate = f->video_tracks.at(i).video_frame_rate * f->speed;
if (!f->video_tracks.at(0).infinite_length) {
tooltip += QCoreApplication::translate("Media", "Frame Rate:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) {
tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed);
} else {
double adjusted_rate = f->video_tracks.at(i).video_frame_rate * f->speed;
tooltip += QCoreApplication::translate("Media", "%1 field(s) (%2 frame(s))").arg(
QString::number(adjusted_rate*2.0),
QString::number(adjusted_rate)
);
}
}
tooltip += "\n";
}
tooltip += QCoreApplication::translate("Media", "%1 field(s) (%2 frame(s))").arg(
QString::number(adjusted_rate*2.0),
QString::number(adjusted_rate)
);
}
}
tooltip += "\n";
}
tooltip += QCoreApplication::translate("Media", "Interlacing:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing);
}
}
tooltip += QCoreApplication::translate("Media", "Interlacing:") + " ";
for (int i=0;i<f->video_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing);
}
}
if (f->audio_tracks.size() > 0) {
tooltip += "\n";
if (f->audio_tracks.size() > 0) {
tooltip += "\n";
tooltip += QCoreApplication::translate("Media", "Audio Frequency:") + " ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->audio_tracks.at(i).audio_frequency * f->speed);
}
tooltip += "\n";
tooltip += QCoreApplication::translate("Media", "Audio Frequency:") + " ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += QString::number(f->audio_tracks.at(i).audio_frequency * f->speed);
}
tooltip += "\n";
tooltip += QCoreApplication::translate("Media", "Audio Channels:") + " ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout);
}
// tooltip += "\n";
}
} else {
tooltip += error;
}
}
break;
case MEDIA_TYPE_SEQUENCE:
{
SequencePtr s = to_sequence();
tooltip += QCoreApplication::translate("Media", "Audio Channels:") + " ";
for (int i=0;i<f->audio_tracks.size();i++) {
if (i > 0) {
tooltip += ", ";
}
tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout);
}
// tooltip += "\n";
}
} else {
tooltip += error;
}
}
break;
case MEDIA_TYPE_SEQUENCE:
{
SequencePtr s = to_sequence();
tooltip = QCoreApplication::translate("Media", "Name: %1"
"\nVideo Dimensions: %2x%3"
"\nFrame Rate: %4"
"\nAudio Frequency: %5"
"\nAudio Layout: %6").arg(
s->name,
QString::number(s->width),
QString::number(s->height),
QString::number(s->frame_rate),
QString::number(s->audio_frequency),
get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout)
);
}
break;
}
tooltip = QCoreApplication::translate("Media", "Name: %1"
"\nVideo Dimensions: %2x%3"
"\nFrame Rate: %4"
"\nAudio Frequency: %5"
"\nAudio Layout: %6").arg(
s->name,
QString::number(s->width),
QString::number(s->height),
QString::number(s->frame_rate),
QString::number(s->audio_frequency),
get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout)
);
}
break;
}
}
VoidPtr Media::to_object() {
return object;
return object;
}
int Media::get_type() {
return type;
return type;
}
const QString &Media::get_name() {
switch (type) {
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
default: return folder_name;
}
switch (type) {
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
default: return folder_name;
}
}
void Media::set_name(const QString &n) {
switch (type) {
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
case MEDIA_TYPE_FOLDER: folder_name = n; break;
}
switch (type) {
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
case MEDIA_TYPE_FOLDER: folder_name = n; break;
}
}
double Media::get_frame_rate(int stream) {
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
if (stream < 0) return f->video_tracks.at(0).video_frame_rate * f->speed;
return f->get_stream_from_file_index(true, stream)->video_frame_rate * f->speed;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
}
return 0;
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
if (stream < 0) return f->video_tracks.at(0).video_frame_rate * f->speed;
return f->get_stream_from_file_index(true, stream)->video_frame_rate * f->speed;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
}
return 0;
}
int Media::get_sampling_rate(int stream) {
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
if (stream < 0) return f->audio_tracks.at(0).audio_frequency * f->speed;
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency * f->speed;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
}
return 0;
switch (get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
FootagePtr f = to_footage();
if (stream < 0) return f->audio_tracks.at(0).audio_frequency * f->speed;
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency * f->speed;
}
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
}
return 0;
}
void Media::appendChild(Media *child) {
child->set_parent(this);
children.append(child);
child->set_parent(this);
children.append(child);
}
bool Media::setData(int col, const QVariant &value) {
if (col == 0) {
QString n = value.toString();
if (!n.isEmpty() && get_name() != n) {
olive::UndoStack.push(new MediaRename(this, value.toString()));
return true;
}
}
return false;
if (col == 0) {
QString n = value.toString();
if (!n.isEmpty() && get_name() != n) {
olive::UndoStack.push(new MediaRename(this, value.toString()));
return true;
}
}
return false;
}
Media *Media::child(int row) {
return children.value(row);
return children.value(row);
}
int Media::childCount() const {
return children.count();
return children.count();
}
int Media::columnCount() const {
return 3;
return 3;
}
QVariant Media::data(int column, int role) {
switch (role) {
case Qt::DecorationRole:
if (column == 0) {
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
if (f->video_tracks.size() > 0
&& f->video_tracks.at(0).preview_done) {
return f->video_tracks.at(0).video_preview_square;
}
}
switch (role) {
case Qt::DecorationRole:
if (column == 0) {
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
if (f->video_tracks.size() > 0
&& f->video_tracks.at(0).preview_done) {
return f->video_tracks.at(0).video_preview_square;
}
}
return icon;
}
break;
case Qt::DisplayRole:
switch (column) {
case 0: return (root) ? QCoreApplication::translate("Media", "Name") : get_name();
case 1:
if (root) return QCoreApplication::translate("Media", "Duration");
if (get_type() == MEDIA_TYPE_SEQUENCE) {
SequencePtr s = to_sequence();
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
double r = 30;
return icon;
}
break;
case Qt::DisplayRole:
switch (column) {
case 0: return (root) ? QCoreApplication::translate("Media", "Name") : get_name();
case 1:
if (root) return QCoreApplication::translate("Media", "Duration");
if (get_type() == MEDIA_TYPE_SEQUENCE) {
SequencePtr s = to_sequence();
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
double r = 30;
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
r = f->video_tracks.at(0).video_frame_rate * f->speed;
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
r = f->video_tracks.at(0).video_frame_rate * f->speed;
long len = f->get_length_in_frames(r);
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
}
break;
case 2:
if (root) return QCoreApplication::translate("Media", "Rate");
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
double r;
if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) {
return QString::number(get_frame_rate()) + " FPS";
} else if (f->audio_tracks.size() > 0) {
return QString::number(get_sampling_rate()) + " Hz";
}
}
break;
}
break;
case Qt::ToolTipRole:
return tooltip;
}
return QVariant();
long len = f->get_length_in_frames(r);
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
}
break;
case 2:
if (root) return QCoreApplication::translate("Media", "Rate");
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
if (get_type() == MEDIA_TYPE_FOOTAGE) {
FootagePtr f = to_footage();
double r;
if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) {
return QString::number(get_frame_rate()) + " FPS";
} else if (f->audio_tracks.size() > 0) {
return QString::number(get_sampling_rate()) + " Hz";
}
}
break;
}
break;
case Qt::ToolTipRole:
return tooltip;
}
return QVariant();
}
int Media::row() const {
if (parent) {
return parent->children.indexOf(const_cast<Media*>(this));
}
return 0;
if (parent) {
return parent->children.indexOf(const_cast<Media*>(this));
}
return 0;
}
Media *Media::parentItem() {
return parent;
return parent;
}
void Media::removeChild(int i) {
children.removeAt(i);
children.removeAt(i);
}
QVector<Marker> &Media::get_markers() {
// returns the marker array from the internal object
//
// NOTE: if this media object is not footage or a sequence, the result is
// undefined - most likely a crash
// returns the marker array from the internal object
//
// NOTE: if this media object is not footage or a sequence, the result is
// undefined - most likely a crash
if (get_type() == MEDIA_TYPE_FOOTAGE) {
return to_footage()->markers;
} else {
return to_sequence()->markers;
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
return to_footage()->markers;
} else {
return to_sequence()->markers;
}
}
+42 -42
View File
@@ -29,9 +29,9 @@
#include "project/footage.h"
enum MediaType {
MEDIA_TYPE_FOOTAGE,
MEDIA_TYPE_SEQUENCE,
MEDIA_TYPE_FOLDER
MEDIA_TYPE_FOOTAGE,
MEDIA_TYPE_SEQUENCE,
MEDIA_TYPE_FOLDER
};
class Sequence;
@@ -42,51 +42,51 @@ using VoidPtr = std::shared_ptr<void>;
class Media
{
public:
Media(Media* iparent);
~Media();
FootagePtr to_footage();
SequencePtr to_sequence();
void set_footage(FootagePtr f);
void set_sequence(SequencePtr s);
void set_folder();
void set_icon(const QIcon &ico);
void set_parent(Media* p);
void update_tooltip(const QString& error = 0);
VoidPtr to_object();
int get_type();
const QString& get_name();
void set_name(const QString& n);
Media(Media* iparent);
~Media();
FootagePtr to_footage();
SequencePtr to_sequence();
void set_footage(FootagePtr f);
void set_sequence(SequencePtr s);
void set_folder();
void set_icon(const QIcon &ico);
void set_parent(Media* p);
void update_tooltip(const QString& error = 0);
VoidPtr to_object();
int get_type();
const QString& get_name();
void set_name(const QString& n);
double get_frame_rate(int stream = -1);
int get_sampling_rate(int stream = -1);
double get_frame_rate(int stream = -1);
int get_sampling_rate(int stream = -1);
// item functions
void appendChild(Media *child);
bool setData(int col, const QVariant &value);
Media *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column, int role);
int row() const;
Media *parentItem();
void removeChild(int i);
// item functions
void appendChild(Media *child);
bool setData(int col, const QVariant &value);
Media *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column, int role);
int row() const;
Media *parentItem();
void removeChild(int i);
// get markers from internal object
QVector<Marker>& get_markers();
// get markers from internal object
QVector<Marker>& get_markers();
bool root;
int temp_id;
int temp_id2;
bool root;
int temp_id;
int temp_id2;
private:
int type;
VoidPtr object;
int type;
VoidPtr object;
// item functions
QList<Media*> children;
Media* parent;
QString folder_name;
QString tooltip;
QIcon icon;
// item functions
QList<Media*> children;
Media* parent;
QString folder_name;
QString tooltip;
QIcon icon;
};
#endif // MEDIA_H
+6
View File
@@ -21,6 +21,12 @@
#ifndef PROJECTELEMENTS_H
#define PROJECTELEMENTS_H
/**
Simple header to include all classes used for an Olive project file
*/
// includes elements the user can use in a project
#include "media.h"
#include "footage.h"
+47 -46
View File
@@ -25,45 +25,45 @@
#include "debug.h"
Sequence::Sequence() {
playhead = 0;
using_workarea = false;
workarea_in = 0;
workarea_out = 0;
wrapper_sequence = false;
playhead = 0;
using_workarea = false;
workarea_in = 0;
workarea_out = 0;
wrapper_sequence = false;
}
Sequence::~Sequence() {}
SequencePtr Sequence::copy() {
SequencePtr s(new Sequence());
s->name = QCoreApplication::translate("Sequence", "%1 (copy)").arg(name);
s->width = width;
s->height = height;
s->frame_rate = frame_rate;
s->audio_frequency = audio_frequency;
s->audio_layout = audio_layout;
s->clips.resize(clips.size());
for (int i=0;i<clips.size();i++) {
ClipPtr c = clips.at(i);
if (c == nullptr) {
s->clips[i] = nullptr;
} else {
ClipPtr copy = c->copy(s);
copy->linked = c->linked;
s->clips[i] = copy;
}
}
return s;
SequencePtr s(new Sequence());
s->name = QCoreApplication::translate("Sequence", "%1 (copy)").arg(name);
s->width = width;
s->height = height;
s->frame_rate = frame_rate;
s->audio_frequency = audio_frequency;
s->audio_layout = audio_layout;
s->clips.resize(clips.size());
for (int i=0;i<clips.size();i++) {
ClipPtr c = clips.at(i);
if (c == nullptr) {
s->clips[i] = nullptr;
} else {
ClipPtr copy = c->copy(s);
copy->linked = c->linked;
s->clips[i] = copy;
}
}
return s;
}
long Sequence::getEndFrame() {
long end = 0;
for (int j=0;j<clips.size();j++) {
ClipPtr c = clips.at(j);
if (c != nullptr && c->timeline_out > end) {
end = c->timeline_out;
}
}
long end = 0;
for (int j=0;j<clips.size();j++) {
ClipPtr c = clips.at(j);
if (c != nullptr && c->timeline_out() > end) {
end = c->timeline_out();
}
}
return end;
}
@@ -72,27 +72,28 @@ void Sequence::RefreshClips(Media *m) {
ClipPtr c = clips.at(i);
if (c != nullptr
&& (m == nullptr || c->media == m)) {
&& (m == nullptr || c->media() == m)) {
c->Close(true);
c->refresh();
}
}
}
void Sequence::getTrackLimits(int* video_tracks, int* audio_tracks) {
int vt = 0;
int at = 0;
for (int j=0;j<clips.size();j++) {
ClipPtr c = clips.at(j);
if (c != nullptr) {
if (c->track < 0 && c->track < vt) { // video clip
vt = c->track;
} else if (c->track > at) {
at = c->track;
}
}
}
if (video_tracks != nullptr) *video_tracks = vt;
if (audio_tracks != nullptr) *audio_tracks = at;
int vt = 0;
int at = 0;
for (int j=0;j<clips.size();j++) {
ClipPtr c = clips.at(j);
if (c != nullptr) {
if (c->track() < 0 && c->track() < vt) { // video clip
vt = c->track();
} else if (c->track() > at) {
at = c->track();
}
}
}
if (video_tracks != nullptr) *video_tracks = vt;
if (audio_tracks != nullptr) *audio_tracks = at;
}
// static variable for the currently active sequence
+279 -276
View File
@@ -23,7 +23,7 @@
#include "panels/panels.h"
#include "project/media.h"
#include "project/undo.h"
#include "playback/playback.h"
#include "rendering/renderfunctions.h"
#include "panels/timeline.h"
#include "panels/project.h"
#include "project/footage.h"
@@ -47,173 +47,176 @@
#include <QDebug>
SourcesCommon::SourcesCommon(Project* parent) :
editing_item(nullptr),
project_parent(parent)
editing_item(nullptr),
project_parent(parent)
{
rename_timer.setInterval(1000);
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
rename_timer.setInterval(1000);
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
}
void SourcesCommon::create_seq_from_selected() {
if (!selected_items.isEmpty()) {
QVector<Media*> media_list;
for (int i=0;i<selected_items.size();i++) {
media_list.append(project_parent->item_to_media(selected_items.at(i)));
}
if (!selected_items.isEmpty()) {
QVector<Media*> media_list;
for (int i=0;i<selected_items.size();i++) {
media_list.append(project_parent->item_to_media(selected_items.at(i)));
}
ComboAction* ca = new ComboAction();
SequencePtr s = create_sequence_from_media(media_list);
ComboAction* ca = new ComboAction();
SequencePtr s = create_sequence_from_media(media_list);
// add clips to it
panel_timeline->create_ghosts_from_media(s, 0, media_list);
panel_timeline->add_clips_from_ghosts(ca, s);
// add clips to it
panel_timeline->create_ghosts_from_media(s, 0, media_list);
panel_timeline->add_clips_from_ghosts(ca, s);
project_parent->create_sequence_internal(ca, s, true, nullptr);
olive::UndoStack.push(ca);
}
project_parent->create_sequence_internal(ca, s, true, nullptr);
olive::UndoStack.push(ca);
}
}
void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& items) {
QMenu menu(parent);
QMenu menu(parent);
selected_items = items;
selected_items = items;
QAction* import_action = menu.addAction(tr("Import..."));
QObject::connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog()));
QAction* import_action = menu.addAction(tr("Import..."));
QObject::connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog()));
QMenu* new_menu = menu.addMenu(tr("New"));
olive::MenuHelper.make_new_menu(new_menu);
QMenu* new_menu = menu.addMenu(tr("New"));
olive::MenuHelper.make_new_menu(new_menu);
QMenu* view_menu = menu.addMenu(tr("View"));
QMenu* view_menu = menu.addMenu(tr("View"));
QAction* tree_view_action = view_menu->addAction(tr("Tree View"));
connect(tree_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_tree_view()));
QAction* tree_view_action = view_menu->addAction(tr("Tree View"));
connect(tree_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_tree_view()));
QAction* icon_view_action = view_menu->addAction(tr("Icon View"));
connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
QAction* icon_view_action = view_menu->addAction(tr("Icon View"));
connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
toolbar_action->setCheckable(true);
toolbar_action->setChecked(project_parent->toolbar_widget->isVisible());
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool)));
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
toolbar_action->setCheckable(true);
toolbar_action->setChecked(project_parent->toolbar_widget->isVisible());
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool)));
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
show_sequences->setCheckable(true);
show_sequences->setChecked(panel_project->sorter->get_show_sequences());
connect(show_sequences, SIGNAL(triggered(bool)), panel_project->sorter, SLOT(set_show_sequences(bool)));
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
show_sequences->setCheckable(true);
show_sequences->setChecked(panel_project->sorter->get_show_sequences());
connect(show_sequences, SIGNAL(triggered(bool)), panel_project->sorter, SLOT(set_show_sequences(bool)));
if (items.size() > 0) {
if (items.size() == 1) {
Media* first_media = project_parent->item_to_media(items.at(0));
if (items.size() > 0) {
if (items.size() == 1) {
Media* first_media = project_parent->item_to_media(items.at(0));
// replace footage
int type = first_media->get_type();
if (type == MEDIA_TYPE_FOOTAGE) {
QAction* replace_action = menu.addAction(tr("Replace/Relink Media"));
QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
// replace footage
int type = first_media->get_type();
if (type == MEDIA_TYPE_FOOTAGE) {
QAction* replace_action = menu.addAction(tr("Replace/Relink Media"));
QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
#if defined(Q_OS_WIN)
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Explorer"));
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Explorer"));
#elif defined(Q_OS_MAC)
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Finder"));
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Finder"));
#else
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in File Manager"));
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in File Manager"));
#endif
QObject::connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
}
if (type != MEDIA_TYPE_FOLDER) {
QAction* replace_clip_media = menu.addAction(tr("Replace Clips Using This Media"));
QObject::connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
}
}
QObject::connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
}
if (type != MEDIA_TYPE_FOLDER) {
QAction* replace_clip_media = menu.addAction(tr("Replace Clips Using This Media"));
QObject::connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
}
}
// analyze selected footage types
bool all_sequences = true;
bool all_footage = true;
// analyze selected footage types
bool all_sequences = true;
bool all_footage = true;
cached_selected_footage.clear();
for (int i=0;i<items.size();i++) {
Media* m = project_parent->item_to_media(items.at(i));
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
all_sequences = false;
}
if (m->get_type() == MEDIA_TYPE_FOOTAGE) {
cached_selected_footage.append(m->to_footage());
} else {
all_footage = false;
}
}
cached_selected_footage.clear();
for (int i=0;i<items.size();i++) {
Media* m = project_parent->item_to_media(items.at(i));
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
all_sequences = false;
}
if (m->get_type() == MEDIA_TYPE_FOOTAGE) {
cached_selected_footage.append(m);
} else {
all_footage = false;
}
}
// create sequence from
QAction* create_seq_from = menu.addAction(tr("Create Sequence With This Media"));
QObject::connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
// create sequence from
QAction* create_seq_from = menu.addAction(tr("Create Sequence With This Media"));
QObject::connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
// ONLY sequences are selected
if (all_sequences) {
// ONLY sequences are selected
QAction* duplicate_action = menu.addAction(tr("Duplicate"));
QObject::connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
}
// ONLY sequences are selected
if (all_sequences) {
// ONLY sequences are selected
QAction* duplicate_action = menu.addAction(tr("Duplicate"));
QObject::connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
}
// ONLY footage is selected
if (all_footage) {
QAction* delete_footage_from_sequences = menu.addAction(tr("Delete All Clips Using This Media"));
QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
// ONLY footage is selected
if (all_footage) {
QAction* delete_footage_from_sequences = menu.addAction(tr("Delete All Clips Using This Media"));
QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
QMenu* proxies = menu.addMenu(tr("Proxy"));
QMenu* proxies = menu.addMenu(tr("Proxy"));
// special case if one footage item is selected and its proxy is currently being generated
if (cached_selected_footage.size() == 1
&& cached_selected_footage.at(0)->proxy
&& cached_selected_footage.at(0)->proxy_path.isEmpty()) {
QAction* action = proxies->addAction(tr("Generating proxy: %1% complete").arg(proxy_generator.get_proxy_progress(cached_selected_footage.at(0))));
action->setEnabled(false);
} else {
// determine whether any selected footage has or doesn't have proxies
bool footage_without_proxies_exists = false;
bool footage_with_proxies_exists = false;
// special case if one footage item is selected and its proxy is currently being generated
if (cached_selected_footage.size() == 1
&& cached_selected_footage.at(0)->to_footage()->proxy
&& cached_selected_footage.at(0)->to_footage()->proxy_path.isEmpty()) {
QAction* action = proxies->addAction(tr("Generating proxy: %1% complete").arg(
olive::proxy_generator.get_proxy_progress(cached_selected_footage.at(0))
)
);
action->setEnabled(false);
} else {
// determine whether any selected footage has or doesn't have proxies
bool footage_without_proxies_exists = false;
bool footage_with_proxies_exists = false;
for (int i=0;i<cached_selected_footage.size();i++) {
if (cached_selected_footage.at(i)->proxy) {
footage_with_proxies_exists = true;
} else {
footage_without_proxies_exists = true;
}
}
for (int i=0;i<cached_selected_footage.size();i++) {
if (cached_selected_footage.at(i)->to_footage()->proxy) {
footage_with_proxies_exists = true;
} else {
footage_without_proxies_exists = true;
}
}
// if footage was selected WITHOUT proxies
if (footage_without_proxies_exists) {
QString create_proxy_text;
// if footage was selected WITHOUT proxies
if (footage_without_proxies_exists) {
QString create_proxy_text;
if (footage_with_proxies_exists) {
// some of the footage already has proxies, so we use a different string
create_proxy_text = tr("Create/Modify Proxy");
} else {
// none of the footage has proxies
create_proxy_text = tr("Create Proxy");
}
if (footage_with_proxies_exists) {
// some of the footage already has proxies, so we use a different string
create_proxy_text = tr("Create/Modify Proxy");
} else {
// none of the footage has proxies
create_proxy_text = tr("Create Proxy");
}
proxies->addAction(create_proxy_text, this, SLOT(open_create_proxy_dialog()));
}
proxies->addAction(create_proxy_text, this, SLOT(open_create_proxy_dialog()));
}
// if footage was selected WITH proxies
if (footage_with_proxies_exists) {
// if footage was selected WITH proxies
if (footage_with_proxies_exists) {
if (!footage_without_proxies_exists) {
// if all the footage has proxies, we didn't make a "Create/Modify" above, so we create one here (but only "modify")
proxies->addAction(tr("Modify Proxy"), this, SLOT(open_create_proxy_dialog()));
}
if (!footage_without_proxies_exists) {
// if all the footage has proxies, we didn't make a "Create/Modify" above, so we create one here (but only "modify")
proxies->addAction(tr("Modify Proxy"), this, SLOT(open_create_proxy_dialog()));
}
proxies->addAction(tr("Restore Original"), this, SLOT(clear_proxies_from_selected()));
}
}
}
proxies->addAction(tr("Restore Original"), this, SLOT(clear_proxies_from_selected()));
}
}
}
// delete media
QAction* delete_action = menu.addAction(tr("Delete"));
QObject::connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
// delete media
QAction* delete_action = menu.addAction(tr("Delete"));
QObject::connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
if (items.size() == 1) {
if (items.size() == 1) {
Media* media_item = project_parent->item_to_media(items.at(0));
if (media_item->get_type() != MEDIA_TYPE_FOLDER) {
@@ -223,160 +226,160 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
preview_in_media_viewer_action->setData(reinterpret_cast<quintptr>(media_item));
}
QAction* properties_action = menu.addAction(tr("Properties..."));
QObject::connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
}
}
QAction* properties_action = menu.addAction(tr("Properties..."));
QObject::connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
}
}
menu.exec(QCursor::pos());
menu.exec(QCursor::pos());
}
void SourcesCommon::mousePressEvent(QMouseEvent *) {
stop_rename_timer();
stop_rename_timer();
}
void SourcesCommon::item_click(Media *m, const QModelIndex& index) {
if (editing_item == m) {
rename_timer.start();
} else {
editing_item = m;
editing_index = index;
}
if (editing_item == m) {
rename_timer.start();
} else {
editing_item = m;
editing_index = index;
}
}
void SourcesCommon::mouseDoubleClickEvent(const QModelIndexList& selected_items) {
stop_rename_timer();
if (selected_items.size() == 0) {
project_parent->import_dialog();
} else if (selected_items.size() == 1) {
stop_rename_timer();
if (selected_items.size() == 0) {
project_parent->import_dialog();
} else if (selected_items.size() == 1) {
Media* media = project_parent->item_to_media(selected_items.at(0));
if (media->get_type() == MEDIA_TYPE_SEQUENCE) {
olive::UndoStack.push(new ChangeSequenceAction(media->to_sequence()));
} else {
OpenSelectedMediaInMediaViewer(project_parent->item_to_media(selected_items.at(0)));
}
}
}
}
void SourcesCommon::dropEvent(QWidget* parent, QDropEvent *event, const QModelIndex& drop_item, const QModelIndexList& items) {
const QMimeData* mimeData = event->mimeData();
Media* m = project_parent->item_to_media(drop_item);
if (mimeData->hasUrls()) {
// drag files in from outside
QList<QUrl> urls = mimeData->urls();
if (!urls.isEmpty()) {
QStringList paths;
for (int i=0;i<urls.size();i++) {
paths.append(urls.at(i).toLocalFile());
}
bool replace = false;
if (urls.size() == 1
&& drop_item.isValid()
&& m->get_type() == MEDIA_TYPE_FOOTAGE
&& !QFileInfo(paths.at(0)).isDir()
&& olive::CurrentConfig.drop_on_media_to_replace
&& QMessageBox::question(
parent,
tr("Replace Media"),
tr("You dropped a file onto '%1'. Would you like to replace it with the dropped file?").arg(m->get_name()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
replace = true;
project_parent->replace_media(m, paths.at(0));
}
if (!replace) {
QModelIndex parent;
if (drop_item.isValid()) {
if (m->get_type() == MEDIA_TYPE_FOLDER) {
parent = drop_item;
} else {
parent = drop_item.parent();
}
}
project_parent->process_file_list(paths, false, nullptr, panel_project->item_to_media(parent));
}
}
event->acceptProposedAction();
} else {
event->ignore();
const QMimeData* mimeData = event->mimeData();
Media* m = project_parent->item_to_media(drop_item);
if (mimeData->hasUrls()) {
// drag files in from outside
QList<QUrl> urls = mimeData->urls();
if (!urls.isEmpty()) {
QStringList paths;
for (int i=0;i<urls.size();i++) {
paths.append(urls.at(i).toLocalFile());
}
bool replace = false;
if (urls.size() == 1
&& drop_item.isValid()
&& m->get_type() == MEDIA_TYPE_FOOTAGE
&& !QFileInfo(paths.at(0)).isDir()
&& olive::CurrentConfig.drop_on_media_to_replace
&& QMessageBox::question(
parent,
tr("Replace Media"),
tr("You dropped a file onto '%1'. Would you like to replace it with the dropped file?").arg(m->get_name()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
replace = true;
project_parent->replace_media(m, paths.at(0));
}
if (!replace) {
QModelIndex parent;
if (drop_item.isValid()) {
if (m->get_type() == MEDIA_TYPE_FOLDER) {
parent = drop_item;
} else {
parent = drop_item.parent();
}
}
project_parent->process_file_list(paths, false, nullptr, panel_project->item_to_media(parent));
}
}
event->acceptProposedAction();
} else {
event->ignore();
// dragging files within project
// if we dragged to the root OR dragged to a folder
if (!drop_item.isValid() || m->get_type() == MEDIA_TYPE_FOLDER) {
QVector<Media*> move_items;
for (int i=0;i<items.size();i++) {
const QModelIndex& item = items.at(i);
const QModelIndex& parent = item.parent();
Media* s = project_parent->item_to_media(item);
if (parent != drop_item && item != drop_item) {
bool ignore = false;
if (parent.isValid()) {
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
QModelIndex par = parent;
while (par.isValid() && !ignore) {
for (int j=0;j<items.size();j++) {
if (par == items.at(j)) {
ignore = true;
break;
}
}
par = par.parent();
}
}
if (!ignore) {
move_items.append(s);
}
}
}
if (move_items.size() > 0) {
MediaMove* mm = new MediaMove();
mm->to = m;
mm->items = move_items;
olive::UndoStack.push(mm);
}
}
}
// dragging files within project
// if we dragged to the root OR dragged to a folder
if (!drop_item.isValid() || m->get_type() == MEDIA_TYPE_FOLDER) {
QVector<Media*> move_items;
for (int i=0;i<items.size();i++) {
const QModelIndex& item = items.at(i);
const QModelIndex& parent = item.parent();
Media* s = project_parent->item_to_media(item);
if (parent != drop_item && item != drop_item) {
bool ignore = false;
if (parent.isValid()) {
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
QModelIndex par = parent;
while (par.isValid() && !ignore) {
for (int j=0;j<items.size();j++) {
if (par == items.at(j)) {
ignore = true;
break;
}
}
par = par.parent();
}
}
if (!ignore) {
move_items.append(s);
}
}
}
if (move_items.size() > 0) {
MediaMove* mm = new MediaMove();
mm->to = m;
mm->items = move_items;
olive::UndoStack.push(mm);
}
}
}
}
void SourcesCommon::reveal_in_browser() {
Media* media = project_parent->item_to_media(selected_items.at(0));
FootagePtr m = media->to_footage();
Media* media = project_parent->item_to_media(selected_items.at(0));
FootagePtr m = media->to_footage();
#if defined(Q_OS_WIN)
QStringList args;
args << "/select," << QDir::toNativeSeparators(m->url);
QProcess::startDetached("explorer", args);
QStringList args;
args << "/select," << QDir::toNativeSeparators(m->url);
QProcess::startDetached("explorer", args);
#elif defined(Q_OS_MAC)
QStringList args;
args << "-e";
args << "tell application \"Finder\"";
args << "-e";
args << "activate";
args << "-e";
args << "select POSIX file \""+m->url+"\"";
args << "-e";
args << "end tell";
QProcess::startDetached("osascript", args);
QStringList args;
args << "-e";
args << "tell application \"Finder\"";
args << "-e";
args << "activate";
args << "-e";
args << "select POSIX file \""+m->url+"\"";
args << "-e";
args << "end tell";
QProcess::startDetached("osascript", args);
#else
QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
#endif
}
void SourcesCommon::stop_rename_timer() {
rename_timer.stop();
rename_timer.stop();
}
void SourcesCommon::rename_interval() {
stop_rename_timer();
if (view->hasFocus() && editing_item != nullptr) {
view->edit(editing_index);
}
stop_rename_timer();
if (view->hasFocus() && editing_item != nullptr) {
view->edit(editing_index);
}
}
void SourcesCommon::item_renamed(Media* item) {
if (editing_item == item) {
MediaRename* mr = new MediaRename(item, "idk");
olive::UndoStack.push(mr);
editing_item = nullptr;
if (editing_item == item) {
MediaRename* mr = new MediaRename(item, "idk");
olive::UndoStack.push(mr);
editing_item = nullptr;
}
}
@@ -393,46 +396,46 @@ void SourcesCommon::OpenSelectedMediaInMediaViewer(Media* item) {
}
void SourcesCommon::open_create_proxy_dialog() {
// open the proxy dialog and send it a list of currently selected footage
ProxyDialog pd(olive::MainWindow, cached_selected_footage);
pd.exec();
// open the proxy dialog and send it a list of currently selected footage
ProxyDialog pd(olive::MainWindow, cached_selected_footage);
pd.exec();
}
void SourcesCommon::clear_proxies_from_selected() {
QList<QString> delete_list;
QList<QString> delete_list;
for (int i=0;i<cached_selected_footage.size();i++) {
FootagePtr f = cached_selected_footage.at(i);
for (int i=0;i<cached_selected_footage.size();i++) {
FootagePtr f = cached_selected_footage.at(i)->to_footage();
if (f->proxy && !f->proxy_path.isEmpty()) {
if (QFileInfo::exists(f->proxy_path)) {
if (QMessageBox::question(olive::MainWindow,
tr("Delete proxy"),
tr("Would you like to delete the proxy file \"%1\" as well?").arg(f->proxy_path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
delete_list.append(f->proxy_path);
}
}
}
if (f->proxy && !f->proxy_path.isEmpty()) {
if (QFileInfo::exists(f->proxy_path)) {
if (QMessageBox::question(olive::MainWindow,
tr("Delete proxy"),
tr("Would you like to delete the proxy file \"%1\" as well?").arg(f->proxy_path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
delete_list.append(f->proxy_path);
}
}
}
f->proxy = false;
f->proxy_path.clear();
}
f->proxy = false;
f->proxy_path.clear();
}
if (olive::ActiveSequence != nullptr) {
// close all clips so we can delete any proxies requested to be deleted
closeActiveClips(olive::ActiveSequence);
}
if (olive::ActiveSequence != nullptr) {
// close all clips so we can delete any proxies requested to be deleted
close_active_clips(olive::ActiveSequence);
}
// delete proxies requested to be deleted
for (int i=0;i<delete_list.size();i++) {
QFile::remove(delete_list.at(i));
}
// delete proxies requested to be deleted
for (int i=0;i<delete_list.size();i++) {
QFile::remove(delete_list.at(i));
}
if (olive::ActiveSequence != nullptr) {
// update viewer (will re-open active clips with original media)
panel_sequence_viewer->viewer_widget->frame_update();
}
if (olive::ActiveSequence != nullptr) {
// update viewer (will re-open active clips with original media)
panel_sequence_viewer->viewer_widget->frame_update();
}
olive::MainWindow->setWindowModified(true);
olive::MainWindow->setWindowModified(true);
}
+22 -22
View File
@@ -36,38 +36,38 @@ class QDropEvent;
struct Footage;
class SourcesCommon : public QObject {
Q_OBJECT
Q_OBJECT
public:
SourcesCommon(Project *parent);
QAbstractItemView* view;
void show_context_menu(QWidget* parent, const QModelIndexList &items);
SourcesCommon(Project *parent);
QAbstractItemView* view;
void show_context_menu(QWidget* parent, const QModelIndexList &items);
void mousePressEvent(QMouseEvent* e);
void mousePressEvent(QMouseEvent* e);
void mouseDoubleClickEvent(const QModelIndexList& selected_items);
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
void item_click(Media* m, const QModelIndex &index);
void item_click(Media* m, const QModelIndex &index);
private slots:
void create_seq_from_selected();
void reveal_in_browser();
void rename_interval();
void item_renamed(Media *item);
void create_seq_from_selected();
void reveal_in_browser();
void rename_interval();
void item_renamed(Media *item);
void OpenSelectedMediaInMediaViewerFromAction();
void OpenSelectedMediaInMediaViewer(Media* item);
// proxy functions
void open_create_proxy_dialog();
void clear_proxies_from_selected();
// proxy functions
void open_create_proxy_dialog();
void clear_proxies_from_selected();
private:
Media* editing_item;
QModelIndex editing_index;
QModelIndexList selected_items;
Project* project_parent;
void stop_rename_timer();
QTimer rename_timer;
Media* editing_item;
QModelIndex editing_index;
QModelIndexList selected_items;
Project* project_parent;
void stop_rename_timer();
QTimer rename_timer;
// we cache the selected footage items for open_create_proxy_dialog()
QVector<FootagePtr> cached_selected_footage;
// we cache the selected footage items for open_create_proxy_dialog()
QVector<Media*> cached_selected_footage;
};
#endif // SOURCESCOMMON_H
+1 -1
View File
@@ -52,7 +52,7 @@ Transition::Transition(ClipPtr c, ClipPtr s, const EffectMeta* em) :
LabelSlider* length_ui_ele = static_cast<LabelSlider*>(length_field->ui_element);
length_ui_ele->set_display_type(LABELSLIDER_FRAMENUMBER);
length_ui_ele->set_frame_rate(parent_clip->sequence == nullptr ? parent_clip->cached_fr : parent_clip->sequence->frame_rate);
length_ui_ele->set_frame_rate(parent_clip->sequence == nullptr ? parent_clip->cached_frame_rate() : parent_clip->sequence->frame_rate);
}
TransitionPtr Transition::copy(ClipPtr c, ClipPtr s) {
+116 -62
View File
@@ -32,12 +32,13 @@
#include "panels/effectcontrols.h"
#include "panels/viewer.h"
#include "panels/timeline.h"
#include "playback/playback.h"
#include "ui/sourcetable.h"
#include "project/effect.h"
#include "project/transition.h"
#include "project/footage.h"
#include "playback/cacher.h"
#include "rendering/renderfunctions.h"
#include "rendering/cacher.h"
#include "io/previewgenerator.h"
#include "ui/labelslider.h"
#include "ui/viewerwidget.h"
#include "project/marker.h"
@@ -45,16 +46,17 @@
#include "io/clipboard.h"
#include "project/media.h"
#include "debug.h"
#include "oliveglobal.h"
QUndoStack olive::UndoStack;
MoveClipAction::MoveClipAction(ClipPtr c, long iin, long iout, long iclip_in, int itrack, bool irelative) {
clip = c;
old_in = c->timeline_in;
old_out = c->timeline_out;
old_clip_in = c->clip_in;
old_track = c->track;
old_in = c->timeline_in();
old_out = c->timeline_out();
old_clip_in = c->clip_in();
old_track = c->track();
new_in = iin;
new_out = iout;
@@ -66,29 +68,29 @@ MoveClipAction::MoveClipAction(ClipPtr c, long iin, long iout, long iclip_in, in
void MoveClipAction::doUndo() {
if (relative) {
clip->timeline_in -= new_in;
clip->timeline_out -= new_out;
clip->clip_in -= new_clip_in;
clip->track -= new_track;
clip->set_timeline_in (clip->timeline_in() - new_in);
clip->set_timeline_out (clip->timeline_out() - new_out);
clip->set_clip_in (clip->clip_in() - new_clip_in);
clip->set_track (clip->track() - new_track);
} else {
clip->timeline_in = old_in;
clip->timeline_out = old_out;
clip->clip_in = old_clip_in;
clip->track = old_track;
clip->set_timeline_in(old_in);
clip->set_timeline_out(old_out);
clip->set_clip_in(old_clip_in);
clip->set_track(old_track);
}
}
void MoveClipAction::doRedo() {
if (relative) {
clip->timeline_in += new_in;
clip->timeline_out += new_out;
clip->clip_in += new_clip_in;
clip->track += new_track;
clip->set_timeline_in(clip->timeline_in() + new_in);
clip->set_timeline_out(clip->timeline_out() + new_out);
clip->set_clip_in(clip->clip_in() + new_clip_in);
clip->set_track(clip->track() + new_track);
} else {
clip->timeline_in = new_in;
clip->timeline_out = new_out;
clip->clip_in = new_clip_in;
clip->track = new_track;
clip->set_timeline_in(new_in);
clip->set_timeline_out(new_out);
clip->set_clip_in(new_clip_in);
clip->set_track(new_track);
}
}
@@ -116,8 +118,8 @@ void DeleteClipAction::doUndo() {
void DeleteClipAction::doRedo() {
// remove ref to clip
ref = seq->clips.at(index);
if (ref->open) {
close_clip(ref, true);
if (ref->IsOpen()) {
ref->Close(true);
}
seq->clips[index] = nullptr;
@@ -143,12 +145,12 @@ ChangeSequenceAction::ChangeSequenceAction(SequencePtr s) {
}
void ChangeSequenceAction::doUndo() {
set_sequence(old_sequence);
olive::Global->set_sequence(old_sequence);
}
void ChangeSequenceAction::doRedo() {
old_sequence = olive::ActiveSequence;
set_sequence(new_sequence);
olive::Global->set_sequence(new_sequence);
}
SetTimelineInOutCommand::SetTimelineInOutCommand(SequencePtr s, bool enabled, long in, long out) {
@@ -165,7 +167,7 @@ void SetTimelineInOutCommand::doUndo() {
// footage viewer functions
if (seq->wrapper_sequence) {
FootagePtr m = seq->clips.at(0)->media->to_footage();
FootagePtr m = seq->clips.at(0)->media()->to_footage();
m->using_inout = old_enabled;
m->in = old_in;
m->out = old_out;
@@ -183,7 +185,7 @@ void SetTimelineInOutCommand::doRedo() {
// footage viewer functions
if (seq->wrapper_sequence) {
FootagePtr m = seq->clips.at(0)->media->to_footage();
FootagePtr m = seq->clips.at(0)->media()->to_footage();
m->using_inout = new_enabled;
m->in = new_in;
m->out = new_out;
@@ -417,11 +419,11 @@ void AddClipCommand::doUndo() {
}
// deselect the area occupied by this clip
panel_timeline->deselect_area(c->timeline_in, c->timeline_out, c->track);
panel_timeline->deselect_area(c->timeline_in(), c->timeline_out(), c->track());
// if the clip is open, close it
if (c->open) {
close_clip(c, true);
if (c->IsOpen()) {
c->Close(true);
}
}
@@ -468,7 +470,6 @@ void LinkCommand::doUndo() {
void LinkCommand::doRedo() {
old_links.clear();
for (int i=0;i<clips.size();i++) {
dout << clips.at(i);
ClipPtr c = s->clips.at(clips.at(i));
if (link) {
for (int j=0;j<clips.size();j++) {
@@ -516,8 +517,8 @@ void ReplaceMediaCommand::replace(QString& filename) {
SequencePtr s = all_sequences.at(i)->to_sequence();
for (int j=0;j<s->clips.size();j++) {
ClipPtr c = s->clips.at(j);
if (c != nullptr && c->media == item && c->open) {
close_clip(c, true);
if (c != nullptr && c->media() == item && c->IsOpen()) {
c->Close(true);
c->replaced = true;
}
}
@@ -526,8 +527,8 @@ void ReplaceMediaCommand::replace(QString& filename) {
// replace media
QStringList files;
files.append(filename);
item->to_footage()->ready_lock.lock();
panel_project->process_file_list(files, false, item, nullptr);
PreviewGenerator* pg = new PreviewGenerator(item);
}
void ReplaceMediaCommand::doUndo() {
@@ -553,23 +554,23 @@ void ReplaceClipMediaCommand::replace(bool undo) {
for (int i=0;i<clips.size();i++) {
ClipPtr c = clips.at(i);
if (c->open) {
close_clip(c, true);
if (c->IsOpen()) {
c->Close(true);
}
if (undo) {
if (!preserve_clip_ins) {
c->clip_in = old_clip_ins.at(i);
c->set_clip_in(old_clip_ins.at(i));
}
c->media = old_media;
c->set_media(old_media, c->media_stream_index());
} else {
if (!preserve_clip_ins) {
old_clip_ins.append(c->clip_in);
c->clip_in = 0;
old_clip_ins.append(c->clip_in());
c->set_clip_in(0);
}
c->media = new_media;
c->set_media(new_media, c->media_stream_index());
}
c->replaced = true;
@@ -685,20 +686,68 @@ void EffectFieldUndo::doRedo() {
}
}
SetAutoscaleAction::SetAutoscaleAction() {}
SetClipProperty::SetClipProperty(SetClipPropertyType type) : type_(type)
{}
void SetAutoscaleAction::doUndo() {
for (int i=0;i<clips.size();i++) {
clips.at(i)->autoscale = !clips.at(i)->autoscale;
void SetClipProperty::AddSetting(ClipPtr c, bool setting)
{
clips_.append(c);
setting_.append(setting);
// store current setting for undoing
bool old_setting = false;
switch (type_) {
case kSetClipPropertyAutoscale:
old_setting = c->autoscaled();
break;
case kSetClipPropertyReversed:
old_setting = c->reversed();
break;
case kSetClipPropertyMaintainAudioPitch:
old_setting = c->speed().maintain_audio_pitch;
break;
case kSetClipPropertyEnabled:
old_setting = c->enabled();
break;
}
old_setting_.append(old_setting);
}
void SetClipProperty::MainLoop(bool undo)
{
for (int i=0;i<clips_.size();i++) {
bool setting = (undo) ? old_setting_.at(i) : setting_.at(i);
switch (type_) {
case kSetClipPropertyAutoscale:
clips_.at(i)->set_autoscaled(setting);
break;
case kSetClipPropertyReversed:
clips_.at(i)->set_reversed(setting);
break;
case kSetClipPropertyMaintainAudioPitch:
{
ClipSpeed s = clips_.at(i)->speed();
s.maintain_audio_pitch = setting;
clips_.at(i)->set_speed(s);
}
break;
case kSetClipPropertyEnabled:
clips_.at(i)->set_enabled(setting);
break;
}
}
}
void SetClipProperty::doUndo() {
MainLoop(true);
panel_sequence_viewer->viewer_widget->frame_update();
}
void SetAutoscaleAction::doRedo() {
for (int i=0;i<clips.size();i++) {
clips.at(i)->autoscale = !clips.at(i)->autoscale;
}
void SetClipProperty::doRedo() {
MainLoop(false);
panel_sequence_viewer->viewer_widget->frame_update();
}
@@ -782,19 +831,24 @@ void DeleteMarkerAction::doRedo() {
SetSpeedAction::SetSpeedAction(ClipPtr c, double speed) {
clip = c;
old_speed = c->speed;
old_speed = c->speed().value;
new_speed = speed;
}
void SetSpeedAction::doUndo() {
clip->speed = old_speed;
clip->recalculateMaxLength();
ClipSpeed cs = clip->speed();
cs.value = old_speed;
clip->set_speed(cs);
}
void SetSpeedAction::doRedo() {
clip->speed = new_speed;
clip->recalculateMaxLength();
ClipSpeed cs = clip->speed();
cs.value = new_speed;
clip->set_speed(cs);
}
SetBool::SetBool(bool* b, bool setting) {
@@ -868,7 +922,7 @@ void EditSequenceCommand::update() {
}
if (olive::ActiveSequence == seq) {
set_sequence(seq);
olive::Global->set_sequence(seq);
}
}
@@ -907,7 +961,7 @@ void CloseAllClipsCommand::doUndo() {
}
void CloseAllClipsCommand::doRedo() {
closeActiveClips(olive::ActiveSequence);
close_active_clips(olive::ActiveSequence);
}
UpdateFootageTooltip::UpdateFootageTooltip(Media *i) {
@@ -955,15 +1009,15 @@ RenameClipCommand::RenameClipCommand() {}
void RenameClipCommand::doUndo() {
for (int i=0;i<clips.size();i++) {
clips.at(i)->name = old_names.at(i);
clips.at(i)->set_name(old_names.at(i));
}
}
void RenameClipCommand::doRedo() {
old_names.resize(clips.size());
for (int i=0;i<clips.size();i++) {
old_names[i] = clips.at(i)->name;
clips.at(i)->name = new_name;
old_names[i] = clips.at(i)->name();
clips.at(i)->set_name(new_name);
}
}
@@ -1007,7 +1061,7 @@ void RippleAction::doRedo() {
if (!ignore.contains(i)) {
ClipPtr c = s->clips.at(i);
if (c != nullptr) {
if (c->timeline_in >= point) {
if (c->timeline_in() >= point) {
move_clip(ca, c, length, length, 0, 0, true, true);
}
}
@@ -1108,7 +1162,7 @@ void RefreshClips::doRedo() {
SequencePtr s = all_sequences.at(i)->to_sequence();
for (int j=0;j<s->clips.size();j++) {
ClipPtr c = s->clips.at(j);
if (c != nullptr && c->media == media) {
if (c != nullptr && c->media() == media) {
c->replaced = true;
c->refresh();
}
+16 -3
View File
@@ -362,12 +362,25 @@ private:
bool done;
};
class SetAutoscaleAction : public OliveAction {
enum SetClipPropertyType {
kSetClipPropertyAutoscale,
kSetClipPropertyReversed,
kSetClipPropertyMaintainAudioPitch,
kSetClipPropertyEnabled
};
class SetClipProperty : public OliveAction {
public:
SetAutoscaleAction();
SetClipProperty(SetClipPropertyType type);
virtual void doUndo() override;
virtual void doRedo() override;
QVector<ClipPtr> clips;
void AddSetting(ClipPtr c, bool setting);
private:
SetClipPropertyType type_;
QVector<ClipPtr> clips_;
QVector<bool> setting_;
QVector<bool> old_setting_;
void MainLoop(bool undo);
};
class AddMarkerAction : public OliveAction {