many fixes and ripple edit corrections
This commit is contained in:
+2
-2
@@ -75,8 +75,8 @@ public:
|
||||
LabelSlider* intensity_val;
|
||||
LabelSlider* rotation_val;
|
||||
LabelSlider* frequency_val;
|
||||
private slots:
|
||||
void set_values();
|
||||
public slots:
|
||||
void init();
|
||||
private:
|
||||
int shake_progress;
|
||||
int shake_limit;
|
||||
|
||||
@@ -33,7 +33,7 @@ ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFF
|
||||
rotation_val->set_value(0);
|
||||
frequency_val->set_value(10);
|
||||
|
||||
set_values();
|
||||
init();
|
||||
|
||||
connect(intensity_val, SIGNAL(valueChanged()), this, SLOT(field_changed()));
|
||||
connect(rotation_val, SIGNAL(valueChanged()), this, SLOT(field_changed()));
|
||||
@@ -43,12 +43,14 @@ ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFF
|
||||
connect(frequency_val, SIGNAL(valueChanged()), this, SLOT(set_values()));
|
||||
}
|
||||
|
||||
void ShakeEffect::set_values() {
|
||||
shake_limit = qRound(parent_clip->sequence->frame_rate / frequency_val->value());
|
||||
shake_progress = shake_limit;
|
||||
next_x = 0;
|
||||
next_y = 0;
|
||||
next_rot = 0;
|
||||
void ShakeEffect::init() {
|
||||
if (parent_clip->sequence != NULL) {
|
||||
shake_limit = qRound(parent_clip->sequence->frame_rate / frequency_val->value());
|
||||
shake_progress = shake_limit;
|
||||
next_x = 0;
|
||||
next_y = 0;
|
||||
next_rot = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Effect* ShakeEffect::copy(Clip* c) {
|
||||
|
||||
@@ -85,15 +85,16 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_T
|
||||
}
|
||||
|
||||
void TransformEffect::init() {
|
||||
if (parent_clip->media_stream != NULL) {
|
||||
MediaStream* ms = parent_clip->media->get_stream_from_file_index(parent_clip->media_stream);
|
||||
if (ms != NULL && parent_clip->sequence != NULL) {
|
||||
position_x->set_default_value(parent_clip->sequence->width/2);
|
||||
position_y->set_default_value(parent_clip->sequence->height/2);
|
||||
scale_x->set_default_value(100);
|
||||
scale_y->set_default_value(100);
|
||||
scale_y->setEnabled(false);
|
||||
uniform_scale_box->setChecked(true);
|
||||
default_anchor_x = parent_clip->media_stream->video_width/2;
|
||||
default_anchor_y = parent_clip->media_stream->video_height/2;
|
||||
default_anchor_x = ms->video_width/2;
|
||||
default_anchor_y = ms->video_height/2;
|
||||
anchor_x_box->set_default_value(default_anchor_x);
|
||||
anchor_y_box->set_default_value(default_anchor_y);
|
||||
opacity->set_default_value(100);
|
||||
|
||||
@@ -619,3 +619,13 @@ void MainWindow::on_actionLink_Unlink_triggered()
|
||||
{
|
||||
panel_timeline->toggle_links();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionRipple_To_In_Point_triggered()
|
||||
{
|
||||
panel_timeline->ripple_to_in_point(true);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionRipple_to_Out_Point_triggered()
|
||||
{
|
||||
panel_timeline->ripple_to_in_point(false);
|
||||
}
|
||||
|
||||
@@ -154,6 +154,10 @@ private slots:
|
||||
|
||||
void on_actionLink_Unlink_triggered();
|
||||
|
||||
void on_actionRipple_To_In_Point_triggered();
|
||||
|
||||
void on_actionRipple_to_Out_Point_triggered();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
void setup_layout();
|
||||
|
||||
+19
-1
@@ -74,9 +74,11 @@
|
||||
<addaction name="actionSelect_All"/>
|
||||
<addaction name="actionDeselect_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSplit_at_Playhead"/>
|
||||
<addaction name="actionAdd_Default_Transition"/>
|
||||
<addaction name="actionLink_Unlink"/>
|
||||
<addaction name="actionRipple_To_In_Point"/>
|
||||
<addaction name="actionRipple_to_Out_Point"/>
|
||||
<addaction name="actionSplit_at_Playhead"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
@@ -621,6 +623,22 @@
|
||||
<string>Ctrl+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_To_In_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_to_Out_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>W</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
||||
+9
-11
@@ -354,30 +354,30 @@ void Project::import_dialog() {
|
||||
process_file_list(files);
|
||||
}
|
||||
|
||||
void Project::set_item_to_folder(QTreeWidgetItem* item) {
|
||||
void set_item_to_folder(QTreeWidgetItem* item) {
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOLDER);
|
||||
}
|
||||
|
||||
Media* Project::get_media_from_tree(QTreeWidgetItem* item) {
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item) {
|
||||
return reinterpret_cast<Media*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
|
||||
}
|
||||
|
||||
void Project::set_media_of_tree(QTreeWidgetItem* item, Media* media) {
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media) {
|
||||
item->setText(0, media->name);
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOOTAGE);
|
||||
item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast<quintptr>(media)));
|
||||
}
|
||||
|
||||
Sequence* Project::get_sequence_from_tree(QTreeWidgetItem* item) {
|
||||
Sequence* get_sequence_from_tree(QTreeWidgetItem* item) {
|
||||
return reinterpret_cast<Sequence*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
|
||||
}
|
||||
|
||||
void Project::set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence) {
|
||||
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence) {
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_SEQUENCE);
|
||||
item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast<quintptr>(sequence)));
|
||||
}
|
||||
|
||||
int Project::get_type_from_tree(QTreeWidgetItem* item) {
|
||||
int get_type_from_tree(QTreeWidgetItem* item) {
|
||||
return item->data(0, Qt::UserRole + 1).toInt();
|
||||
}
|
||||
|
||||
@@ -588,7 +588,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
if (stream.name() == "clip" && stream.isStartElement()) {
|
||||
//<clip id="0" name="Brock_Drying_Pan.gif" clipin="0" in="44" out="144" track="-1" r="192" g="128" b="128" media="2" stream="0">
|
||||
int media_id, stream_id;
|
||||
Clip* c = new Clip();
|
||||
Clip* c = new Clip(s);
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
if (attr.name() == "name") {
|
||||
@@ -621,13 +621,11 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
Media* m = loaded_media.at(j);
|
||||
if (m->save_id == media_id) {
|
||||
c->media = m;
|
||||
c->media_stream = c->media->get_stream_from_file_index(stream_id);
|
||||
c->media_stream = stream_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c->sequence = s;
|
||||
|
||||
// load links and effects
|
||||
while (!(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) {
|
||||
stream.readNext();
|
||||
@@ -864,7 +862,7 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
stream.writeAttribute("g", QString::number(c->color_g));
|
||||
stream.writeAttribute("b", QString::number(c->color_b));
|
||||
stream.writeAttribute("media", QString::number(c->media->save_id));
|
||||
stream.writeAttribute("stream", QString::number(c->media_stream->file_index));
|
||||
stream.writeAttribute("stream", QString::number(c->media_stream));
|
||||
|
||||
stream.writeStartElement("linked"); // linked
|
||||
for (int k=0;k<c->linked.size();k++) {
|
||||
|
||||
+11
-9
@@ -37,6 +37,13 @@ extern QString project_url;
|
||||
extern QStringList recent_projects;
|
||||
extern QString recent_proj_file;
|
||||
|
||||
int get_type_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item);
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
Sequence* get_sequence_from_tree(QTreeWidgetItem* item);
|
||||
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence);
|
||||
void set_item_to_folder(QTreeWidgetItem* item);
|
||||
|
||||
class Project : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -47,13 +54,10 @@ public:
|
||||
bool is_focused();
|
||||
void clear();
|
||||
QTreeWidgetItem* import_file(QString url);
|
||||
void import_dialog();
|
||||
void new_sequence(TimelineAction* ta, Sequence* s, bool open, QTreeWidgetItem* parent);
|
||||
QString get_next_sequence_name();
|
||||
void delete_media(QTreeWidgetItem* item);
|
||||
void delete_selected_media();
|
||||
void process_file_list(QStringList& files);
|
||||
void duplicate_selected();
|
||||
|
||||
void new_project();
|
||||
void load_project();
|
||||
@@ -61,15 +65,13 @@ public:
|
||||
|
||||
QTreeWidgetItem* new_folder();
|
||||
|
||||
int get_type_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item);
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
Sequence* get_sequence_from_tree(QTreeWidgetItem* item);
|
||||
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence);
|
||||
void set_item_to_folder(QTreeWidgetItem* item);
|
||||
void save_recent_projects();
|
||||
|
||||
SourceTable* source_table;
|
||||
public slots:
|
||||
void import_dialog();
|
||||
void delete_selected_media();
|
||||
void duplicate_selected();
|
||||
private:
|
||||
Ui::Project *ui;
|
||||
QTreeWidgetItem* new_item();
|
||||
|
||||
+109
-49
@@ -285,44 +285,55 @@ void Timeline::delete_selection(bool ripple_delete) {
|
||||
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
|
||||
long ripple_point = selections.at(0).in;
|
||||
long ripple_length = selections.at(0).out - selections.at(0).in;
|
||||
|
||||
// retrieve ripple_point and ripple_length from current selection
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (ripple_delete) {
|
||||
if (ripple_point > s.in) ripple_point = s.in;
|
||||
if (ripple_length > s.out - s.in) ripple_length = s.out - s.in;
|
||||
}
|
||||
}
|
||||
delete_areas_and_relink(ta, selections);
|
||||
selections.clear();
|
||||
|
||||
if (ripple_delete) {
|
||||
long validator;
|
||||
if (ripple_delete) {
|
||||
long ripple_point = selections.at(0).in;
|
||||
long ripple_length = selections.at(0).out - selections.at(0).in;
|
||||
|
||||
// retrieve ripple_point and ripple_length from current selection
|
||||
for (int i=1;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
ripple_point = qMin(ripple_point, s.in);
|
||||
ripple_length = qMin(ripple_length, s.out - s.in);
|
||||
}
|
||||
|
||||
// it feels a little more intuitive with this here
|
||||
ripple_point++;
|
||||
|
||||
QVector<int> ignore;
|
||||
bool can_ripple = true;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
// check every clip after and see if it'll collide
|
||||
// NOTE, we could probably re-use the validation code for the ripple tool here for optimization (since it's technically better code I think)
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (c != NULL && c->timeline_in >= ripple_point) {
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
Clip* cc = sequence->get_clip(j);
|
||||
if (cc != NULL && cc->timeline_in < ripple_point) {
|
||||
validator = c->timeline_in - ripple_length - cc->timeline_out;
|
||||
if (validator < 0) ripple_length += validator;
|
||||
|
||||
if (ripple_length <= 0) {
|
||||
// we've seen all we need to see here (can't ripple so stop looping)
|
||||
i = j = sequence->clip_count();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c->timeline_in < ripple_point && c->timeline_out > ripple_point) {
|
||||
// conflict detected, but this clip may be getting deleted so let's check
|
||||
bool deleted = false;
|
||||
for (int j=0;j<selections.size();j++) {
|
||||
const Selection& s = selections.at(j);
|
||||
if (s.track == c->track
|
||||
&& !(c->timeline_in < s.in && c->timeline_out < s.in)
|
||||
&& !(c->timeline_in > s.out && c->timeline_out > s.out)) {
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!deleted) {
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
Clip* cc = sequence->get_clip(j);
|
||||
if (cc->track == c->track
|
||||
&& cc->timeline_in > c->timeline_out
|
||||
&& cc->timeline_in < c->timeline_out + ripple_length) {
|
||||
ripple_length = cc->timeline_in - c->timeline_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ripple_length > 0) ripple(ta, ripple_point, -ripple_length);
|
||||
if (can_ripple) ta->ripple(sequence, ripple_point, -ripple_length, ignore);
|
||||
}
|
||||
|
||||
selections.clear();
|
||||
|
||||
undo_stack.push(ta);
|
||||
|
||||
redraw_all_clips(true);
|
||||
@@ -349,16 +360,7 @@ void Timeline::set_zoom(bool in) {
|
||||
redraw_all_clips(false);
|
||||
}
|
||||
|
||||
void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length) {
|
||||
// ripple all clips around the ripple_point
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (c != NULL && c->timeline_in >= ripple_point) {
|
||||
ta->increase_timeline_in(sequence, i, ripple_length);
|
||||
ta->increase_timeline_out(sequence, i, ripple_length);
|
||||
}
|
||||
}
|
||||
|
||||
/*void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length) {
|
||||
// ripple the selections
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
Selection& s = selections[i];
|
||||
@@ -368,7 +370,7 @@ void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length)
|
||||
s.out += ripple_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void Timeline::decheck_tool_buttons(QObject* sender) {
|
||||
for (int i=0;i<tool_buttons.count();i++) {
|
||||
@@ -413,7 +415,7 @@ void Timeline::on_snappingButton_toggled(bool checked) {
|
||||
Clip* Timeline::split_clip(TimelineAction* ta, int p, long frame) {
|
||||
Clip* pre = sequence->get_clip(p);
|
||||
if (pre != NULL && pre->timeline_in < frame && pre->timeline_out > frame) { // guard against attempts to split at in/out points
|
||||
Clip* post = pre->copy();
|
||||
Clip* post = pre->copy(sequence);
|
||||
|
||||
ta->set_timeline_out(sequence, p, frame);
|
||||
post->timeline_in = frame;
|
||||
@@ -545,7 +547,7 @@ void Timeline::delete_areas_and_relink(TimelineAction* ta, QVector<Selection>& a
|
||||
// middle of clip is within deletion area
|
||||
|
||||
// duplicate clip
|
||||
Clip* post = c->copy();
|
||||
Clip* post = c->copy(sequence);
|
||||
|
||||
ta->set_timeline_out(sequence, j, s.in);
|
||||
post->timeline_in = s.out;
|
||||
@@ -585,7 +587,7 @@ void Timeline::copy(bool del) {
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
Clip* copied_clip = c->copy();
|
||||
Clip* copied_clip = c->copy(NULL);
|
||||
|
||||
// copy linked IDs (we correct these later in paste())
|
||||
copied_clip->linked = c->linked;
|
||||
@@ -652,8 +654,7 @@ void Timeline::paste() {
|
||||
Clip* c = clip_clipboard.at(i);
|
||||
|
||||
// create copy of clip and offset by playhead
|
||||
Clip* cc = c->copy();
|
||||
cc->sequence = sequence;
|
||||
Clip* cc = c->copy(sequence);
|
||||
cc->timeline_in += playhead;
|
||||
cc->timeline_out += playhead;
|
||||
cc->track = c->track;
|
||||
@@ -696,6 +697,65 @@ void Timeline::paste() {
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::ripple_to_in_point(bool in) {
|
||||
if (sequence != NULL) {
|
||||
// get track count
|
||||
int track_min = 0;
|
||||
int track_max = 0;
|
||||
|
||||
bool one_frame_mode = false;
|
||||
|
||||
// find closest in point to playhead
|
||||
long in_point = in ? 0 : LONG_MAX;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
track_min = qMin(track_min, c->track);
|
||||
track_max = qMax(track_max, c->track);
|
||||
if ((in && c->timeline_in > in_point && c->timeline_in <= playhead)
|
||||
|| (!in && c->timeline_in < in_point && c->timeline_in >= playhead)) {
|
||||
in_point = c->timeline_in;
|
||||
if (playhead == in_point) {
|
||||
one_frame_mode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((in && c->timeline_out > in_point && c->timeline_out <= playhead)
|
||||
|| (!in && c->timeline_out < in_point && c->timeline_out > playhead)) {
|
||||
in_point = c->timeline_out;
|
||||
if (playhead == in_point) {
|
||||
one_frame_mode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (one_frame_mode) {
|
||||
|
||||
} else {
|
||||
// set up deletion areas based on track count
|
||||
QVector<Selection> areas;
|
||||
for (int i=track_min;i<=track_max;i++) {
|
||||
Selection s;
|
||||
s.in = qMin(in_point, playhead);
|
||||
s.out = qMax(in_point, playhead);
|
||||
s.track = i;
|
||||
areas.append(s);
|
||||
}
|
||||
|
||||
// trim and move clips around the in point
|
||||
QVector<int> ignore;
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
delete_areas_and_relink(ta, areas);
|
||||
ta->ripple(sequence, in_point, (in) ? (in_point - playhead) : (playhead - in_point), ignore);
|
||||
undo_stack.push(ta);
|
||||
|
||||
redraw_all_clips(true);
|
||||
|
||||
if (in) seek(in_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Timeline::split_selection(TimelineAction* ta) {
|
||||
bool split = false;
|
||||
|
||||
@@ -712,14 +772,14 @@ bool Timeline::split_selection(TimelineAction* ta) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (s.track == clip->track) {
|
||||
if (clip->timeline_in < s.in && clip->timeline_out > s.out) {
|
||||
Clip* split_A = clip->copy();
|
||||
Clip* split_A = clip->copy(sequence);
|
||||
split_A->clip_in += (s.in - clip->timeline_in);
|
||||
split_A->timeline_in = s.in;
|
||||
split_A->timeline_out = s.out;
|
||||
pre_splits.append(j);
|
||||
post_splits.append(split_A);
|
||||
|
||||
Clip* split_B = clip->copy();
|
||||
Clip* split_B = clip->copy(sequence);
|
||||
split_B->clip_in += (s.out - clip->timeline_in);
|
||||
split_B->timeline_in = s.out;
|
||||
secondary_post_splits.append(split_B);
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
#ifndef TIMELINE_H
|
||||
#ifndef TIMELINE_H
|
||||
#define TIMELINE_H
|
||||
|
||||
#include "ui/timelinetools.h"
|
||||
@@ -32,7 +32,7 @@ struct Ghost {
|
||||
|
||||
// importing variables
|
||||
Media* media;
|
||||
MediaStream* media_stream;
|
||||
int media_stream;
|
||||
|
||||
// other variables
|
||||
long ghost_length;
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
QVector<int> get_tracks_of_linked_clips(int i);
|
||||
bool has_clip_been_split(int c);
|
||||
void toggle_links();
|
||||
void ripple_to_in_point(bool in);
|
||||
|
||||
int get_snap_range();
|
||||
int getScreenPointFromFrame(long frame);
|
||||
@@ -169,7 +170,7 @@ public:
|
||||
bool importing;
|
||||
|
||||
// ripple
|
||||
void ripple(TimelineAction* ta, long ripple_point, long ripple_length);
|
||||
// void ripple(TimelineAction* ta, long ripple_point, long ripple_length);
|
||||
|
||||
Ui::Timeline *ui;
|
||||
public slots:
|
||||
|
||||
+11
-8
@@ -151,7 +151,8 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
|
||||
void reset_cache(Clip* c, long target_frame) {
|
||||
// if we seek to a whole other place in the timeline, we'll need to reset the cache with new values
|
||||
if (c->media_stream->infinite_length) {
|
||||
MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream);
|
||||
if (ms->infinite_length) {
|
||||
// if this clip is a still image, we only need one frame
|
||||
if (!c->cache_A.written) {
|
||||
retrieve_next_frame_raw_data(c, c->cache_A.frames[0]);
|
||||
@@ -165,7 +166,7 @@ void reset_cache(Clip* c, long target_frame) {
|
||||
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
av_seek_frame(c->formatCtx, c->media_stream->file_index, clip_frame_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_BACKWARD);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, clip_frame_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_BACKWARD);
|
||||
|
||||
// play up to the frame we actually want
|
||||
long retrieved_frame = 0;
|
||||
@@ -184,7 +185,7 @@ void reset_cache(Clip* c, long target_frame) {
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
||||
swr_drop_output(c->swr_ctx, swr_get_out_samples(c->swr_ctx, 0));
|
||||
av_seek_frame(c->formatCtx, c->media_stream->file_index, playhead_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_BACKWARD);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, playhead_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_BACKWARD);
|
||||
c->audio_target_frame = target_frame;
|
||||
c->need_new_audio_frame = true;
|
||||
c->audio_just_reset = true;
|
||||
@@ -198,6 +199,7 @@ void open_clip_worker(Clip* clip) {
|
||||
// opens file resource for FFmpeg and prepares Clip struct for playback
|
||||
QByteArray ba = clip->media->url.toUtf8();
|
||||
const char* filename = ba.constData();
|
||||
MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream);
|
||||
|
||||
int errCode = avformat_open_input(
|
||||
&clip->formatCtx,
|
||||
@@ -220,7 +222,7 @@ void open_clip_worker(Clip* clip) {
|
||||
|
||||
av_dump_format(clip->formatCtx, 0, filename, 0);
|
||||
|
||||
clip->stream = clip->formatCtx->streams[clip->media_stream->file_index];
|
||||
clip->stream = clip->formatCtx->streams[ms->file_index];
|
||||
clip->codec = avcodec_find_decoder(clip->stream->codecpar->codec_id);
|
||||
clip->codecCtx = avcodec_alloc_context3(clip->codec);
|
||||
avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar);
|
||||
@@ -263,7 +265,7 @@ void open_clip_worker(Clip* clip) {
|
||||
);
|
||||
|
||||
// create memory cache for video
|
||||
if (clip->media_stream->infinite_length) {
|
||||
if (ms->infinite_length) {
|
||||
clip->cache_size = 1;
|
||||
} else {
|
||||
clip->cache_size = ceil(av_q2d(av_guess_frame_rate(clip->formatCtx, clip->stream, NULL))/4); // cache is half a second in total
|
||||
@@ -284,7 +286,7 @@ void open_clip_worker(Clip* clip) {
|
||||
av_frame_get_buffer(clip->cache_A.frames[i], 0);
|
||||
clip->cache_A.frames[i]->linesize[0] = clip->stream->codecpar->width*4;
|
||||
|
||||
if (!clip->media_stream->infinite_length) {
|
||||
if (!ms->infinite_length) {
|
||||
clip->cache_B.frames[i] = av_frame_alloc();
|
||||
av_frame_make_writable(clip->cache_B.frames[i]);
|
||||
clip->cache_B.frames[i]->width = clip->stream->codecpar->width;
|
||||
@@ -359,6 +361,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo
|
||||
|
||||
void close_clip_worker(Clip* clip) {
|
||||
// closes ffmpeg file handle and frees any memory used for caching
|
||||
MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream);
|
||||
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
sws_freeContext(clip->sws_ctx);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
@@ -371,10 +374,10 @@ void close_clip_worker(Clip* clip) {
|
||||
|
||||
for (size_t i=0;i<clip->cache_size;i++) {
|
||||
av_frame_free(&clip->cache_A.frames[i]);
|
||||
if (!clip->media_stream->infinite_length) av_frame_free(&clip->cache_B.frames[i]);
|
||||
if (!ms->infinite_length) av_frame_free(&clip->cache_B.frames[i]);
|
||||
}
|
||||
delete [] clip->cache_A.frames;
|
||||
if (!clip->media_stream->infinite_length) delete [] clip->cache_B.frames;
|
||||
if (!ms->infinite_length) delete [] clip->cache_B.frames;
|
||||
|
||||
av_frame_free(&clip->frame);
|
||||
|
||||
|
||||
@@ -77,12 +77,13 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
long clip_time = seconds_to_clip_frame(c, playhead_to_seconds(c, playhead));
|
||||
|
||||
// do we need to update the texture?
|
||||
if ((!c->media_stream->infinite_length && c->texture_frame != clip_time) ||
|
||||
(c->media_stream->infinite_length && c->texture_frame == -1)) {
|
||||
MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream);
|
||||
if ((!ms->infinite_length && c->texture_frame != clip_time) ||
|
||||
(ms->infinite_length && c->texture_frame == -1)) {
|
||||
AVFrame* current_frame = NULL;
|
||||
|
||||
// get frame data
|
||||
if (c->media_stream->infinite_length) { // if clip is a still frame, we only need one
|
||||
if (ms->infinite_length) { // if clip is a still frame, we only need one
|
||||
if (c->cache_A.written) {
|
||||
// retrieve cached frame
|
||||
current_frame = c->cache_A.frames[0];
|
||||
@@ -156,7 +157,7 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
// set up opengl texture
|
||||
if (c->texture == NULL) {
|
||||
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
c->texture->setSize(c->media_stream->video_width, c->media_stream->video_height);
|
||||
c->texture->setSize(ms->video_width, ms->video_height);
|
||||
c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||
c->texture->setMipLevels(c->texture->maximumMipLevels());
|
||||
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
@@ -206,7 +207,7 @@ int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||
}
|
||||
read_ret = av_read_frame(c->formatCtx, c->pkt);
|
||||
c->pkt_written = true;
|
||||
} while (read_ret >= 0 && c->pkt->stream_index != c->media_stream->file_index);
|
||||
} while (read_ret >= 0 && c->pkt->stream_index != c->media_stream);
|
||||
|
||||
if (read_ret >= 0) {
|
||||
int send_ret = avcodec_send_packet(c->codecCtx, c->pkt);
|
||||
|
||||
+4
-4
@@ -12,7 +12,7 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
Clip::Clip() {
|
||||
Clip::Clip(Sequence* s) : sequence(s) {
|
||||
reset();
|
||||
enabled = true;
|
||||
clip_in = 0;
|
||||
@@ -27,8 +27,8 @@ Clip::Clip() {
|
||||
pkt = new AVPacket();
|
||||
}
|
||||
|
||||
Clip* Clip::copy() {
|
||||
Clip* copy = new Clip();
|
||||
Clip* Clip::copy(Sequence* s) {
|
||||
Clip* copy = new Clip(s);
|
||||
|
||||
copy->enabled = enabled;
|
||||
copy->name = QString(name);
|
||||
@@ -39,7 +39,7 @@ Clip* Clip::copy() {
|
||||
copy->color_r = color_r;
|
||||
copy->color_g = color_g;
|
||||
copy->color_b = color_b;
|
||||
copy->sequence = sequence;
|
||||
copy->sequence = s;
|
||||
copy->media = media;
|
||||
copy->media_stream = media_stream;
|
||||
|
||||
|
||||
+3
-3
@@ -36,9 +36,9 @@ struct ClipCache {
|
||||
|
||||
struct Clip
|
||||
{
|
||||
Clip();
|
||||
Clip(Sequence* s);
|
||||
~Clip();
|
||||
Clip* copy();
|
||||
Clip* copy(Sequence* s);
|
||||
void reset();
|
||||
bool undeletable;
|
||||
|
||||
@@ -57,7 +57,7 @@ struct Clip
|
||||
quint8 color_b;
|
||||
long getLength();
|
||||
Media* media; // attached media
|
||||
MediaStream* media_stream;
|
||||
int media_stream;
|
||||
Sequence* sequence;
|
||||
|
||||
// other variables (should be "duplicated" in copy())
|
||||
|
||||
@@ -25,7 +25,7 @@ Sequence* Sequence::copy() {
|
||||
for (int i=0;i<clip_count();i++) {
|
||||
Clip* c = get_clip(i);
|
||||
if (c != NULL) {
|
||||
Clip* copy = c->copy();
|
||||
Clip* copy = c->copy(s);
|
||||
copy->linked = c->linked;
|
||||
s->add_clip(copy);
|
||||
}
|
||||
|
||||
+61
-19
@@ -24,7 +24,7 @@ QUndoStack undo_stack;
|
||||
#define TA_ADD_CLIP_IN 7
|
||||
#define TA_ADD_TRACK 8
|
||||
|
||||
TimelineAction::TimelineAction() : done(false), change_seq(false) {}
|
||||
TimelineAction::TimelineAction() : done(false), change_seq(false), ripple_enabled(false) {}
|
||||
|
||||
TimelineAction::~TimelineAction() {
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
@@ -40,7 +40,7 @@ TimelineAction::~TimelineAction() {
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<new_sequence_items.size();i++) {
|
||||
delete panel_project->get_sequence_from_tree(new_sequence_items.at(i));
|
||||
delete get_sequence_from_tree(new_sequence_items.at(i));
|
||||
delete new_sequence_items.at(i);
|
||||
}
|
||||
for (int i=0;i<media_to_add.size();i++) {
|
||||
@@ -129,7 +129,16 @@ void TimelineAction::delete_media(QTreeWidgetItem* item) {
|
||||
deleted_media.append(item);
|
||||
}
|
||||
|
||||
void TimelineAction::ripple(Sequence* s, long point, long length, QVector<int>& ignore) {
|
||||
ripple_enabled = true;
|
||||
ripple_point = point;
|
||||
ripple_length = length;
|
||||
ripple_sequence = s;
|
||||
ripple_ignores = ignore;
|
||||
}
|
||||
|
||||
void TimelineAction::undo() {
|
||||
// re-add deleted media
|
||||
for (int i=0;i<deleted_media.size();i++) {
|
||||
QTreeWidgetItem* item = deleted_media.at(i);
|
||||
QTreeWidgetItem* parent = deleted_media_parents.at(i);
|
||||
@@ -140,7 +149,16 @@ void TimelineAction::undo() {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
// de-ripple clips
|
||||
if (ripple_enabled) {
|
||||
for (int i=0;i<rippled_clips.size();i++) {
|
||||
Clip* c = ripple_sequence->get_clip(rippled_clips.at(i));
|
||||
c->timeline_in -= ripple_length;
|
||||
c->timeline_out -= ripple_length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=actions.size()-1;i>=0;i--) {
|
||||
switch (actions.at(i)) {
|
||||
case TA_IN:
|
||||
sequences.at(i)->get_clip(clips.at(i))->timeline_in = old_values.at(i);
|
||||
@@ -172,7 +190,7 @@ void TimelineAction::undo() {
|
||||
}
|
||||
}
|
||||
|
||||
// add new sequences
|
||||
// remove added sequences
|
||||
for (int i=0;i<new_sequence_items.size();i++) {
|
||||
if (new_sequence_parents.at(i) == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(new_sequence_items.at(i)));
|
||||
@@ -218,6 +236,7 @@ void TimelineAction::redo() {
|
||||
added_indexes.clear();
|
||||
deleted_media_parents.clear();
|
||||
removed_link_from_sequence.clear();
|
||||
rippled_clips.clear();
|
||||
|
||||
for (int i=0;i<new_sequence_items.size();i++) {
|
||||
if (new_sequence_parents.at(i) == NULL) {
|
||||
@@ -227,6 +246,23 @@ void TimelineAction::redo() {
|
||||
}
|
||||
}
|
||||
|
||||
// add any new clips
|
||||
if (clips_to_add.size() > 0) {
|
||||
QVector<Clip*> copies;
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
Clip* original = clips_to_add.at(i);
|
||||
Clip* copy = original->copy(sequence_to_add_clips_to.at(i));
|
||||
copy->linked.resize(original->linked.size());
|
||||
for (int j=0;j<original->linked.size();j++) {
|
||||
copy->linked[j] = original->linked.at(j) + sequence_to_add_clips_to.at(i)->clip_count();
|
||||
}
|
||||
copies.append(copy);
|
||||
}
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
added_indexes.append(sequence_to_add_clips_to.at(i)->add_clip(copies.at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
switch (actions.at(i)) {
|
||||
case TA_IN:
|
||||
@@ -300,20 +336,25 @@ void TimelineAction::redo() {
|
||||
}
|
||||
}
|
||||
|
||||
// add any new clips
|
||||
if (clips_to_add.size() > 0) {
|
||||
QVector<Clip*> copies;
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
Clip* original = clips_to_add.at(i);
|
||||
Clip* copy = original->copy();
|
||||
copy->linked.resize(original->linked.size());
|
||||
for (int j=0;j<original->linked.size();j++) {
|
||||
copy->linked[j] = original->linked.at(j) + sequence_to_add_clips_to.at(i)->clip_count();
|
||||
// ripple clips - moves all clips after a certain ripple_point by ripple_length
|
||||
if (ripple_enabled) {
|
||||
for (int j=0;j<ripple_sequence->clip_count();j++) {
|
||||
bool found = false;
|
||||
for (int i=0;i<ripple_ignores.size();i++) {
|
||||
if (ripple_ignores.at(i) == j) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Clip* c = ripple_sequence->get_clip(j);
|
||||
if (c != NULL && c->timeline_in >= ripple_point) {
|
||||
c->timeline_in += ripple_length;
|
||||
c->timeline_out += ripple_length;
|
||||
rippled_clips.append(j);
|
||||
}
|
||||
}
|
||||
copies.append(copy);
|
||||
}
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
added_indexes.append(sequence_to_add_clips_to.at(i)->add_clip(copies.at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,8 +365,8 @@ void TimelineAction::redo() {
|
||||
deleted_media_parents.append(item->parent());
|
||||
|
||||
// if we're deleting the open sequence, close it
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE &&
|
||||
panel_project->get_sequence_from_tree(item) == sequence &&
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE &&
|
||||
get_sequence_from_tree(item) == sequence &&
|
||||
!change_seq) {
|
||||
old_seq = sequence;
|
||||
new_seq = NULL;
|
||||
@@ -347,6 +388,7 @@ void TimelineAction::redo() {
|
||||
}
|
||||
}
|
||||
|
||||
// change current sequence
|
||||
if (change_seq) {
|
||||
old_seq = sequence;
|
||||
set_sequence(new_seq);
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
void delete_clip(Sequence* s, int clip);
|
||||
void add_media(QTreeWidgetItem* item);
|
||||
void delete_media(QTreeWidgetItem* item);
|
||||
void ripple(Sequence* s, long point, long length, QVector<int> &ignore);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
@@ -65,6 +66,13 @@ private:
|
||||
|
||||
QVector<QTreeWidgetItem*> media_to_add;
|
||||
|
||||
Sequence* ripple_sequence;
|
||||
bool ripple_enabled;
|
||||
long ripple_point;
|
||||
long ripple_length;
|
||||
QVector<int> rippled_clips;
|
||||
QVector<int> ripple_ignores;
|
||||
|
||||
void new_action(Sequence* s, int action, int clip, long old_val, long new_val);
|
||||
void offset_links(QVector<Clip*>& clips, int offset);
|
||||
};
|
||||
|
||||
+42
-3
@@ -11,15 +11,54 @@
|
||||
#include <QDragEnterEvent>
|
||||
#include <QMimeData>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QDebug>
|
||||
|
||||
SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) {
|
||||
editing_item = NULL;
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
rename_timer.setInterval(500);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
|
||||
connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(item_click(QTreeWidgetItem*,int)));
|
||||
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(item_renamed(QTreeWidgetItem*)));
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&)));
|
||||
}
|
||||
|
||||
void SourceTable::show_context_menu(const QPoint& pos) {
|
||||
QMenu menu(this);
|
||||
|
||||
if (selectedItems().size() == 0) {
|
||||
QAction* import_action = menu.addAction("Import...");
|
||||
connect(import_action, SIGNAL(triggered(bool)), panel_project, SLOT(import_dialog()));
|
||||
} else {
|
||||
if (selectedItems().size() == 1) {
|
||||
// replace footage
|
||||
if (get_type_from_tree(selectedItems().at(0)) == MEDIA_TYPE_FOOTAGE) {
|
||||
menu.addAction("Replace/Relink Media");
|
||||
}
|
||||
menu.addAction("Replace Clips Using This Media");
|
||||
}
|
||||
|
||||
// duplicate item
|
||||
bool all_sequences = true;
|
||||
for (int i=0;i<selectedItems().size();i++) {
|
||||
if (get_type_from_tree(selectedItems().at(i)) != MEDIA_TYPE_SEQUENCE) {
|
||||
all_sequences = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_sequences) {
|
||||
QAction* duplicate_action = menu.addAction("Duplicate");
|
||||
connect(duplicate_action, SIGNAL(triggered(bool)), panel_project, SLOT(duplicate_selected()));
|
||||
}
|
||||
|
||||
// delete media
|
||||
QAction* delete_action = menu.addAction("Delete");
|
||||
connect(delete_action, SIGNAL(triggered(bool)), panel_project, SLOT(delete_selected_media()));
|
||||
}
|
||||
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SourceTable::item_renamed(QTreeWidgetItem* item) {
|
||||
@@ -62,9 +101,9 @@ void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) {
|
||||
panel_project->import_dialog();
|
||||
} else if (selectedItems().count() == 1) {
|
||||
QTreeWidgetItem* item = selectedItems().at(0);
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) {
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) {
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
ta->change_sequence(panel_project->get_sequence_from_tree(item));
|
||||
ta->change_sequence(get_sequence_from_tree(item));
|
||||
undo_stack.push(ta);
|
||||
}
|
||||
}
|
||||
@@ -105,7 +144,7 @@ void SourceTable::dropEvent(QDropEvent* event) {
|
||||
QVector<QTreeWidgetItem*> move_items;
|
||||
QTreeWidgetItem* drop_item = itemAt(event->pos());
|
||||
// if we dragged to the root OR dragged to a folder
|
||||
if (drop_item == NULL || (drop_item != NULL && panel_project->get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER)) {
|
||||
if (drop_item == NULL || (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER)) {
|
||||
QList<QTreeWidgetItem*> selected_items = selectedItems();
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
QTreeWidgetItem* s = selected_items.at(i);
|
||||
|
||||
@@ -27,6 +27,7 @@ private slots:
|
||||
void item_click(QTreeWidgetItem* item, int column);
|
||||
void stop_rename_timer();
|
||||
void item_renamed(QTreeWidgetItem *item);
|
||||
void show_context_menu(const QPoint& pos);
|
||||
};
|
||||
|
||||
class MediaMove : public QUndoCommand {
|
||||
|
||||
+72
-46
@@ -74,8 +74,8 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
bool got_audio_values = false;
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* item = items.at(i);
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = panel_project->get_media_from_tree(item);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
if (m->ready) {
|
||||
if (!got_video_values) {
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
@@ -111,8 +111,8 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
}
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* item = items.at(i);
|
||||
if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = panel_project->get_media_from_tree(item);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
if (m->ready) {
|
||||
Ghost g;
|
||||
g.clip = -1;
|
||||
@@ -128,12 +128,12 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
g.trimming = false;
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j);
|
||||
g.media_stream = m->audio_tracks.at(j)->file_index;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j);
|
||||
g.media_stream = m->video_tracks.at(j)->file_index;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
}
|
||||
@@ -191,11 +191,11 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
QVector<Selection> delete_areas;
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
Selection s;
|
||||
s.in = g.in;
|
||||
s.out = g.out;
|
||||
s.track = g.track;
|
||||
delete_areas.append(s);
|
||||
Selection sel;
|
||||
sel.in = g.in;
|
||||
sel.out = g.out;
|
||||
sel.track = g.track;
|
||||
delete_areas.append(sel);
|
||||
}
|
||||
panel_timeline->delete_areas_and_relink(ta, delete_areas);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
|
||||
Clip* c = new Clip();
|
||||
Clip* c = new Clip(s);
|
||||
c->media = g.media;
|
||||
c->media_stream = g.media_stream;
|
||||
c->timeline_in = g.in;
|
||||
@@ -216,9 +216,9 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
c->color_g = 192;
|
||||
c->color_b = 128;
|
||||
} else if (c->media->audio_tracks.size() == 0) {
|
||||
// video only (purpleish)
|
||||
// video only (orangeish)
|
||||
c->color_r = 192;
|
||||
c->color_g = 128;
|
||||
c->color_g = 160;
|
||||
c->color_b = 128;
|
||||
} else {
|
||||
// video and audio (blueish)
|
||||
@@ -226,13 +226,11 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
c->color_g = 128;
|
||||
c->color_b = 192;
|
||||
}
|
||||
c->sequence = sequence;
|
||||
c->track = g.track;
|
||||
c->name = c->media->name;
|
||||
|
||||
if (c->track < 0) {
|
||||
// add default video effects
|
||||
qDebug() << "hello";
|
||||
// add default video effects
|
||||
c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c));
|
||||
} else {
|
||||
// add default audio effects
|
||||
@@ -412,18 +410,22 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
ripple_length = first_ghost.old_out - panel_timeline->ghosts.at(0).out;
|
||||
ripple_point = first_ghost.old_out;
|
||||
}
|
||||
QVector<int> ignore_clips;
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
long comp_point;
|
||||
|
||||
// push rippled clips forward if necessary
|
||||
if (panel_timeline->trim_in_point) {
|
||||
comp_point = g.old_in;
|
||||
} else {
|
||||
comp_point = g.old_out;
|
||||
ignore_clips.append(g.clip);
|
||||
panel_timeline->ghosts[i].in += ripple_length;
|
||||
panel_timeline->ghosts[i].out += ripple_length;
|
||||
}
|
||||
if (ripple_point > comp_point) ripple_point = comp_point;
|
||||
|
||||
long comp_point = panel_timeline->trim_in_point ? g.old_in : g.old_out;
|
||||
ripple_point = qMin(ripple_point, comp_point);
|
||||
}
|
||||
if (!panel_timeline->trim_in_point) ripple_length = -ripple_length;
|
||||
panel_timeline->ripple(ta, ripple_point, ripple_length);
|
||||
ta->ripple(sequence, ripple_point, ripple_length, ignore_clips);
|
||||
}
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_POINTER && (event->modifiers() & Qt::AltModifier)) { // if holding alt, duplicate rather than move
|
||||
@@ -435,7 +437,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
if (g.old_in != g.in || g.old_out != g.out || g.track != g.old_track || g.clip_in != g.old_clip_in) {
|
||||
// create copy of clip
|
||||
Clip* c = sequence->get_clip(g.clip)->copy();
|
||||
Clip* c = sequence->get_clip(g.clip)->copy(sequence);
|
||||
|
||||
c->timeline_in = g.in;
|
||||
c->timeline_out = g.out;
|
||||
@@ -674,7 +676,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
|
||||
// validate ghosts for trimming
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_SLIP) {
|
||||
if (!sequence->get_clip(g.clip)->media_stream->infinite_length) {
|
||||
if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) {
|
||||
// prevent slip moving a clip below 0 clip_in
|
||||
validator = g.old_clip_in - frame_diff;
|
||||
if (validator < 0) frame_diff += validator;
|
||||
@@ -694,7 +696,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
|
||||
// prevent clip_in from going below 0
|
||||
if (!c->media_stream->infinite_length) {
|
||||
if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) {
|
||||
validator = g.old_clip_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
}
|
||||
@@ -704,7 +706,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
if (validator < 1) frame_diff += (1 - validator);
|
||||
|
||||
// prevent clip length exceeding media length
|
||||
if (!c->media_stream->infinite_length) {
|
||||
if (!c->media->get_stream_from_file_index(c->media_stream)->infinite_length) {
|
||||
validator = g.old_clip_in + g.ghost_length + frame_diff;
|
||||
if (validator > g.media_length) frame_diff -= validator - g.media_length;
|
||||
}
|
||||
@@ -713,10 +715,13 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
// ripple ops
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
for (int j=0;j<post_clips.size();j++) {
|
||||
// prevent any rippled clip from going below 0
|
||||
Clip* post = post_clips.at(j);
|
||||
validator = post->timeline_in - frame_diff;
|
||||
if (validator < 0) frame_diff += validator;
|
||||
|
||||
// prevent any rippled clip from going below 0
|
||||
if (panel_timeline->trim_in_point) {
|
||||
validator = post->timeline_in - frame_diff;
|
||||
if (validator < 0) frame_diff += validator;
|
||||
}
|
||||
|
||||
// prevent any post-clips colliding with pre-clips
|
||||
for (int k=0;k<pre_clips.size();k++) {
|
||||
@@ -750,9 +755,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (temp_frame_diff != frame_diff) {
|
||||
panel_timeline->snapped = false;
|
||||
}
|
||||
panel_timeline->snapped = (temp_frame_diff == frame_diff);
|
||||
|
||||
// apply changes to ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
@@ -1303,37 +1306,60 @@ void TimelineWidget::redraw_clips() {
|
||||
}
|
||||
}
|
||||
|
||||
// draw thumbnail/waveform
|
||||
if (clip->media_stream->preview_done) {
|
||||
MediaStream* ms = clip->media->get_stream_from_file_index(clip->media_stream);
|
||||
if (ms == NULL) {
|
||||
// draw "error lines" if media stream is missing
|
||||
clip_painter.setPen(QPen(QColor(64, 64, 64), 2));
|
||||
int limit = clip_rect.width();
|
||||
int clip_height = clip_rect.bottom()-clip_rect.top();
|
||||
for (int j=-clip_height;j<limit;j+=15) {
|
||||
int lines_start_x = clip_rect.left()+j;
|
||||
int lines_start_y = clip_rect.bottom();
|
||||
int lines_end_x = lines_start_x + clip_height;
|
||||
int lines_end_y = clip_rect.top();
|
||||
if (lines_start_x < clip_rect.left()) {
|
||||
lines_start_y -= (clip_rect.left() - lines_start_x);
|
||||
lines_start_x = clip_rect.left();
|
||||
}
|
||||
if (lines_end_x > clip_rect.right()) {
|
||||
lines_end_y -= (clip_rect.right() - lines_end_x);
|
||||
lines_end_x = clip_rect.right();
|
||||
}
|
||||
clip_painter.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y);
|
||||
}
|
||||
} else if (ms->preview_done) {
|
||||
// draw thumbnail/waveform
|
||||
if (clip->track < 0) {
|
||||
int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING;
|
||||
int thumb_height = clip_rect.height()-thumb_y;
|
||||
int thumb_width = (thumb_height*((float)clip->media_stream->video_preview.width()/(float)clip->media_stream->video_preview.height()));
|
||||
int thumb_width = (thumb_height*((double)ms->video_preview.width()/(double)ms->video_preview.height()));
|
||||
if (thumb_height > thumb_y && text_rect.width() + CLIP_TEXT_PADDING > thumb_width) { // at small clip heights, don't even draw it
|
||||
QRect thumb_rect(thumb_x, clip_rect.y()+thumb_y, thumb_width, thumb_height);
|
||||
clip_painter.drawImage(thumb_rect, clip->media_stream->video_preview);
|
||||
clip_painter.drawImage(thumb_rect, ms->video_preview);
|
||||
}
|
||||
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
|
||||
int divider = clip->media_stream->audio_channels*2;
|
||||
int divider = ms->audio_channels*2;
|
||||
|
||||
clip_painter.setPen(QColor(80, 80, 80));
|
||||
int channel_height = clip_rect.height()/clip->media_stream->audio_channels;
|
||||
int channel_height = clip_rect.height()/ms->audio_channels;
|
||||
long media_length = clip->media->get_length_in_frames(clip->sequence->frame_rate);
|
||||
for (int i=0;i<clip_rect.width();i++) {
|
||||
int waveform_index = qFloor((((clip->clip_in + ((float) i/panel_timeline->zoom))/media_length) * clip->media_stream->audio_preview.size())/divider)*divider;
|
||||
int waveform_index = qFloor((((clip->clip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
|
||||
|
||||
for (int j=0;j<clip->media_stream->audio_channels;j++) {
|
||||
for (int j=0;j<ms->audio_channels;j++) {
|
||||
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
|
||||
if (offset >= 0 && offset < clip->media_stream->audio_preview.size()) {
|
||||
qint8 min = (double)clip->media_stream->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)clip->media_stream->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
|
||||
/*if (offset >= 0 && offset < ms->audio_preview.size()) {
|
||||
|
||||
} else {
|
||||
QMessageBox::critical(this, "AAAAAAAAAAAAA", "Here's that terrible, no-good bug again!!! I've stepped around it but plz report the following to Matt:\n\n" + QString::number(offset) + " vs " + QString::number(clip->media_stream->audio_preview.size()), QMessageBox::Ok);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -16,8 +16,7 @@ struct Clip;
|
||||
class Timeline;
|
||||
class TimelineAction;
|
||||
|
||||
class TimelineWidget : public QWidget
|
||||
{
|
||||
class TimelineWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TimelineWidget(QWidget *parent = 0);
|
||||
|
||||
+9
-5
@@ -72,7 +72,9 @@ void ViewerWidget::paintGL() {
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != NULL) {
|
||||
if (c->media->ready && is_clip_active(c, panel_timeline->playhead)) {
|
||||
if (c->media->ready
|
||||
&& c->media->get_stream_from_file_index(c->media_stream) != NULL
|
||||
&& is_clip_active(c, panel_timeline->playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
@@ -113,6 +115,8 @@ void ViewerWidget::paintGL() {
|
||||
qDebug() << "[WARNING] Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (panel_timeline->playhead >= c->timeline_in) {
|
||||
MediaStream* ms = c->media->get_stream_from_file_index(c->media_stream);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
glLoadIdentity();
|
||||
@@ -124,8 +128,8 @@ void ViewerWidget::paintGL() {
|
||||
} else {
|
||||
glOrtho(-half_width, half_width, half_height, -half_height, -1, 1);
|
||||
}
|
||||
int anchor_x = c->media_stream->video_width/2;
|
||||
int anchor_y = c->media_stream->video_height/2;
|
||||
int anchor_x = ms->video_width/2;
|
||||
int anchor_y = ms->video_height/2;
|
||||
|
||||
// perform all transform effects
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
@@ -145,8 +149,8 @@ void ViewerWidget::paintGL() {
|
||||
}
|
||||
}
|
||||
|
||||
int anchor_right = c->media_stream->video_width - anchor_x;
|
||||
int anchor_bottom = c->media_stream->video_height - anchor_y;
|
||||
int anchor_right = ms->video_width - anchor_x;
|
||||
int anchor_bottom = ms->video_height - anchor_y;
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user