extremely limited speed support implemented #120

This commit is contained in:
itsmattkc
2018-10-01 14:51:36 +10:00
parent c21a21c219
commit ac153e4cff
14 changed files with 143 additions and 91 deletions
+81 -14
View File
@@ -6,36 +6,103 @@
#include <QCheckBox> #include <QCheckBox>
#include <QPushButton> #include <QPushButton>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDebug>
#include "ui/labelslider.h" #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) { SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) {
QGridLayout* layout = new QGridLayout(); QVBoxLayout* main_layout = new QVBoxLayout();
layout->setSpacing(6); setLayout(main_layout);
layout->setMargin(9);
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 = new LabelSlider();
percent->decimal_places = 2;
percent->set_display_type(LABELSLIDER_PERCENT); 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(); 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 = new LabelSlider();
duration->set_display_type(LABELSLIDER_FRAMENUMBER); 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"); reverse = new QCheckBox("Reverse");
reverseLayout->addWidget(reverse); main_layout->addWidget(reverse);
layout->addLayout(reverseLayout, 3, 0, 2, 1); main_layout->addWidget(new QCheckBox("Maintain Audio Pitch"));
main_layout->addWidget(new QCheckBox("Ripple"));
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 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(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();
} }
+10
View File
@@ -9,15 +9,25 @@ class QCheckBox;
class SpeedDialog : public QDialog class SpeedDialog : public QDialog
{ {
Q_OBJECT
public: public:
SpeedDialog(QWidget* parent = 0); SpeedDialog(QWidget* parent = 0);
QVector<Clip*> clips; QVector<Clip*> clips;
void run();
private slots:
void percent_update();
void duration_update();
void frame_rate_update();
void accept();
private: private:
LabelSlider* percent; LabelSlider* percent;
LabelSlider* duration; LabelSlider* duration;
LabelSlider* frame_rate; LabelSlider* frame_rate;
QCheckBox* reverse; QCheckBox* reverse;
double normal_fr;
}; };
#endif // SPEEDDIALOG_H #endif // SPEEDDIALOG_H
-9
View File
@@ -46,12 +46,3 @@ MediaStream* Media::get_stream_from_file_index(bool video, int index) {
} }
return NULL; 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;
}
}
-2
View File
@@ -50,6 +50,4 @@ struct Media {
void reset(); void reset();
}; };
int guess_layout_from_channels(int channel_count);
#endif // MEDIA_H #endif // MEDIA_H
+1 -1
View File
@@ -110,7 +110,7 @@ void PreviewGenerator::generate_waveform() {
avcodec_open2(codec_ctx[i], codec, NULL); avcodec_open2(codec_ctx[i], codec, NULL);
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) { 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
View File
@@ -300,7 +300,7 @@ void MainWindow::openSpeedDialog() {
s.clips.append(c); s.clips.append(c);
} }
} }
if (s.clips.size() > 0) s.exec(); if (s.clips.size() > 0) s.run();
} }
} }
+7 -41
View File
@@ -685,46 +685,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
} else if (attr.name() == "duration") { } else if (attr.name() == "duration") {
m->length = attr.value().toLongLong(); 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); 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); Clip* c = new Clip(s);
// backwards compatibility code // backwards compatibility code
c->autoscale = false; c->autoscale = false;
c->frame_rate = 0;
c->media = NULL; c->media = NULL;
for (int j=0;j<stream.attributes().size();j++) { 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(); media_id = attr.value().toInt();
} else if (attr.name() == "stream") { } else if (attr.name() == "stream") {
stream_id = attr.value().toInt(); stream_id = attr.value().toInt();
} else if (attr.name() == "framerate") {
c->frame_rate = attr.value().toDouble();
} else if (attr.name() == "sequence") { } else if (attr.name() == "sequence") {
c->media_type = MEDIA_TYPE_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("autoscale", QString::number(c->autoscale));
stream.writeAttribute("framerate", QString::number(c->frame_rate));
stream.writeAttribute("type", QString::number(c->media_type)); stream.writeAttribute("type", QString::number(c->media_type));
switch (c->media_type) { switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE: case MEDIA_TYPE_FOOTAGE:
+21 -16
View File
@@ -170,7 +170,6 @@ void cache_audio_worker(Clip* c, Clip* nest) {
} else { } else {
long buffer_timeline_out = get_buffer_offset_from_frame(timeline_out); long buffer_timeline_out = get_buffer_offset_from_frame(timeline_out);
audio_write_lock.lock(); audio_write_lock.lock();
qDebug() << "starting to write:" << audio_ibuffer_read;
while (c->frame_sample_index < nb_bytes while (c->frame_sample_index < nb_bytes
&& c->audio_buffer_write < audio_ibuffer_read+audio_ibuffer_size && c->audio_buffer_write < audio_ibuffer_read+audio_ibuffer_size
&& c->audio_buffer_write < buffer_timeline_out) { && c->audio_buffer_write < buffer_timeline_out) {
@@ -207,6 +206,8 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
int i = 0; int i = 0;
double fr_ratio = qRound((c->sequence->frame_rate / c->frame_rate)*100)*0.01;
/* old swscale solution /* old swscale solution
if (!c->reached_end) { if (!c->reached_end) {
while (i < c->cache_size) { while (i < c->cache_size) {
@@ -227,11 +228,19 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
if (ret < 0) { if (ret < 0) {
if (ret == AVERROR(EAGAIN)) { if (ret == AVERROR(EAGAIN)) {
ret = retrieve_next_frame(c, c->frame); ret = retrieve_next_frame(c, c->frame);
if (ret >= 0) { if (ret >= 0) {
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { // optimization: mathematically determine based on the sequence and clips' frame rates whether this frame will actually be shown
qDebug() << "[ERROR] Could not feed filtergraph -" << ret; double timeline_frame = (i+cache->offset) * fr_ratio;
error = true;
break; 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 { } else {
if (ret == AVERROR_EOF) { if (ret == AVERROR_EOF) {
@@ -442,29 +451,25 @@ void open_clip_worker(Clip* clip) {
clip->stream->codecpar->sample_aspect_ratio.den 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"); AVFilterContext* negate_ctx;
AVFilter* buffersink = avfilter_get_by_name("buffersink"); avfilter_graph_create_filter(&negate_ctx, avfilter_get_by_name("setpts"), "rev", "0.5*PTS", NULL, clip->filter_graph);
if (buffersrc == NULL || buffersink == NULL) { avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
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);
enum AVPixelFormat pix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE }; 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); 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); avfilter_graph_config(clip->filter_graph, NULL);
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
// if FFmpeg can't pick up the channel layout (usually WAV), assume // if FFmpeg can't pick up the channel layout (usually WAV), assume
// based on channel count (doesn't support surround sound sources yet) // based on channel count (doesn't support surround sound sources yet)
if (clip->codecCtx->channel_layout == 0) { 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 // init resampling context
+6 -1
View File
@@ -114,8 +114,13 @@ bool get_clip_frame(Clip* c, long playhead) {
// do we need to update the texture? // 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); 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 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; AVFrame* current_frame = NULL;
bool no_frame = false; bool no_frame = false;
+1
View File
@@ -54,6 +54,7 @@ Clip* Clip::copy(Sequence* s) {
copy->media_type = media_type; copy->media_type = media_type;
copy->media_stream = media_stream; copy->media_stream = media_stream;
copy->autoscale = autoscale; copy->autoscale = autoscale;
copy->frame_rate = frame_rate;
for (int i=0;i<effects.size();i++) { for (int i=0;i<effects.size();i++) {
copy->effects.append(effects.at(i)->copy(copy)); copy->effects.append(effects.at(i)->copy(copy));
+1
View File
@@ -69,6 +69,7 @@ struct Clip
int media_stream; int media_stream;
int getWidth(); int getWidth();
int getHeight(); int getHeight();
double frame_rate;
// other variables (should be "duplicated" in copy()) // other variables (should be "duplicated" in copy())
QList<Effect*> effects; QList<Effect*> effects;
+3 -2
View File
@@ -21,6 +21,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) {
set_value(0, false); set_value(0, false);
set = false; set = false;
display_type = LABELSLIDER_NORMAL; display_type = LABELSLIDER_NORMAL;
decimal_places = 1;
} }
void LabelSlider::set_display_type(int type) { void LabelSlider::set_display_type(int type) {
@@ -54,9 +55,9 @@ bool LabelSlider::is_dragging() {
QString LabelSlider::valueToString(double v) { QString LabelSlider::valueToString(double v) {
switch (display_type) { switch (display_type) {
case LABELSLIDER_FRAMENUMBER: return frame_to_timecode(v, config.timecode_view, sequence->frame_rate); 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() { double LabelSlider::getPreviousValue() {
+1
View File
@@ -24,6 +24,7 @@ public:
bool is_dragging(); bool is_dragging();
virtual QString valueToString(double v); virtual QString valueToString(double v);
double getPreviousValue(); double getPreviousValue();
int decimal_places;
protected: protected:
void mousePressEvent(QMouseEvent *ev); void mousePressEvent(QMouseEvent *ev);
void mouseMoveEvent(QMouseEvent *ev); void mouseMoveEvent(QMouseEvent *ev);
+10 -4
View File
@@ -329,6 +329,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
c->timeline_in = g.in; c->timeline_in = g.in;
c->timeline_out = g.out; c->timeline_out = g.out;
c->clip_in = g.clip_in; c->clip_in = g.clip_in;
c->track = g.track;
if (c->media_type == MEDIA_TYPE_FOOTAGE) { if (c->media_type == MEDIA_TYPE_FOOTAGE) {
Media* m = static_cast<Media*>(c->media); Media* m = static_cast<Media*>(c->media);
if (m->video_tracks.size() == 0) { if (m->video_tracks.size() == 0) {
@@ -347,6 +348,9 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
c->color_g = 128; c->color_g = 128;
c->color_b = 192; 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; c->name = m->name;
} else if (c->media_type == MEDIA_TYPE_SEQUENCE) { } else if (c->media_type == MEDIA_TYPE_SEQUENCE) {
// sequence (red?ish?) // sequence (red?ish?)
@@ -354,11 +358,11 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
c->color_g = 128; c->color_g = 128;
c->color_b = 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); added_clips.append(c);
} }
@@ -617,11 +621,13 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
c->color_b = 64; c->color_b = 64;
c->track = g.track; c->track = g.track;
if (c->track < 0) c->frame_rate = sequence->frame_rate;
QVector<Clip*> add; QVector<Clip*> add;
add.append(c); add.append(c);
ca->append(new AddClipCommand(sequence, add)); ca->append(new AddClipCommand(sequence, add));
int clipIndex = sequence->clips.size(); // int clipIndex = sequence->clips.size();
if (c->track < 0) { if (c->track < 0) {
// default video effects (before custom effects) // default video effects (before custom effects)
c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c)); c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c));