extremely limited speed support implemented #120
This commit is contained in:
+81
-14
@@ -6,36 +6,103 @@
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "io/media.h"
|
||||
#include "playback/playback.h"
|
||||
|
||||
SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) {
|
||||
QGridLayout* layout = new QGridLayout();
|
||||
layout->setSpacing(6);
|
||||
layout->setMargin(9);
|
||||
QVBoxLayout* main_layout = new QVBoxLayout();
|
||||
setLayout(main_layout);
|
||||
|
||||
setLayout(layout);
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setSpacing(6);
|
||||
|
||||
layout->addWidget(new QLabel("Speed:"), 0, 0);
|
||||
grid->addWidget(new QLabel("Speed:"), 0, 0);
|
||||
percent = new LabelSlider();
|
||||
percent->decimal_places = 2;
|
||||
percent->set_display_type(LABELSLIDER_PERCENT);
|
||||
layout->addWidget(percent, 0, 1);
|
||||
grid->addWidget(percent, 0, 1);
|
||||
|
||||
layout->addWidget(new QLabel("Frame Rate:"), 1, 0);
|
||||
grid->addWidget(new QLabel("Frame Rate:"), 1, 0);
|
||||
frame_rate = new LabelSlider();
|
||||
layout->addWidget(frame_rate, 1, 1);
|
||||
frame_rate->decimal_places = 3;
|
||||
grid->addWidget(frame_rate, 1, 1);
|
||||
|
||||
layout->addWidget(new QLabel("Duration:"), 2, 0);
|
||||
grid->addWidget(new QLabel("Duration:"), 2, 0);
|
||||
duration = new LabelSlider();
|
||||
duration->set_display_type(LABELSLIDER_FRAMENUMBER);
|
||||
layout->addWidget(duration, 2, 1);
|
||||
grid->addWidget(duration, 2, 1);
|
||||
|
||||
main_layout->addLayout(grid);
|
||||
|
||||
QHBoxLayout* reverseLayout = new QHBoxLayout();
|
||||
reverse = new QCheckBox("Reverse");
|
||||
reverseLayout->addWidget(reverse);
|
||||
layout->addLayout(reverseLayout, 3, 0, 2, 1);
|
||||
main_layout->addWidget(reverse);
|
||||
main_layout->addWidget(new QCheckBox("Maintain Audio Pitch"));
|
||||
main_layout->addWidget(new QCheckBox("Ripple"));
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttonBox, 4, 0, 2, 1);
|
||||
buttonBox->setCenterButtons(true);
|
||||
main_layout->addWidget(buttonBox);
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
||||
connect(percent, SIGNAL(valueChanged()), this, SLOT(percent_update()));
|
||||
connect(frame_rate, SIGNAL(valueChanged()), this, SLOT(frame_rate_update()));
|
||||
connect(duration, SIGNAL(valueChanged()), this, SLOT(duration_update()));
|
||||
}
|
||||
|
||||
void SpeedDialog::run() {
|
||||
if (clips.size() > 1) {
|
||||
qDebug() << "[WARNING] Setting multiple clip speeds at once is unsupported and may break things - sorry!";
|
||||
}
|
||||
|
||||
normal_fr = 1;
|
||||
|
||||
Clip* first_clip = clips.at(0);
|
||||
|
||||
switch (first_clip->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
normal_fr = static_cast<Media*>(first_clip->media)->get_stream_from_file_index(true, first_clip->media_stream)->video_frame_rate;
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
normal_fr = static_cast<Sequence*>(first_clip->media)->frame_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
frame_rate->set_default_value(first_clip->frame_rate);
|
||||
percent->set_default_value((first_clip->frame_rate / normal_fr) * 100);
|
||||
duration->set_default_value(first_clip->getLength());
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void SpeedDialog::percent_update() {
|
||||
double pc = (percent->value()*0.01);
|
||||
frame_rate->set_value(normal_fr * pc, false);
|
||||
duration->set_value(clips.at(0)->getLength() / pc, false);
|
||||
}
|
||||
|
||||
void SpeedDialog::duration_update() {
|
||||
double pc = clips.at(0)->getLength() / duration->value();
|
||||
frame_rate->set_value(normal_fr * pc, false);
|
||||
percent->set_value(pc * 100, false);
|
||||
}
|
||||
|
||||
void SpeedDialog::frame_rate_update() {
|
||||
double fr = (frame_rate->value());
|
||||
double pc = (fr / normal_fr);
|
||||
percent->set_value(pc * 100, false);
|
||||
duration->set_value(clips.at(0)->getLength() / pc, false);
|
||||
}
|
||||
|
||||
void SpeedDialog::accept() {
|
||||
for (int i=0;i<clips.size();i++) {
|
||||
clips.at(i)->frame_rate = frame_rate->value();
|
||||
close_clip(clips.at(i));
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
@@ -9,15 +9,25 @@ class QCheckBox;
|
||||
|
||||
class SpeedDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpeedDialog(QWidget* parent = 0);
|
||||
QVector<Clip*> clips;
|
||||
|
||||
void run();
|
||||
private slots:
|
||||
void percent_update();
|
||||
void duration_update();
|
||||
void frame_rate_update();
|
||||
void accept();
|
||||
private:
|
||||
LabelSlider* percent;
|
||||
LabelSlider* duration;
|
||||
LabelSlider* frame_rate;
|
||||
|
||||
QCheckBox* reverse;
|
||||
|
||||
double normal_fr;
|
||||
};
|
||||
|
||||
#endif // SPEEDDIALOG_H
|
||||
|
||||
@@ -46,12 +46,3 @@ MediaStream* Media::get_stream_from_file_index(bool video, int index) {
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int guess_layout_from_channels(int channel_count) {
|
||||
if (channel_count == 1) {
|
||||
return AV_CH_LAYOUT_MONO;
|
||||
} else {
|
||||
qDebug() << "[WARNING] Could not detect audio channel layout - assuming stereo";
|
||||
return AV_CH_LAYOUT_STEREO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,4 @@ struct Media {
|
||||
void reset();
|
||||
};
|
||||
|
||||
int guess_layout_from_channels(int channel_count);
|
||||
|
||||
#endif // MEDIA_H
|
||||
|
||||
@@ -110,7 +110,7 @@ void PreviewGenerator::generate_waveform() {
|
||||
avcodec_open2(codec_ctx[i], codec, NULL);
|
||||
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) {
|
||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||
codec_ctx[i]->channel_layout = av_get_default_channel_layout(fmt_ctx->streams[i]->codecpar->channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -300,7 +300,7 @@ void MainWindow::openSpeedDialog() {
|
||||
s.clips.append(c);
|
||||
}
|
||||
}
|
||||
if (s.clips.size() > 0) s.exec();
|
||||
if (s.clips.size() > 0) s.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-41
@@ -685,46 +685,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
} else if (attr.name() == "duration") {
|
||||
m->length = attr.value().toLongLong();
|
||||
}
|
||||
}
|
||||
|
||||
/*while (!(stream.name() == child_search && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
if (stream.isStartElement()) {
|
||||
if (stream.name() == "video") {
|
||||
MediaStream* ms = new MediaStream();
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
if (attr.name() == "id") {
|
||||
ms->file_index = attr.value().toInt();
|
||||
} else if (attr.name() == "width") {
|
||||
ms->video_width = attr.value().toInt();
|
||||
} else if (attr.name() == "height") {
|
||||
ms->video_height = attr.value().toInt();
|
||||
} else if (attr.name() == "framerate") {
|
||||
ms->video_frame_rate = attr.value().toDouble();
|
||||
} else if (attr.name() == "infinite") {
|
||||
ms->infinite_length = (attr.value().toInt() == 1);
|
||||
}
|
||||
}
|
||||
m->video_tracks.append(ms);
|
||||
} else if (stream.name() == "audio") {
|
||||
MediaStream* ms = new MediaStream();
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
if (attr.name() == "id") {
|
||||
ms->file_index = attr.value().toInt();
|
||||
} else if (attr.name() == "channels") {
|
||||
ms->audio_channels = attr.value().toInt();
|
||||
} else if (attr.name() == "layout") {
|
||||
ms->audio_layout = attr.value().toInt();
|
||||
} else if (attr.name() == "frequency") {
|
||||
ms->audio_frequency = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
m->audio_tracks.append(ms);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
set_footage_of_tree(item, m);
|
||||
|
||||
@@ -794,7 +755,8 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
Clip* c = new Clip(s);
|
||||
|
||||
// backwards compatibility code
|
||||
c->autoscale = false;
|
||||
c->autoscale = false;
|
||||
c->frame_rate = 0;
|
||||
|
||||
c->media = NULL;
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
@@ -826,6 +788,8 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
media_id = attr.value().toInt();
|
||||
} else if (attr.name() == "stream") {
|
||||
stream_id = attr.value().toInt();
|
||||
} else if (attr.name() == "framerate") {
|
||||
c->frame_rate = attr.value().toDouble();
|
||||
} else if (attr.name() == "sequence") {
|
||||
c->media_type = MEDIA_TYPE_SEQUENCE;
|
||||
|
||||
@@ -1125,6 +1089,8 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
|
||||
stream.writeAttribute("autoscale", QString::number(c->autoscale));
|
||||
|
||||
stream.writeAttribute("framerate", QString::number(c->frame_rate));
|
||||
|
||||
stream.writeAttribute("type", QString::number(c->media_type));
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
|
||||
+21
-16
@@ -170,7 +170,6 @@ void cache_audio_worker(Clip* c, Clip* nest) {
|
||||
} else {
|
||||
long buffer_timeline_out = get_buffer_offset_from_frame(timeline_out);
|
||||
audio_write_lock.lock();
|
||||
qDebug() << "starting to write:" << audio_ibuffer_read;
|
||||
while (c->frame_sample_index < nb_bytes
|
||||
&& c->audio_buffer_write < audio_ibuffer_read+audio_ibuffer_size
|
||||
&& c->audio_buffer_write < buffer_timeline_out) {
|
||||
@@ -207,6 +206,8 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
|
||||
int i = 0;
|
||||
|
||||
double fr_ratio = qRound((c->sequence->frame_rate / c->frame_rate)*100)*0.01;
|
||||
|
||||
/* old swscale solution
|
||||
if (!c->reached_end) {
|
||||
while (i < c->cache_size) {
|
||||
@@ -227,11 +228,19 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR(EAGAIN)) {
|
||||
ret = retrieve_next_frame(c, c->frame);
|
||||
|
||||
if (ret >= 0) {
|
||||
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
|
||||
qDebug() << "[ERROR] Could not feed filtergraph -" << ret;
|
||||
error = true;
|
||||
break;
|
||||
// optimization: mathematically determine based on the sequence and clips' frame rates whether this frame will actually be shown
|
||||
double timeline_frame = (i+cache->offset) * fr_ratio;
|
||||
|
||||
if (qFloor(timeline_frame) == timeline_frame) {
|
||||
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
|
||||
qDebug() << "[ERROR] Could not feed filtergraph -" << ret;
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
if (ret == AVERROR_EOF) {
|
||||
@@ -442,29 +451,25 @@ void open_clip_worker(Clip* clip) {
|
||||
clip->stream->codecpar->sample_aspect_ratio.den
|
||||
);
|
||||
|
||||
char errormsg[100];
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", args, NULL, clip->filter_graph);
|
||||
|
||||
AVFilter* buffersrc = avfilter_get_by_name("buffer");
|
||||
AVFilter* buffersink = avfilter_get_by_name("buffersink");
|
||||
AVFilterContext* negate_ctx;
|
||||
avfilter_graph_create_filter(&negate_ctx, avfilter_get_by_name("setpts"), "rev", "0.5*PTS", NULL, clip->filter_graph);
|
||||
|
||||
if (buffersrc == NULL || buffersink == NULL) {
|
||||
qDebug() << "lol";
|
||||
}
|
||||
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, buffersrc, "in", args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, buffersink, "out", NULL, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
|
||||
enum AVPixelFormat pix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE };
|
||||
av_opt_set_int_list(clip->buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
avfilter_link(clip->buffersrc_ctx, 0, negate_ctx, 0);
|
||||
avfilter_link(negate_ctx, 0, clip->buffersink_ctx, 0);
|
||||
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// if FFmpeg can't pick up the channel layout (usually WAV), assume
|
||||
// based on channel count (doesn't support surround sound sources yet)
|
||||
if (clip->codecCtx->channel_layout == 0) {
|
||||
clip->codecCtx->channel_layout = guess_layout_from_channels(clip->stream->codecpar->channels);
|
||||
clip->codecCtx->channel_layout = av_get_default_channel_layout(clip->stream->codecpar->channels);
|
||||
}
|
||||
|
||||
// init resampling context
|
||||
|
||||
@@ -114,8 +114,13 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
// do we need to update the texture?
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
|
||||
// backwards compatibilty code
|
||||
if (c->frame_rate == 0) {
|
||||
c->frame_rate = ms->video_frame_rate;
|
||||
}
|
||||
|
||||
long sequence_clip_time = playhead - c->timeline_in + c->clip_in;
|
||||
long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, ms->video_frame_rate);
|
||||
long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, c->frame_rate);
|
||||
|
||||
AVFrame* current_frame = NULL;
|
||||
bool no_frame = false;
|
||||
|
||||
@@ -54,6 +54,7 @@ Clip* Clip::copy(Sequence* s) {
|
||||
copy->media_type = media_type;
|
||||
copy->media_stream = media_stream;
|
||||
copy->autoscale = autoscale;
|
||||
copy->frame_rate = frame_rate;
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
copy->effects.append(effects.at(i)->copy(copy));
|
||||
|
||||
@@ -69,6 +69,7 @@ struct Clip
|
||||
int media_stream;
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
double frame_rate;
|
||||
|
||||
// other variables (should be "duplicated" in copy())
|
||||
QList<Effect*> effects;
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) {
|
||||
set_value(0, false);
|
||||
set = false;
|
||||
display_type = LABELSLIDER_NORMAL;
|
||||
decimal_places = 1;
|
||||
}
|
||||
|
||||
void LabelSlider::set_display_type(int type) {
|
||||
@@ -54,9 +55,9 @@ bool LabelSlider::is_dragging() {
|
||||
QString LabelSlider::valueToString(double v) {
|
||||
switch (display_type) {
|
||||
case LABELSLIDER_FRAMENUMBER: return frame_to_timecode(v, config.timecode_view, sequence->frame_rate);
|
||||
case LABELSLIDER_PERCENT: return QString::number(v, 'f', 1) + "%";
|
||||
case LABELSLIDER_PERCENT: return QString::number(v, 'f', decimal_places) + "%";
|
||||
}
|
||||
return QString::number(v, 'f', 1);
|
||||
return QString::number(v, 'f', decimal_places);
|
||||
}
|
||||
|
||||
double LabelSlider::getPreviousValue() {
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
bool is_dragging();
|
||||
virtual QString valueToString(double v);
|
||||
double getPreviousValue();
|
||||
int decimal_places;
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
void mouseMoveEvent(QMouseEvent *ev);
|
||||
|
||||
+10
-4
@@ -329,6 +329,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
c->timeline_in = g.in;
|
||||
c->timeline_out = g.out;
|
||||
c->clip_in = g.clip_in;
|
||||
c->track = g.track;
|
||||
if (c->media_type == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = static_cast<Media*>(c->media);
|
||||
if (m->video_tracks.size() == 0) {
|
||||
@@ -347,6 +348,9 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
c->color_g = 128;
|
||||
c->color_b = 192;
|
||||
}
|
||||
if (c->track < 0) {
|
||||
c->frame_rate = m->get_stream_from_file_index(true, c->media_stream)->video_frame_rate;
|
||||
}
|
||||
c->name = m->name;
|
||||
} else if (c->media_type == MEDIA_TYPE_SEQUENCE) {
|
||||
// sequence (red?ish?)
|
||||
@@ -354,11 +358,11 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
c->color_g = 128;
|
||||
c->color_b = 128;
|
||||
|
||||
c->name = static_cast<Sequence*>(c->media)->name;
|
||||
Sequence* media = static_cast<Sequence*>(c->media);
|
||||
c->frame_rate = media->frame_rate;
|
||||
c->name = media->name;
|
||||
}
|
||||
|
||||
c->track = g.track;
|
||||
|
||||
added_clips.append(c);
|
||||
}
|
||||
|
||||
@@ -617,11 +621,13 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
c->color_b = 64;
|
||||
c->track = g.track;
|
||||
|
||||
if (c->track < 0) c->frame_rate = sequence->frame_rate;
|
||||
|
||||
QVector<Clip*> add;
|
||||
add.append(c);
|
||||
ca->append(new AddClipCommand(sequence, add));
|
||||
|
||||
int clipIndex = sequence->clips.size();
|
||||
// int clipIndex = sequence->clips.size();
|
||||
if (c->track < 0) {
|
||||
// default video effects (before custom effects)
|
||||
c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c));
|
||||
|
||||
Reference in New Issue
Block a user