merge of long discussed new pipeline
This commit is contained in:
+239
-160
@@ -1,20 +1,20 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
#include "effects/effect.h"
|
||||
#include "nodes/node.h"
|
||||
#include "effects/transition.h"
|
||||
#include "project/footage.h"
|
||||
#include "global/config.h"
|
||||
@@ -32,34 +32,31 @@
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "project/media.h"
|
||||
#include "project/clipboard.h"
|
||||
#include "undo/undo.h"
|
||||
#include "global/clipboard.h"
|
||||
#include "global/debug.h"
|
||||
#include "global/timing.h"
|
||||
|
||||
const int kRGBAComponentCount = 4;
|
||||
|
||||
Clip::Clip(Sequence* s) :
|
||||
sequence(s),
|
||||
Clip::Clip(Track *s) :
|
||||
track_(s),
|
||||
cacher(this),
|
||||
enabled_(true),
|
||||
clip_in_(0),
|
||||
timeline_in_(0),
|
||||
timeline_out_(0),
|
||||
track_(0),
|
||||
media_(nullptr),
|
||||
reverse_(false),
|
||||
autoscale_(olive::CurrentConfig.autoscale_by_default),
|
||||
autoscale_(olive::config.autoscale_by_default),
|
||||
opening_transition(nullptr),
|
||||
closing_transition(nullptr),
|
||||
undeletable(false),
|
||||
replaced(false),
|
||||
fbo(nullptr),
|
||||
open_(false),
|
||||
texture(nullptr)
|
||||
texture(0)
|
||||
{
|
||||
}
|
||||
|
||||
ClipPtr Clip::copy(Sequence* s) {
|
||||
ClipPtr Clip::copy(Track* s) {
|
||||
ClipPtr copy = std::make_shared<Clip>(s);
|
||||
|
||||
copy->set_enabled(enabled());
|
||||
@@ -67,7 +64,6 @@ ClipPtr Clip::copy(Sequence* s) {
|
||||
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());
|
||||
@@ -78,7 +74,7 @@ ClipPtr Clip::copy(Sequence* s) {
|
||||
copy->effects.append(effects.at(i)->copy(copy.get()));
|
||||
}
|
||||
|
||||
copy->set_cached_frame_rate((this->sequence == nullptr) ? cached_frame_rate() : this->sequence->frame_rate);
|
||||
copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->sequence()->frame_rate);
|
||||
|
||||
copy->refresh();
|
||||
|
||||
@@ -87,25 +83,42 @@ ClipPtr Clip::copy(Sequence* s) {
|
||||
|
||||
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();
|
||||
&& timeline_in(true) <= timecode
|
||||
&& timeline_out(true) > timecode
|
||||
&& timecode - timeline_in(true) + clip_in(true) < media_length()
|
||||
&& !track()->IsEffectivelyMuted();
|
||||
}
|
||||
|
||||
bool Clip::IsSelected(bool containing)
|
||||
{
|
||||
if (this->sequence == nullptr) {
|
||||
if (this->track_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->sequence->IsClipSelected(this, containing);
|
||||
return this->track_->IsClipSelected(this, containing);
|
||||
}
|
||||
|
||||
bool Clip::IsTransitionSelected(TransitionType type)
|
||||
{
|
||||
switch (type) {
|
||||
case kTransitionOpening:
|
||||
return track_->IsTransitionSelected(opening_transition.get());
|
||||
case kTransitionClosing:
|
||||
return track_->IsTransitionSelected(closing_transition.get());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Selection Clip::ToSelection()
|
||||
{
|
||||
return Selection(timeline_in(), timeline_out(), track());
|
||||
}
|
||||
|
||||
olive::TrackType Clip::type()
|
||||
{
|
||||
return track()->type();
|
||||
}
|
||||
|
||||
const QColor &Clip::color()
|
||||
@@ -134,7 +147,7 @@ FootageStream *Clip::media_stream()
|
||||
{
|
||||
if (media() != nullptr
|
||||
&& media()->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
return media()->to_footage()->get_stream_from_file_index(track() < 0, media_stream_index());
|
||||
return media()->to_footage()->get_stream_from_file_index(type() == olive::kTypeVideo, media_stream_index());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -151,6 +164,11 @@ void Clip::set_media(Media *m, int s)
|
||||
media_stream_ = s;
|
||||
}
|
||||
|
||||
void Clip::Move(ComboAction *ca, long iin, long iout, long iclip_in, Track *itrack, bool verify_transitions, bool relative)
|
||||
{
|
||||
track()->sequence()->MoveClip(this, ca, iin, iout, iclip_in, itrack, verify_transitions, relative);
|
||||
}
|
||||
|
||||
bool Clip::enabled()
|
||||
{
|
||||
return enabled_;
|
||||
@@ -161,51 +179,18 @@ void Clip::set_enabled(bool e)
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
void Clip::move(ComboAction* ca, long iin, long iout, long iclip_in, int itrack, bool verify_transitions, bool relative)
|
||||
{
|
||||
ca->append(new MoveClipAction(this, iin, iout, iclip_in, itrack, relative));
|
||||
|
||||
if (verify_transitions) {
|
||||
|
||||
// if this is a shared transition, and the corresponding clip will be moved away somehow
|
||||
if (opening_transition != nullptr
|
||||
&& opening_transition->secondary_clip != nullptr
|
||||
&& opening_transition->secondary_clip->timeline_out() != iin) {
|
||||
// separate transition
|
||||
ca->append(new SetPointer(reinterpret_cast<void**>(&opening_transition->secondary_clip), nullptr));
|
||||
ca->append(new AddTransitionCommand(nullptr,
|
||||
opening_transition->secondary_clip,
|
||||
opening_transition,
|
||||
nullptr,
|
||||
0));
|
||||
}
|
||||
|
||||
if (closing_transition != nullptr
|
||||
&& closing_transition->secondary_clip != nullptr
|
||||
&& closing_transition->parent_clip->timeline_in() != iout) {
|
||||
// separate transition
|
||||
ca->append(new SetPointer(reinterpret_cast<void**>(&closing_transition->secondary_clip), nullptr));
|
||||
ca->append(new AddTransitionCommand(nullptr,
|
||||
this,
|
||||
closing_transition,
|
||||
nullptr,
|
||||
0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::reset_audio() {
|
||||
if (UsesCacher()) {
|
||||
cacher.ResetAudio();
|
||||
}
|
||||
if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* nested_sequence = media()->to_sequence().get();
|
||||
for (int i=0;i<nested_sequence->clips.size();i++) {
|
||||
Clip* c = nested_sequence->clips.at(i).get();
|
||||
if (c != nullptr) {
|
||||
c->reset_audio();
|
||||
}
|
||||
|
||||
QVector<Clip*> nested_sequence_clips = media()->to_sequence()->GetAllClips();
|
||||
|
||||
for (int i=0;i<nested_sequence_clips.size();i++) {
|
||||
nested_sequence_clips.at(i)->reset_audio();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +199,9 @@ void Clip::refresh() {
|
||||
if (replaced && media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* m = media()->to_footage();
|
||||
|
||||
if (track() < 0 && m->video_tracks.size() > 0) {
|
||||
if (type() == olive::kTypeVideo && m->video_tracks.size() > 0) {
|
||||
set_media(media(), m->video_tracks.at(0).file_index);
|
||||
} else if (track() >= 0 && m->audio_tracks.size() > 0) {
|
||||
} else if (type() == olive::kTypeAudio && m->audio_tracks.size() > 0) {
|
||||
set_media(media(), m->audio_tracks.at(0).file_index);
|
||||
}
|
||||
}
|
||||
@@ -235,7 +220,7 @@ QVector<Marker> &Clip::get_markers() {
|
||||
return markers;
|
||||
}
|
||||
|
||||
int Clip::IndexOfEffect(Effect *e)
|
||||
int Clip::IndexOfEffect(Node *e)
|
||||
{
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i).get() == e) {
|
||||
@@ -249,13 +234,91 @@ Clip::~Clip() {
|
||||
if (IsOpen()) {
|
||||
Close(true);
|
||||
}
|
||||
}
|
||||
|
||||
effects.clear();
|
||||
void Clip::Save(QXmlStreamWriter &stream)
|
||||
{
|
||||
stream.writeStartElement("clip");
|
||||
stream.writeAttribute("id", QString::number(load_id));
|
||||
|
||||
stream.writeAttribute("enabled", QString::number(enabled()));
|
||||
stream.writeAttribute("name", name());
|
||||
stream.writeAttribute("clipin", QString::number(clip_in()));
|
||||
stream.writeAttribute("in", QString::number(timeline_in()));
|
||||
stream.writeAttribute("out", QString::number(timeline_out()));
|
||||
|
||||
stream.writeAttribute("r", QString::number(color().red()));
|
||||
stream.writeAttribute("g", QString::number(color().green()));
|
||||
stream.writeAttribute("b", QString::number(color().blue()));
|
||||
|
||||
stream.writeAttribute("autoscale", QString::number(autoscaled()));
|
||||
stream.writeAttribute("speed", QString::number(speed().value, 'f', 10));
|
||||
stream.writeAttribute("maintainpitch", QString::number(speed().maintain_audio_pitch));
|
||||
stream.writeAttribute("reverse", QString::number(reversed()));
|
||||
|
||||
if (media() != nullptr) {
|
||||
stream.writeAttribute("type", QString::number(media()->get_type()));
|
||||
switch (media()->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
stream.writeAttribute("media", QString::number(media()->to_footage()->save_id));
|
||||
stream.writeAttribute("stream", QString::number(media_stream_index()));
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
stream.writeAttribute("sequence", QString::number(media()->to_sequence()->save_id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// save markers
|
||||
// only necessary for null media clips, since media has its own markers
|
||||
if (media() == nullptr) {
|
||||
for (int k=0;k<get_markers().size();k++) {
|
||||
get_markers().at(k).Save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
// save clip links
|
||||
stream.writeStartElement("linked"); // linked
|
||||
for (int k=0;k<linked.size();k++) {
|
||||
stream.writeStartElement("link"); // link
|
||||
stream.writeAttribute("id", QString::number(linked.at(k)->load_id));
|
||||
stream.writeEndElement(); // link
|
||||
}
|
||||
stream.writeEndElement(); // linked
|
||||
|
||||
// save opening and closing transitions
|
||||
for (int t=kTransitionOpening;t<=kTransitionClosing;t++) {
|
||||
TransitionPtr transition = (t == kTransitionOpening) ? opening_transition : closing_transition;
|
||||
|
||||
if (transition != nullptr) {
|
||||
stream.writeStartElement((t == kTransitionOpening) ? "opening" : "closing");
|
||||
|
||||
// check if this is a shared transition
|
||||
if (this == transition->secondary_clip) {
|
||||
// if so, just save a reference to the other clip
|
||||
stream.writeAttribute("shared",
|
||||
QString::number(transition->parent_clip->load_id));
|
||||
} else {
|
||||
// otherwise save the whole transition
|
||||
transition->save(stream);
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // opening/closing
|
||||
}
|
||||
}
|
||||
|
||||
for (int k=0;k<effects.size();k++) {
|
||||
stream.writeStartElement("effect"); // effect
|
||||
effects.at(k)->save(stream);
|
||||
stream.writeEndElement(); // effect
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // clip
|
||||
}
|
||||
|
||||
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)
|
||||
// we must be the secondary clip, so return (clip in - length)
|
||||
return clip_in_ - opening_transition->get_true_length();
|
||||
}
|
||||
return clip_in_;
|
||||
@@ -281,7 +344,7 @@ void Clip::set_timeline_in(long 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)
|
||||
// we must be the primary clip, so return (timeline out + length)
|
||||
return timeline_out_ + closing_transition->get_true_length();
|
||||
} else {
|
||||
return timeline_out_;
|
||||
@@ -348,12 +411,12 @@ AVRational Clip::time_base()
|
||||
return cacher.media_time_base();
|
||||
}
|
||||
|
||||
int Clip::track()
|
||||
Track *Clip::track()
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
|
||||
void Clip::set_track(int t)
|
||||
void Clip::set_track(Track *t)
|
||||
{
|
||||
track_ = t;
|
||||
}
|
||||
@@ -364,18 +427,18 @@ long Clip::length() {
|
||||
}
|
||||
|
||||
double Clip::media_frame_rate() {
|
||||
Q_ASSERT(track_ < 0);
|
||||
Q_ASSERT(type() == olive::kTypeVideo);
|
||||
if (media_ != nullptr) {
|
||||
double rate = media_->get_frame_rate(media_stream_index());
|
||||
if (!qIsNaN(rate)) return rate;
|
||||
}
|
||||
if (sequence != nullptr) return sequence->frame_rate;
|
||||
if (track() != nullptr) return track()->sequence()->frame_rate;
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
long Clip::media_length() {
|
||||
if (this->sequence != nullptr) {
|
||||
double fr = this->sequence->frame_rate;
|
||||
if (this->track() != nullptr) {
|
||||
double fr = this->track()->sequence()->frame_rate;
|
||||
|
||||
fr /= speed_.value;
|
||||
|
||||
@@ -386,7 +449,7 @@ long Clip::media_length() {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* m = media_->to_footage();
|
||||
const FootageStream* ms = m->get_stream_from_file_index(track_ < 0, media_stream_index());
|
||||
const FootageStream* ms = m->get_stream_from_file_index(type() == olive::kTypeVideo, media_stream_index());
|
||||
if (ms != nullptr && ms->infinite_length) {
|
||||
return LONG_MAX;
|
||||
} else {
|
||||
@@ -396,7 +459,7 @@ long Clip::media_length() {
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
Sequence* s = media_->to_sequence().get();
|
||||
return rescale_frame_number(s->getEndFrame(), s->frame_rate, fr);
|
||||
return rescale_frame_number(s->GetEndFrame(), s->frame_rate, fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,13 +468,13 @@ long Clip::media_length() {
|
||||
}
|
||||
|
||||
int Clip::media_width() {
|
||||
if (media_ == nullptr && sequence != nullptr) return sequence->width;
|
||||
if (media_ == nullptr && track() != nullptr) return track()->sequence()->width;
|
||||
switch (media_->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
const FootageStream* ms = media_stream();
|
||||
if (ms != nullptr) return ms->video_width;
|
||||
if (sequence != nullptr) return sequence->width;
|
||||
if (track() != nullptr) return track()->sequence()->width;
|
||||
break;
|
||||
}
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
@@ -424,13 +487,13 @@ int Clip::media_width() {
|
||||
}
|
||||
|
||||
int Clip::media_height() {
|
||||
if (media_ == nullptr && sequence != nullptr) return sequence->height;
|
||||
if (media_ == nullptr && track() != nullptr) return track()->sequence()->height;
|
||||
switch (media_->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
const FootageStream* ms = media_stream();
|
||||
if (ms != nullptr) return ms->video_height;
|
||||
if (sequence != nullptr) return sequence->height;
|
||||
if (track() != nullptr) return track()->sequence()->height;
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
@@ -444,16 +507,17 @@ int Clip::media_height() {
|
||||
|
||||
void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points) {
|
||||
if (change_timeline_points) {
|
||||
this->move(ca,
|
||||
qRound(double(timeline_in_) * multiplier),
|
||||
qRound(double(timeline_out_) * multiplier),
|
||||
qRound(double(clip_in_) * multiplier),
|
||||
track_);
|
||||
track()->sequence()->MoveClip(this,
|
||||
ca,
|
||||
qRound(double(timeline_in_) * multiplier),
|
||||
qRound(double(timeline_out_) * multiplier),
|
||||
qRound(double(clip_in_) * multiplier),
|
||||
track_);
|
||||
}
|
||||
|
||||
// move keyframes
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
EffectPtr e = effects.at(i);
|
||||
NodePtr e = effects.at(i);
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
EffectRow* r = e->row(j);
|
||||
for (int l=0;l<r->FieldCount();l++) {
|
||||
@@ -475,7 +539,7 @@ void Clip::Open() {
|
||||
}
|
||||
|
||||
// reset variable used to optimize uploading frame data
|
||||
texture_frame = -1;
|
||||
texture_timestamp = -1;
|
||||
|
||||
if (UsesCacher()) {
|
||||
// cacher will unlock open_lock
|
||||
@@ -493,12 +557,14 @@ void Clip::Close(bool wait) {
|
||||
open_ = false;
|
||||
|
||||
if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
close_active_clips(media()->to_sequence().get());
|
||||
media()->to_sequence()->Close();
|
||||
}
|
||||
|
||||
// destroy opengl texture in main thread
|
||||
delete texture;
|
||||
texture = nullptr;
|
||||
if (texture > 0) {
|
||||
QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture);
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
// close all effects
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
@@ -508,18 +574,10 @@ void Clip::Close(bool wait) {
|
||||
}
|
||||
|
||||
// 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;
|
||||
fbo.clear();
|
||||
|
||||
for (int j=0;j<fbo_count;j++) {
|
||||
delete fbo[j];
|
||||
}
|
||||
|
||||
delete [] fbo;
|
||||
|
||||
fbo = nullptr;
|
||||
}
|
||||
// delete OCIO shader
|
||||
ocio_shader = nullptr;
|
||||
|
||||
if (UsesCacher()) {
|
||||
cacher.Close(wait);
|
||||
@@ -563,60 +621,81 @@ bool Clip::Retrieve()
|
||||
|
||||
if (frame != nullptr && cacher.queue()->contains(frame)) {
|
||||
|
||||
//if (frame->pts != texture_timestamp) {
|
||||
|
||||
bool allocate_data = false;
|
||||
|
||||
QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();
|
||||
|
||||
// check if the opengl texture exists yet, create it if not
|
||||
if (texture == nullptr) {
|
||||
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
if (texture == 0) {
|
||||
|
||||
// create texture object
|
||||
f->glGenTextures(1, &texture);
|
||||
|
||||
f->glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
// set texture filtering to bilinear
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// set texture wrapping to clamp
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// queue an allocation ahead
|
||||
allocate_data = true;
|
||||
|
||||
} else {
|
||||
|
||||
f->glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
}
|
||||
|
||||
int video_width = cacher.media_width();
|
||||
int video_height = cacher.media_height();
|
||||
|
||||
const olive::PixelFormatInfo& pix_fmt_info = olive::pixel_formats.at(cacher.media_pixel_format());
|
||||
|
||||
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/pix_fmt_info.bytes_per_pixel);
|
||||
|
||||
if (allocate_data) {
|
||||
|
||||
// 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());
|
||||
f->glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
pix_fmt_info.internal_format,
|
||||
video_width,
|
||||
video_height,
|
||||
0,
|
||||
pix_fmt_info.pixel_format,
|
||||
pix_fmt_info.pixel_type,
|
||||
frame->data[0]
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
f->glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
video_width,
|
||||
video_height,
|
||||
pix_fmt_info.pixel_format,
|
||||
pix_fmt_info.pixel_type,
|
||||
frame->data[0]
|
||||
);
|
||||
|
||||
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||
texture->setMipLevels(texture->maximumMipLevels());
|
||||
texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
||||
texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount);
|
||||
f->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// 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;
|
||||
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
int frame_size = frame->linesize[0]*frame->height;
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
Effect* e = effects.at(i).get();
|
||||
if ((e->Flags() & Effect::ImageFlag) && e->IsEnabled()) {
|
||||
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(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]) {
|
||||
delete [] data_buffer_1;
|
||||
delete [] data_buffer_2;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
texture_timestamp = frame->pts;
|
||||
|
||||
ret = true;
|
||||
} else {
|
||||
@@ -631,7 +710,7 @@ bool Clip::Retrieve()
|
||||
|
||||
bool Clip::UsesCacher()
|
||||
{
|
||||
return track() >= 0 || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
return type() == olive::kTypeAudio || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
}
|
||||
|
||||
ClipSpeed::ClipSpeed() :
|
||||
|
||||
+48
-39
@@ -1,20 +1,20 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
@@ -30,18 +30,17 @@
|
||||
|
||||
#include "rendering/cacher.h"
|
||||
|
||||
#include "effects/effect.h"
|
||||
#include "nodes/node.h"
|
||||
#include "effects/transition.h"
|
||||
#include "undo/comboaction.h"
|
||||
#include "project/media.h"
|
||||
#include "project/footage.h"
|
||||
|
||||
#include "rendering/framebufferobject.h"
|
||||
#include "marker.h"
|
||||
#include "nodes/nodegraph.h"
|
||||
#include "selection.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
}
|
||||
class Track;
|
||||
|
||||
struct ClipSpeed {
|
||||
ClipSpeed();
|
||||
@@ -49,18 +48,21 @@ struct ClipSpeed {
|
||||
bool maintain_audio_pitch;
|
||||
};
|
||||
|
||||
using ClipPtr = std::shared_ptr<Clip>;
|
||||
|
||||
class Sequence;
|
||||
|
||||
class Clip {
|
||||
public:
|
||||
Clip(Sequence *s);
|
||||
Clip(Track *s);
|
||||
~Clip();
|
||||
ClipPtr copy(Sequence *s);
|
||||
ClipPtr copy(Track *s);
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
bool IsActiveAt(long timecode);
|
||||
bool IsSelected(bool containing = true);
|
||||
bool IsTransitionSelected(TransitionType type);
|
||||
|
||||
Selection ToSelection();
|
||||
|
||||
olive::TrackType type();
|
||||
|
||||
const QColor& color();
|
||||
void set_color(int r, int g, int b);
|
||||
@@ -75,17 +77,17 @@ public:
|
||||
long media_length();
|
||||
void set_media(Media* m, int s);
|
||||
|
||||
bool enabled();
|
||||
void set_enabled(bool e);
|
||||
|
||||
void move(ComboAction* ca,
|
||||
void Move(ComboAction* ca,
|
||||
long iin,
|
||||
long iout,
|
||||
long iclip_in,
|
||||
int itrack,
|
||||
Track *itrack,
|
||||
bool verify_transitions = true,
|
||||
bool relative = false);
|
||||
|
||||
bool enabled();
|
||||
void set_enabled(bool e);
|
||||
|
||||
long clip_in(bool with_transition = false);
|
||||
void set_clip_in(long c);
|
||||
|
||||
@@ -95,8 +97,8 @@ public:
|
||||
long timeline_out(bool with_transition = false);
|
||||
void set_timeline_out(long t);
|
||||
|
||||
int track();
|
||||
void set_track(int t);
|
||||
Track* track();
|
||||
void set_track(Track* t);
|
||||
|
||||
bool reversed();
|
||||
void set_reversed(bool r);
|
||||
@@ -121,15 +123,15 @@ public:
|
||||
long length();
|
||||
|
||||
void refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points);
|
||||
Sequence* sequence;
|
||||
Track* parent_;
|
||||
|
||||
// markers
|
||||
QVector<Marker>& get_markers();
|
||||
|
||||
// other variables (should be deep copied/duplicated in copy())
|
||||
int IndexOfEffect(Effect* e);
|
||||
QList<EffectPtr> effects;
|
||||
QVector<int> linked;
|
||||
int IndexOfEffect(Node* e);
|
||||
QList<NodePtr> effects;
|
||||
QVector<Clip*> linked;
|
||||
TransitionPtr opening_transition;
|
||||
TransitionPtr closing_transition;
|
||||
|
||||
@@ -152,17 +154,22 @@ public:
|
||||
QMutex cache_lock;
|
||||
|
||||
// video playback variables
|
||||
QOpenGLFramebufferObject** fbo;
|
||||
QOpenGLTexture* texture;
|
||||
long texture_frame;
|
||||
QVector<FramebufferObject> fbo;
|
||||
GLuint texture;
|
||||
int64_t texture_timestamp;
|
||||
|
||||
#ifndef NO_OCIO
|
||||
QOpenGLShaderProgramPtr ocio_shader;
|
||||
GLuint ocio_lut_texture;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// timeline variables (should be copied in copy())
|
||||
Track* track_;
|
||||
bool enabled_;
|
||||
long clip_in_;
|
||||
long timeline_in_;
|
||||
long timeline_out_;
|
||||
int track_;
|
||||
QString name_;
|
||||
Media* media_;
|
||||
int media_stream_;
|
||||
@@ -174,6 +181,8 @@ private:
|
||||
Cacher cacher;
|
||||
long cacher_frame;
|
||||
|
||||
NodeGraph pipeline_;
|
||||
|
||||
QVector<Marker> markers;
|
||||
QColor color_;
|
||||
bool open_;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "ghost.h"
|
||||
|
||||
Ghost::Ghost() :
|
||||
transition(nullptr),
|
||||
track_movement(0),
|
||||
clip(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Selection Ghost::ToSelection() const
|
||||
{
|
||||
return Selection(in, out, track->Sibling(track_movement));
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef GHOST_H
|
||||
#define GHOST_H
|
||||
|
||||
#include "effects/transition.h"
|
||||
#include "track.h"
|
||||
#include "project/media.h"
|
||||
|
||||
namespace olive {
|
||||
namespace timeline {
|
||||
|
||||
enum TrimType {
|
||||
TRIM_NONE,
|
||||
TRIM_IN,
|
||||
TRIM_OUT
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct Ghost {
|
||||
Ghost();
|
||||
|
||||
Clip* clip;
|
||||
|
||||
long in;
|
||||
long out;
|
||||
long clip_in;
|
||||
|
||||
long old_in;
|
||||
long old_out;
|
||||
long old_clip_in;
|
||||
|
||||
Track* track;
|
||||
int track_movement;
|
||||
|
||||
// importing variables
|
||||
Media* media;
|
||||
int media_stream;
|
||||
|
||||
// other variables
|
||||
long ghost_length;
|
||||
long media_length;
|
||||
olive::timeline::TrimType trim_type;
|
||||
|
||||
// transition trimming
|
||||
TransitionPtr transition;
|
||||
|
||||
Selection ToSelection() const;
|
||||
};
|
||||
|
||||
#endif // GHOST_H
|
||||
+94
-57
@@ -1,20 +1,20 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QInputDialog>
|
||||
#include <QCoreApplication>
|
||||
|
||||
void draw_marker(QPainter &p, int x, int y, int bottom, bool selected) {
|
||||
void Marker::Draw(QPainter &p, int x, int y, int bottom, bool selected) {
|
||||
const QPoint points[5] = {
|
||||
QPoint(x, bottom),
|
||||
QPoint(x + MARKER_SIZE, bottom - MARKER_SIZE),
|
||||
@@ -49,22 +49,24 @@ void draw_marker(QPainter &p, int x, int y, int bottom, bool selected) {
|
||||
p.drawPolygon(points, 5);
|
||||
}
|
||||
|
||||
void set_marker_internal(Sequence* seq, const QVector<int>& clips) {
|
||||
// if clips is empty, the marker is being added to the sequence
|
||||
void Marker::SetOnClips(const QVector<Clip *> &clips)
|
||||
{
|
||||
// Don't bother if there are no clips
|
||||
if (clips.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add_marker is used to determine whether we're adding a marker, depending on whether the user input a marker name
|
||||
// however if (config.set_name_with_marker) is true, we don't need a marker name so we just add
|
||||
bool add_marker = !olive::CurrentConfig.set_name_with_marker;
|
||||
bool add_marker = !olive::config.set_name_with_marker;
|
||||
|
||||
QString marker_name;
|
||||
|
||||
// if (config.set_name_with_marker) is false (set above), ask for a marker name
|
||||
// if Config::set_name_with_marker is true (set above), ask for a marker name
|
||||
if (!add_marker) {
|
||||
QInputDialog d(olive::MainWindow);
|
||||
d.setWindowTitle(QCoreApplication::translate("Marker", "Set Marker"));
|
||||
d.setLabelText(clips.size() > 0
|
||||
? QCoreApplication::translate("Marker", "Set clip marker name:")
|
||||
: QCoreApplication::translate("Marker", "Set sequence marker name:"));
|
||||
d.setLabelText(QCoreApplication::translate("Marker", "Set clip marker name:"));
|
||||
d.setInputMode(QInputDialog::TextInput);
|
||||
add_marker = (d.exec() == QDialog::Accepted);
|
||||
marker_name = d.textValue();
|
||||
@@ -75,43 +77,16 @@ void set_marker_internal(Sequence* seq, const QVector<int>& clips) {
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
if (clips.size() > 0) {
|
||||
|
||||
// add a marker action for each clip
|
||||
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(),
|
||||
marker_name));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// if no clips are selected, we're adding a marker to the sequence
|
||||
|
||||
// kind of hacky, we get the correct marker structure from the viewer panel object that the sequence is attached to
|
||||
if (seq == panel_footage_viewer->seq.get()) {
|
||||
|
||||
// get correct marker reference from footage viewer
|
||||
ca->append(new AddMarkerAction(panel_footage_viewer->marker_ref, seq->playhead, marker_name));
|
||||
|
||||
} else if (seq == panel_sequence_viewer->seq.get()) {
|
||||
|
||||
// get correct marker reference from sequence viewer
|
||||
ca->append(new AddMarkerAction(panel_sequence_viewer->marker_ref, seq->playhead, marker_name));
|
||||
|
||||
} else {
|
||||
|
||||
// fallback to using markers from sequence provided
|
||||
ca->append(new AddMarkerAction(&seq->markers, seq->playhead, marker_name));
|
||||
|
||||
}
|
||||
|
||||
// add a marker action for each clip
|
||||
foreach (Clip* c, clips) {
|
||||
ca->append(new AddMarkerAction(&c->get_markers(),
|
||||
c->track()->sequence()->playhead - c->timeline_in() + c->clip_in(),
|
||||
marker_name));
|
||||
}
|
||||
|
||||
|
||||
// push action
|
||||
olive::UndoStack.push(ca);
|
||||
olive::undo_stack.push(ca);
|
||||
|
||||
// redraw UI for new markers
|
||||
update_ui(false);
|
||||
@@ -120,9 +95,71 @@ void set_marker_internal(Sequence* seq, const QVector<int>& clips) {
|
||||
}
|
||||
}
|
||||
|
||||
void set_marker_internal(Sequence *seq) {
|
||||
// create empty clip array
|
||||
QVector<int> clips;
|
||||
void Marker::SetOnSequence(Sequence *seq) {
|
||||
|
||||
// Don't bother if there is no sequence
|
||||
if (seq == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add_marker is used to determine whether we're adding a marker, depending on whether the user input a marker name
|
||||
// however if (config.set_name_with_marker) is true, we don't need a marker name so we just add
|
||||
bool add_marker = !olive::config.set_name_with_marker;
|
||||
|
||||
QString marker_name;
|
||||
|
||||
// if Config::set_name_with_marker is true (set above), ask for a marker name
|
||||
if (!add_marker) {
|
||||
QInputDialog d(olive::MainWindow);
|
||||
d.setWindowTitle(QCoreApplication::translate("Marker", "Set Marker"));
|
||||
d.setLabelText(QCoreApplication::translate("Marker", "Set sequence marker name:"));
|
||||
d.setInputMode(QInputDialog::TextInput);
|
||||
add_marker = (d.exec() == QDialog::Accepted);
|
||||
marker_name = d.textValue();
|
||||
}
|
||||
|
||||
// if we've decided to add a marker
|
||||
if (add_marker) {
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
// FIXME kind of hacky, we get the correct marker structure from the viewer panel object that the sequence is
|
||||
// attached to, as the viewers will give us the footage marker set if its footage rather than the sequence marker
|
||||
// set
|
||||
|
||||
if (seq == panel_footage_viewer->seq.get()) {
|
||||
|
||||
// get correct marker reference from footage viewer
|
||||
ca->append(new AddMarkerAction(panel_footage_viewer->marker_ref, seq->playhead, marker_name));
|
||||
|
||||
} else if (seq == panel_sequence_viewer->seq.get()) {
|
||||
|
||||
// get correct marker reference from sequence viewer
|
||||
ca->append(new AddMarkerAction(panel_sequence_viewer->marker_ref, seq->playhead, marker_name));
|
||||
|
||||
} else {
|
||||
|
||||
// fallback to using markers from sequence provided
|
||||
ca->append(new AddMarkerAction(&seq->markers, seq->playhead, marker_name));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// push action
|
||||
olive::undo_stack.push(ca);
|
||||
|
||||
// redraw UI for new markers
|
||||
update_ui(false);
|
||||
panel_footage_viewer->update_viewer();
|
||||
|
||||
}
|
||||
|
||||
set_marker_internal(seq, clips);
|
||||
}
|
||||
|
||||
void Marker::Save(QXmlStreamWriter &stream) const
|
||||
{
|
||||
stream.writeStartElement("marker");
|
||||
stream.writeAttribute("frame", QString::number(frame));
|
||||
stream.writeAttribute("name", name);
|
||||
stream.writeEndElement();
|
||||
}
|
||||
|
||||
+21
-18
@@ -1,20 +1,20 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
@@ -25,19 +25,22 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QPainter>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <memory>
|
||||
|
||||
class Clip;
|
||||
class Sequence;
|
||||
using SequencePtr = std::shared_ptr<Sequence>;
|
||||
|
||||
struct Marker {
|
||||
long frame;
|
||||
QString name;
|
||||
|
||||
void Save(QXmlStreamWriter& stream) const;
|
||||
static void Draw(QPainter& p, int x, int y, int bottom, bool selected);
|
||||
|
||||
|
||||
static void SetOnClips(const QVector<Clip*>& clips);
|
||||
static void SetOnSequence(Sequence* seq);
|
||||
};
|
||||
|
||||
void draw_marker(QPainter& p, int x, int y, int bottom, bool selected);
|
||||
|
||||
void set_marker_internal(Sequence *seq, const QVector<int>& clips);
|
||||
void set_marker_internal(Sequence* seq);
|
||||
|
||||
#endif // MARKER_H
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "selection.h"
|
||||
|
||||
#include "effects/transition.h"
|
||||
#include "timeline/clip.h"
|
||||
|
||||
Selection::Selection()
|
||||
{
|
||||
}
|
||||
|
||||
Selection::Selection(long in, long out, Track *track) :
|
||||
in_(in),
|
||||
out_(out),
|
||||
track_(track)
|
||||
{
|
||||
}
|
||||
|
||||
long Selection::in() const
|
||||
{
|
||||
return in_;
|
||||
}
|
||||
|
||||
long Selection::out() const
|
||||
{
|
||||
return out_;
|
||||
}
|
||||
|
||||
Track *Selection::track() const
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
|
||||
void Selection::set_in(long in)
|
||||
{
|
||||
in_ = in;
|
||||
}
|
||||
|
||||
void Selection::set_out(long out)
|
||||
{
|
||||
out_ = out;
|
||||
}
|
||||
|
||||
bool Selection::ContainsTransition(Clip* c, int type) const
|
||||
{
|
||||
if (type == kTransitionOpening) {
|
||||
return c->opening_transition != nullptr
|
||||
&& out_ == c->timeline_in() + c->opening_transition->get_true_length()
|
||||
&& ((c->opening_transition->secondary_clip == nullptr && in_ == c->timeline_in())
|
||||
|| (c->opening_transition->secondary_clip != nullptr && in_ == c->timeline_in() - c->opening_transition->get_true_length()));
|
||||
} else {
|
||||
return c->closing_transition != nullptr
|
||||
&& in_ == c->timeline_out() - c->closing_transition->get_true_length()
|
||||
&& ((c->closing_transition->secondary_clip == nullptr && out_ == c->timeline_out())
|
||||
|| (c->closing_transition->secondary_clip != nullptr && out_ == c->timeline_out() + c->closing_transition->get_true_length()));
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::Tidy(QVector<Selection>& selections)
|
||||
{
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
Selection& s = selections[i];
|
||||
for (int j=0;j<selections.size();j++) {
|
||||
if (i != j) {
|
||||
Selection& ss = selections[j];
|
||||
if (s.track() == ss.track()) {
|
||||
bool remove = false;
|
||||
if (s.in() < ss.in() && s.out() > ss.out()) {
|
||||
// do nothing
|
||||
} else if (s.in() >= ss.in() && s.out() <= ss.out()) {
|
||||
remove = true;
|
||||
} else if (s.in() <= ss.out() && s.out() > ss.out()) {
|
||||
ss.set_out(s.out());
|
||||
remove = true;
|
||||
} else if (s.out() >= ss.in() && s.in() < ss.in()) {
|
||||
ss.set_in(s.in());
|
||||
remove = true;
|
||||
}
|
||||
if (remove) {
|
||||
selections.removeAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-20
@@ -1,36 +1,53 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SELECTION_H
|
||||
#define SELECTION_H
|
||||
|
||||
struct Selection {
|
||||
long in;
|
||||
long out;
|
||||
int track;
|
||||
#include <QVector>
|
||||
|
||||
long old_in;
|
||||
long old_out;
|
||||
int old_track;
|
||||
class Clip;
|
||||
class Track;
|
||||
|
||||
bool trim_in;
|
||||
class Selection {
|
||||
public:
|
||||
Selection();
|
||||
Selection(long in, long out, Track* track);
|
||||
|
||||
long in() const;
|
||||
long out() const;
|
||||
Track* track() const;
|
||||
|
||||
void set_in(long in);
|
||||
void set_out(long out);
|
||||
|
||||
bool ContainsTransition(Clip* c, int type) const;
|
||||
|
||||
static void Tidy(QVector<Selection> &selections);
|
||||
|
||||
private:
|
||||
long in_;
|
||||
long out_;
|
||||
Track* track_;
|
||||
|
||||
bool trim_in_;
|
||||
};
|
||||
|
||||
#endif // SELECTION_H
|
||||
|
||||
+1073
-112
File diff suppressed because it is too large
Load Diff
+86
-27
@@ -1,20 +1,20 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
@@ -27,32 +27,89 @@
|
||||
#include "clip.h"
|
||||
#include "marker.h"
|
||||
#include "selection.h"
|
||||
#include "tracklist.h"
|
||||
#include "ghost.h"
|
||||
|
||||
class Sequence {
|
||||
class Sequence : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Sequence();
|
||||
~Sequence();
|
||||
SequencePtr copy();
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
QString name;
|
||||
void getTrackLimits(int* video_tracks, int* audio_tracks);
|
||||
long getEndFrame();
|
||||
int width;
|
||||
int height;
|
||||
double frame_rate;
|
||||
int audio_frequency;
|
||||
int audio_layout;
|
||||
|
||||
void RefreshClips(Media* m = nullptr);
|
||||
long GetEndFrame();
|
||||
QVector<Clip*> GetAllClips();
|
||||
TrackList* GetTrackList(olive::TrackType type);
|
||||
|
||||
/**
|
||||
* @brief Close all open clips in a Sequence
|
||||
*
|
||||
* Closes any currently open clips on a Sequence and waits for them to close before returning. This may be slow as a
|
||||
* result on large Sequence objects. If a Clip is a nested Sequence, this function calls itself recursively on that
|
||||
* Sequence too.
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The Sequence to close all clips on.
|
||||
*/
|
||||
void Close();
|
||||
|
||||
void RefreshClipsUsingMedia(Media* m = nullptr);
|
||||
QVector<Clip*> SelectedClips(bool containing = true);
|
||||
QVector<int> SelectedClipIndexes();
|
||||
|
||||
Effect* GetSelectedGizmo();
|
||||
void AddClipsFromGhosts(ComboAction *ca, const QVector<Ghost> &ghosts);
|
||||
|
||||
void MoveClip(Clip* c,
|
||||
ComboAction* ca,
|
||||
long iin,
|
||||
long iout,
|
||||
long iclip_in,
|
||||
Track *itrack,
|
||||
bool verify_transitions = true,
|
||||
bool relative = false);
|
||||
|
||||
void EditToPoint(bool in, bool ripple);
|
||||
|
||||
bool SnapPoint(long* l, double zoom, bool use_playhead, bool use_markers, bool use_workarea);
|
||||
|
||||
void DeleteAreas(ComboAction* ca, QVector<Selection> areas, bool deselect_areas = false, bool ripple = false);
|
||||
void DeleteInToOut(bool ripple);
|
||||
void DeleteClipsUsingMedia(const QVector<Media *> &media);
|
||||
|
||||
void Ripple(ComboAction *ca, long point, long length, const QVector<Clip *> &ignore = QVector<Clip*>());
|
||||
|
||||
void ChangeTrackHeightsRelatively(int diff);
|
||||
|
||||
void ToggleLinksOnSelected();
|
||||
|
||||
void Split();
|
||||
bool SplitAllClipsAtPoint(ComboAction *ca, long point);
|
||||
bool SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector<long> positions, bool also_split_links = true);
|
||||
|
||||
void RippleDeleteEmptySpace(ComboAction *ca, Track *track, long point);
|
||||
void RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length);
|
||||
|
||||
Node* GetSelectedGizmo();
|
||||
|
||||
bool IsClipSelected(int clip_index, bool containing = true);
|
||||
bool IsClipSelected(Clip* clip, bool containing = true);
|
||||
bool IsTransitionSelected(Transition* t);
|
||||
|
||||
QVector<Selection> selections;
|
||||
void SelectAll();
|
||||
void SelectAtPlayhead();
|
||||
void ClearSelections();
|
||||
void AddSelectionsToClipboard(bool delete_originals);
|
||||
QVector<Selection> Selections();
|
||||
void SetSelections(const QVector<Selection>& selections);
|
||||
void TidySelections();
|
||||
|
||||
long playhead;
|
||||
|
||||
bool using_workarea;
|
||||
@@ -64,14 +121,16 @@ public:
|
||||
int save_id;
|
||||
|
||||
QVector<Marker> markers;
|
||||
QVector<ClipPtr> clips;
|
||||
signals:
|
||||
void Changed();
|
||||
private:
|
||||
QVector<TrackList*> track_lists_;
|
||||
|
||||
ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame);
|
||||
ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame, long post_in);
|
||||
bool SplitSelection(ComboAction* ca, QVector<Selection> selections);
|
||||
};
|
||||
|
||||
using SequencePtr = std::shared_ptr<Sequence>;
|
||||
|
||||
// static variable for the currently active sequence
|
||||
namespace olive {
|
||||
extern SequencePtr ActiveSequence;
|
||||
}
|
||||
|
||||
#endif // SEQUENCE_H
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "timelinefunctions.h"
|
||||
|
||||
#include "global/math.h"
|
||||
#include "global/config.h"
|
||||
#include "global/timing.h"
|
||||
#include "sequence.h"
|
||||
|
||||
// snapping
|
||||
bool olive::timeline::snapping = true;
|
||||
bool olive::timeline::snapped = false;
|
||||
long olive::timeline::snap_point = 0;
|
||||
|
||||
void olive::timeline::RelinkClips(QVector<Clip *> &pre_clips, QVector<ClipPtr> &post_clips)
|
||||
{
|
||||
// Loop through each "pre" clip
|
||||
for (int i=0;i<pre_clips.size();i++) {
|
||||
|
||||
Clip* pre = pre_clips.at(i);
|
||||
|
||||
// Loop through each "pre" clip's linked array
|
||||
for (int j=0;j<pre->linked.size();j++) {
|
||||
|
||||
// Loop again through the "pre" clips to determine which are linked to each other
|
||||
for (int l=0;l<pre_clips.size();l++) {
|
||||
|
||||
// If this "pre" is linked to this "pre"
|
||||
if (pre->linked.at(j) == pre_clips.at(l)) {
|
||||
|
||||
// Check if we have an equivalent in the post_clips, and link it if so
|
||||
if (post_clips.at(l) != nullptr) {
|
||||
post_clips.at(i)->linked.append(post_clips.at(l).get());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool olive::timeline::SnapToPoint(long point, long* l, double zoom) {
|
||||
long limit = getFrameFromScreenPoint(zoom, 10); // FIXME magic number 10 used for on screen pixel threshold to snap to
|
||||
if (*l > point-limit-1 && *l < point+limit+1) {
|
||||
olive::timeline::snap_point = point;
|
||||
*l = point;
|
||||
olive::timeline::snapped = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QVector<Ghost> olive::timeline::CreateGhostsFromMedia(Sequence *seq,
|
||||
long entry_point,
|
||||
QVector<olive::timeline::MediaImportData> &media_list)
|
||||
{
|
||||
QVector<Ghost> ghosts;
|
||||
|
||||
for (int i=0;i<media_list.size();i++) {
|
||||
bool can_import = true;
|
||||
|
||||
const olive::timeline::MediaImportData import_data = media_list.at(i);
|
||||
Media* medium = import_data.media();
|
||||
Footage* m = nullptr;
|
||||
Sequence* s = nullptr;
|
||||
long sequence_length = 0;
|
||||
long default_clip_in = 0;
|
||||
long default_clip_out = 0;
|
||||
|
||||
switch (medium->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
m = medium->to_footage();
|
||||
can_import = m->ready;
|
||||
if (m->using_inout) {
|
||||
double source_fr = 30;
|
||||
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) {
|
||||
source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
|
||||
}
|
||||
default_clip_in = rescale_frame_number(m->in, source_fr, seq->frame_rate);
|
||||
default_clip_out = rescale_frame_number(m->out, source_fr, seq->frame_rate);
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
s = medium->to_sequence().get();
|
||||
sequence_length = s->GetEndFrame();
|
||||
if (seq != nullptr) sequence_length = rescale_frame_number(sequence_length, s->frame_rate, seq->frame_rate);
|
||||
can_import = (s != seq && sequence_length != 0);
|
||||
if (s->using_workarea) {
|
||||
default_clip_in = rescale_frame_number(s->workarea_in, s->frame_rate, seq->frame_rate);
|
||||
default_clip_out = rescale_frame_number(s->workarea_out, s->frame_rate, seq->frame_rate);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
can_import = false;
|
||||
}
|
||||
|
||||
if (can_import) {
|
||||
Ghost g;
|
||||
g.clip = nullptr;
|
||||
g.trim_type = olive::timeline::TRIM_NONE;
|
||||
g.old_clip_in = g.clip_in = default_clip_in;
|
||||
g.media = medium;
|
||||
g.in = entry_point;
|
||||
g.transition = nullptr;
|
||||
|
||||
switch (medium->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
// is video source a still image?
|
||||
if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) {
|
||||
g.out = g.in + 100;
|
||||
} else {
|
||||
long length = m->get_length_in_frames(seq->frame_rate);
|
||||
g.out = entry_point + length - default_clip_in;
|
||||
if (m->using_inout) {
|
||||
g.out -= (length - default_clip_out);
|
||||
}
|
||||
}
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportAudioOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
if (m->audio_tracks.at(j).enabled) {
|
||||
g.track = seq->GetTrackList(olive::kTypeAudio)->TrackAt(j);
|
||||
g.media_stream = m->audio_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportVideoOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
if (m->video_tracks.at(j).enabled) {
|
||||
g.track = seq->GetTrackList(olive::kTypeVideo)->TrackAt(j);
|
||||
g.media_stream = m->video_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
g.out = entry_point + sequence_length - default_clip_in;
|
||||
|
||||
if (s->using_workarea) {
|
||||
g.out -= (sequence_length - default_clip_out);
|
||||
}
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportVideoOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
g.track = seq->GetTrackList(olive::kTypeVideo)->First();
|
||||
ghosts.append(g);
|
||||
}
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportAudioOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
g.track = seq->GetTrackList(olive::kTypeAudio)->First();
|
||||
ghosts.append(g);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
entry_point = g.out;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<ghosts.size();i++) {
|
||||
Ghost& g = ghosts[i];
|
||||
g.old_in = g.in;
|
||||
g.old_out = g.out;
|
||||
g.track_movement = 0;
|
||||
}
|
||||
|
||||
return ghosts;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef TIMELINEFUNCTIONS_H
|
||||
#define TIMELINEFUNCTIONS_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/mediaimportdata.h"
|
||||
#include "ghost.h"
|
||||
|
||||
namespace olive {
|
||||
namespace timeline {
|
||||
|
||||
enum CreateObjects {
|
||||
ADD_OBJ_TITLE,
|
||||
ADD_OBJ_SOLID,
|
||||
ADD_OBJ_BARS,
|
||||
ADD_OBJ_TONE,
|
||||
ADD_OBJ_NOISE,
|
||||
ADD_OBJ_AUDIO
|
||||
};
|
||||
|
||||
enum Alignment {
|
||||
kAlignmentTop,
|
||||
kAlignmentBottom,
|
||||
kAlignmentSingle
|
||||
};
|
||||
|
||||
void RelinkClips(QVector<Clip*>& pre_clips, QVector<ClipPtr> &post_clips);
|
||||
|
||||
bool SnapToPoint(long point, long* l, double zoom);
|
||||
|
||||
QVector<Ghost> CreateGhostsFromMedia(Sequence *seq, long entry_point, QVector<olive::timeline::MediaImportData> &media_list);
|
||||
|
||||
// snapping
|
||||
extern bool snapping;
|
||||
extern bool snapped;
|
||||
extern long snap_point;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TIMELINEFUNCTIONS_H
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "timelineshared.h"
|
||||
|
||||
TimelineShared::TimelineShared()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef TIMELINESHARED_H
|
||||
#define TIMELINESHARED_H
|
||||
|
||||
|
||||
class TimelineShared
|
||||
{
|
||||
public:
|
||||
TimelineShared();
|
||||
};
|
||||
|
||||
#endif // TIMELINESHARED_H
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "timelinetools.h"
|
||||
|
||||
olive::timeline::Tool olive::timeline::current_tool = olive::timeline::TIMELINE_TOOL_POINTER;
|
||||
@@ -0,0 +1,47 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINETOOLS_H
|
||||
#define TIMELINETOOLS_H
|
||||
|
||||
namespace olive {
|
||||
namespace timeline {
|
||||
|
||||
enum Tool {
|
||||
TIMELINE_TOOL_POINTER,
|
||||
TIMELINE_TOOL_EDIT,
|
||||
TIMELINE_TOOL_RAZOR,
|
||||
TIMELINE_TOOL_RIPPLE,
|
||||
TIMELINE_TOOL_ROLLING,
|
||||
TIMELINE_TOOL_SLIP,
|
||||
TIMELINE_TOOL_SLIDE,
|
||||
TIMELINE_TOOL_HAND,
|
||||
TIMELINE_TOOL_ZOOM,
|
||||
TIMELINE_TOOL_MENU,
|
||||
TIMELINE_TOOL_TRANSITION,
|
||||
TIMELINE_TOOL_COUNT
|
||||
};
|
||||
|
||||
extern Tool current_tool;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TIMELINETOOLS_H
|
||||
@@ -0,0 +1,436 @@
|
||||
#include "track.h"
|
||||
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/tracklist.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "global/math.h"
|
||||
|
||||
int olive::timeline::kTrackDefaultHeight = 40;
|
||||
int olive::timeline::kTrackMinHeight = 30;
|
||||
int olive::timeline::kTrackHeightIncrement = 10;
|
||||
|
||||
Track::Track(TrackList* parent, olive::TrackType type) :
|
||||
parent_(parent),
|
||||
type_(type),
|
||||
muted_(false),
|
||||
soloed_(false),
|
||||
locked_(false),
|
||||
height_(olive::timeline::kTrackDefaultHeight)
|
||||
{
|
||||
}
|
||||
|
||||
Track *Track::copy(TrackList *parent)
|
||||
{
|
||||
Track* t = new Track(parent, type_);
|
||||
|
||||
t->ResizeClipArray(ClipCount());
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
ClipPtr c = clips_.at(i);
|
||||
ClipPtr copy = c->copy(t);
|
||||
copy->linked = c->linked;
|
||||
t->clips_[i] = copy;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
Sequence *Track::sequence()
|
||||
{
|
||||
return parent_->GetParent();
|
||||
}
|
||||
|
||||
TrackList *Track::track_list()
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
void Track::Save(QXmlStreamWriter &stream)
|
||||
{
|
||||
stream.writeStartElement("track");
|
||||
|
||||
for (int j=0;j<clips_.size();j++) {
|
||||
Clip* c = clips_.at(j).get();
|
||||
|
||||
c->Save(stream);
|
||||
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // track
|
||||
}
|
||||
|
||||
olive::TrackType Track::type()
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
int Track::height()
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
void Track::set_height(int h)
|
||||
{
|
||||
height_ = qMax(h, olive::timeline::kTrackMinHeight);
|
||||
emit HeightChanged(h);
|
||||
}
|
||||
|
||||
QString Track::name()
|
||||
{
|
||||
if (name_.isEmpty()) {
|
||||
int display_index = Index() + 1;
|
||||
|
||||
switch (type_) {
|
||||
case olive::kTypeVideo:
|
||||
return tr("Video %1").arg(display_index);
|
||||
case olive::kTypeAudio:
|
||||
return tr("Audio %1").arg(display_index);
|
||||
case olive::kTypeSubtitle:
|
||||
return tr("Subtitle %1").arg(display_index);
|
||||
default:
|
||||
return tr("Unknown %1").arg(display_index);
|
||||
}
|
||||
}
|
||||
|
||||
return name_;
|
||||
}
|
||||
|
||||
void Track::SetName(const QString &s)
|
||||
{
|
||||
name_ = s;
|
||||
}
|
||||
|
||||
void Track::AddClip(ClipPtr clip)
|
||||
{
|
||||
if (clips_.contains(clip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clips_.append(clip);
|
||||
if (clip->track() != nullptr && clip->track() != this) {
|
||||
clip->track()->RemoveClip(clip.get());
|
||||
}
|
||||
clip->set_track(this);
|
||||
}
|
||||
|
||||
int Track::ClipCount()
|
||||
{
|
||||
return clips_.size();
|
||||
}
|
||||
|
||||
ClipPtr Track::GetClip(int i)
|
||||
{
|
||||
return clips_.at(i);
|
||||
}
|
||||
|
||||
void Track::RemoveClip(int i)
|
||||
{
|
||||
clips_.removeAt(i);
|
||||
}
|
||||
|
||||
void Track::RemoveClip(Clip *c)
|
||||
{
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
if (clips_.at(i).get() == c) {
|
||||
clips_.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Track::ResizeClipArray(int new_size)
|
||||
{
|
||||
clips_.resize(new_size);
|
||||
}
|
||||
|
||||
QVector<Clip*> Track::GetAllClips()
|
||||
{
|
||||
QVector<Clip*> clips;
|
||||
|
||||
clips.resize(clips_.size());
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
clips[i] = clips_.at(i).get();
|
||||
}
|
||||
|
||||
return clips;
|
||||
}
|
||||
|
||||
QVector<Clip *> Track::GetSelectedClips(bool containing)
|
||||
{
|
||||
QVector<Clip*> selected_clips;
|
||||
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
|
||||
Clip* c = clips_.at(i).get();
|
||||
|
||||
if (IsClipSelected(c, containing)) {
|
||||
selected_clips.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return selected_clips;
|
||||
}
|
||||
|
||||
ClipPtr Track::GetClipObjectFromRawPtr(Clip *c)
|
||||
{
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
if (clips_.at(i).get() == c) {
|
||||
return clips_.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert here since we shouldn't be calling this function ever
|
||||
Q_ASSERT(false);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Clip *Track::GetClipFromPoint(long point)
|
||||
{
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
Clip* c = clips_.at(i).get();
|
||||
|
||||
if (c->timeline_in() <= point && c->timeline_out() > point) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Track::ContainsClip(Clip *c)
|
||||
{
|
||||
ClipPtr clip;
|
||||
foreach (clip, clips_) {
|
||||
if (clip.get() == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Track *Track::Previous()
|
||||
{
|
||||
int index = Index();
|
||||
|
||||
if (index == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return parent_->TrackAt(index - 1);
|
||||
}
|
||||
|
||||
Track *Track::Next()
|
||||
{
|
||||
return parent_->TrackAt(Index() + 1);
|
||||
}
|
||||
|
||||
Track *Track::Sibling(int diff)
|
||||
{
|
||||
if (diff == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return track_list()->TrackAt(qMax(0, Index() + diff));
|
||||
}
|
||||
|
||||
int Track::Index()
|
||||
{
|
||||
return parent_->IndexOfTrack(this);
|
||||
}
|
||||
|
||||
bool Track::IsClipSelected(int clip_index, bool containing)
|
||||
{
|
||||
return IsClipSelected(clips_.at(clip_index).get(), containing);
|
||||
}
|
||||
|
||||
bool Track::IsClipSelected(Clip *clip, bool containing)
|
||||
{
|
||||
for (int i=0;i<selections_.size();i++) {
|
||||
const Selection& s = selections_.at(i);
|
||||
if (((clip->timeline_in() >= s.in() && clip->timeline_out() <= s.out())
|
||||
|| (!containing && !(clip->timeline_in() >= s.out() || clip->timeline_out() <= s.in())))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Track::IsTransitionSelected(Transition *t)
|
||||
{
|
||||
if (t == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Clip* c = t->parent_clip;
|
||||
|
||||
long transition_in_point;
|
||||
long transition_out_point;
|
||||
|
||||
// Get positions of the transition on the timeline
|
||||
|
||||
if (t == c->opening_transition.get()) {
|
||||
transition_in_point = c->timeline_in();
|
||||
transition_out_point = c->timeline_in() + t->get_true_length();
|
||||
|
||||
if (t->secondary_clip != nullptr) {
|
||||
transition_in_point -= t->get_true_length();
|
||||
}
|
||||
} else {
|
||||
transition_in_point = c->timeline_out() - t->get_true_length();
|
||||
transition_out_point = c->timeline_out();
|
||||
|
||||
if (t->secondary_clip != nullptr) {
|
||||
transition_out_point += t->get_true_length();
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's a selection matching this
|
||||
for (int i=0;i<selections_.size();i++) {
|
||||
const Selection& s = selections_.at(i);
|
||||
if (s.in() <= transition_in_point
|
||||
&& s.out() >= transition_out_point) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Track::SelectArea(long in, long out)
|
||||
{
|
||||
selections_.append(Selection(in, out, this));
|
||||
Selection::Tidy(selections_);
|
||||
}
|
||||
|
||||
void Track::SelectClip(Clip* c)
|
||||
{
|
||||
SelectArea(c->timeline_in(), c->timeline_out());
|
||||
}
|
||||
|
||||
void Track::SelectAll()
|
||||
{
|
||||
ClearSelections();
|
||||
|
||||
// Select every clip
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
SelectClip(clips_.at(i).get());
|
||||
}
|
||||
}
|
||||
|
||||
void Track::SelectAtPoint(long point)
|
||||
{
|
||||
ClearSelections();
|
||||
|
||||
// Select every clip that this point is within
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
|
||||
Clip* c = clips_.at(i).get();
|
||||
|
||||
if (c->timeline_in() <= point
|
||||
&& c->timeline_out() > point) {
|
||||
SelectClip(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Track::ClearSelections()
|
||||
{
|
||||
selections_.clear();
|
||||
}
|
||||
|
||||
void Track::DeselectArea(long in, long out)
|
||||
{
|
||||
int selection_count = selections_.size();
|
||||
for (int i=0;i<selection_count;i++) {
|
||||
Selection& s = selections_[i];
|
||||
|
||||
if (s.in() >= in && s.out() <= out) {
|
||||
// whole selection is in deselect area
|
||||
selections_.removeAt(i);
|
||||
i--;
|
||||
selection_count--;
|
||||
} else if (s.in() < in && s.out() > out) {
|
||||
// middle of selection is in deselect area
|
||||
Selection new_sel(out, s.out(), s.track());
|
||||
selections_.append(new_sel);
|
||||
|
||||
s.set_out(in);
|
||||
} else if (s.in() < in && s.out() > in) {
|
||||
// only out point is in deselect area
|
||||
s.set_out(in);
|
||||
} else if (s.in() < out && s.out() > out) {
|
||||
// only in point is in deselect area
|
||||
s.set_in(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<Selection> Track::Selections()
|
||||
{
|
||||
return selections_;
|
||||
}
|
||||
|
||||
long Track::GetEndFrame()
|
||||
{
|
||||
long end_frame = 0;
|
||||
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
Clip* c = clips_.at(i).get();
|
||||
if (c != nullptr) {
|
||||
end_frame = qMax(end_frame, c->timeline_out(true));
|
||||
}
|
||||
}
|
||||
|
||||
return end_frame;
|
||||
}
|
||||
|
||||
bool Track::IsEffectivelyMuted()
|
||||
{
|
||||
// Check if this track is muted
|
||||
if (muted_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any tracks are soloed
|
||||
bool a_track_is_soloed = false;
|
||||
|
||||
for (int i=0;i<track_list()->TrackCount();i++) {
|
||||
if (track_list()->TrackAt(i)->IsSoloed()) {
|
||||
a_track_is_soloed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return if a track is soloed and this track is not soloed
|
||||
return (a_track_is_soloed && !soloed_);
|
||||
}
|
||||
|
||||
bool Track::IsMuted()
|
||||
{
|
||||
return muted_;
|
||||
}
|
||||
|
||||
void Track::SetMuted(bool muted)
|
||||
{
|
||||
muted_ = muted;
|
||||
}
|
||||
|
||||
bool Track::IsSoloed()
|
||||
{
|
||||
return soloed_;
|
||||
}
|
||||
|
||||
void Track::SetSoloed(bool soloed)
|
||||
{
|
||||
soloed_ = soloed;
|
||||
}
|
||||
|
||||
bool Track::IsLocked()
|
||||
{
|
||||
return locked_;
|
||||
}
|
||||
|
||||
void Track::SetLocked(bool locked)
|
||||
{
|
||||
locked_ = locked;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#ifndef TRACK_H
|
||||
#define TRACK_H
|
||||
|
||||
#include <QVector>
|
||||
#include <memory>
|
||||
|
||||
#include "nodes/node.h"
|
||||
#include "tracktypes.h"
|
||||
#include "undo/comboaction.h"
|
||||
#include "timeline/selection.h"
|
||||
|
||||
class Sequence;
|
||||
class Transition;
|
||||
|
||||
namespace olive {
|
||||
namespace timeline {
|
||||
const int kGhostThickness = 2;
|
||||
const int kClipTextPadding = 3;
|
||||
|
||||
/**
|
||||
* @brief Set default track sizes
|
||||
*
|
||||
* Olive has a few default constants used for adjusting track heights in the Timeline. For HiDPI, it makes
|
||||
* sense to multiply these by the current DPI scale. It uses a variable from QApplication to do this multiplication,
|
||||
* which means the QApplication instance needs to be instantiated before these are calculated. Therefore, call this
|
||||
* function ONCE after QApplication is created to multiply the track heights correctly.
|
||||
*/
|
||||
void MultiplyTrackSizesByDPI();
|
||||
|
||||
extern int kTrackDefaultHeight;
|
||||
extern int kTrackMinHeight;
|
||||
extern int kTrackHeightIncrement;
|
||||
|
||||
extern int kTimelineLabelFixedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackList;
|
||||
|
||||
class Track : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Track(TrackList* parent, olive::TrackType type);
|
||||
Track* copy(TrackList* parent);
|
||||
|
||||
Sequence* sequence();
|
||||
TrackList* track_list();
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
olive::TrackType type();
|
||||
|
||||
int height();
|
||||
void set_height(int h);
|
||||
|
||||
QString name();
|
||||
void SetName(const QString& s);
|
||||
|
||||
void AddClip(ClipPtr clip);
|
||||
int ClipCount();
|
||||
ClipPtr GetClip(int i);
|
||||
void RemoveClip(int i);
|
||||
void RemoveClip(Clip* c);
|
||||
QVector<Clip*> GetAllClips();
|
||||
QVector<Clip*> GetSelectedClips(bool containing);
|
||||
ClipPtr GetClipObjectFromRawPtr(Clip* c);
|
||||
Clip* GetClipFromPoint(long point);
|
||||
bool ContainsClip(Clip* c);
|
||||
|
||||
Track* Previous();
|
||||
Track* Next();
|
||||
Track* Sibling(int diff);
|
||||
|
||||
int Index();
|
||||
|
||||
bool IsClipSelected(int clip_index, bool containing = true);
|
||||
bool IsClipSelected(Clip* clip, bool containing = true);
|
||||
bool IsTransitionSelected(Transition* t);
|
||||
|
||||
void DeleteArea(ComboAction *ca, const Selection& s);
|
||||
void DeleteArea(ComboAction *ca, long in, long out);
|
||||
|
||||
void SelectArea(long in, long out);
|
||||
void SelectClip(Clip *c);
|
||||
void SelectAll();
|
||||
void SelectAtPoint(long point);
|
||||
void TidySelections();
|
||||
void ClearSelections();
|
||||
void DeselectArea(long in, long out);
|
||||
QVector<Selection> Selections();
|
||||
|
||||
long GetEndFrame();
|
||||
|
||||
bool IsEffectivelyMuted();
|
||||
bool IsMuted();
|
||||
bool IsSoloed();
|
||||
bool IsLocked();
|
||||
|
||||
public slots:
|
||||
void SetMuted(bool muted);
|
||||
void SetSoloed(bool soloed);
|
||||
void SetLocked(bool locked);
|
||||
|
||||
signals:
|
||||
void HeightChanged(int height);
|
||||
|
||||
private:
|
||||
void ResizeClipArray(int new_size);
|
||||
|
||||
TrackList* parent_;
|
||||
olive::TrackType type_;
|
||||
int height_;
|
||||
QVector<ClipPtr> clips_;
|
||||
QVector<NodePtr> effects_;
|
||||
QVector<Selection> selections_;
|
||||
|
||||
bool muted_;
|
||||
bool soloed_;
|
||||
bool locked_;
|
||||
|
||||
QString name_;
|
||||
};
|
||||
|
||||
#endif // TRACK_H
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "tracklist.h"
|
||||
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
TrackList::TrackList(Sequence *parent, olive::TrackType type) :
|
||||
QObject(parent),
|
||||
type_(type)
|
||||
{
|
||||
// Ensure we have at least one track
|
||||
AddTrack();
|
||||
}
|
||||
|
||||
void TrackList::Save(QXmlStreamWriter &stream)
|
||||
{
|
||||
stream.writeStartElement("Tracks");
|
||||
|
||||
for (int i=0;i<tracks_.size();i++) {
|
||||
tracks_.at(i)->Save(stream);
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // Tracks
|
||||
}
|
||||
|
||||
TrackList* TrackList::copy(Sequence *parent)
|
||||
{
|
||||
TrackList* t = new TrackList(parent, type_);
|
||||
|
||||
t->ResizeTrackArray(tracks_.size());
|
||||
for (int i=0;i<tracks_.size();i++) {
|
||||
t->tracks_[i] = tracks_.at(i)->copy(t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void TrackList::AddTrack()
|
||||
{
|
||||
Track* track = new Track(this, type_);
|
||||
tracks_.append(track);
|
||||
|
||||
emit TrackCountChanged();
|
||||
}
|
||||
|
||||
void TrackList::RemoveTrack(int i)
|
||||
{
|
||||
if (tracks_.size() == 1) {
|
||||
return;
|
||||
}
|
||||
tracks_.removeAt(i);
|
||||
|
||||
emit TrackCountChanged();
|
||||
}
|
||||
|
||||
Track *TrackList::First()
|
||||
{
|
||||
return tracks_.first();
|
||||
}
|
||||
|
||||
Track *TrackList::Last()
|
||||
{
|
||||
return tracks_.last();
|
||||
}
|
||||
|
||||
int TrackList::TrackCount()
|
||||
{
|
||||
return tracks_.size();
|
||||
}
|
||||
|
||||
int TrackList::IndexOfTrack(Track *track)
|
||||
{
|
||||
for (int i=0;i<tracks_.size();i++) {
|
||||
if (tracks_.at(i) == track) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Track *TrackList::TrackAt(int i)
|
||||
{
|
||||
while (i >= tracks_.size()) {
|
||||
AddTrack();
|
||||
}
|
||||
|
||||
return tracks_.at(i);
|
||||
}
|
||||
|
||||
QVector<Track*> TrackList::tracks()
|
||||
{
|
||||
return tracks_;
|
||||
}
|
||||
|
||||
olive::TrackType TrackList::type()
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
Sequence *TrackList::GetParent()
|
||||
{
|
||||
return static_cast<Sequence*>(parent());
|
||||
}
|
||||
|
||||
void TrackList::ResizeTrackArray(int i)
|
||||
{
|
||||
tracks_.resize(i);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef TRACKLIST_H
|
||||
#define TRACKLIST_H
|
||||
|
||||
#include "track.h"
|
||||
|
||||
class TrackList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TrackList(Sequence* parent, olive::TrackType type);
|
||||
TrackList* copy(Sequence* parent);
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
void AddTrack();
|
||||
void RemoveTrack(int i);
|
||||
Track* First();
|
||||
Track* Last();
|
||||
int TrackCount();
|
||||
int IndexOfTrack(Track* track);
|
||||
Track* TrackAt(int i);
|
||||
QVector<Track*> tracks();
|
||||
|
||||
olive::TrackType type();
|
||||
|
||||
Sequence* GetParent();
|
||||
|
||||
signals:
|
||||
void TrackCountChanged();
|
||||
|
||||
private:
|
||||
void ResizeTrackArray(int i);
|
||||
|
||||
olive::TrackType type_;
|
||||
QVector<Track*> tracks_;
|
||||
};
|
||||
|
||||
#endif // TRACKLIST_H
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef TRACKTYPES_H
|
||||
#define TRACKTYPES_H
|
||||
|
||||
namespace olive {
|
||||
enum TrackType {
|
||||
kTypeVideo,
|
||||
kTypeAudio,
|
||||
kTypeSubtitle,
|
||||
kTypeCount
|
||||
};
|
||||
};
|
||||
|
||||
#endif // TRACKTYPES_H
|
||||
Reference in New Issue
Block a user