reimplemented optimized reversing and various improvements

This commit is contained in:
itsmattkc
2018-10-19 11:03:50 +11:00
parent da24f0ef6a
commit 8b0b33f431
8 changed files with 199 additions and 193 deletions
+44 -38
View File
@@ -157,6 +157,9 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->action_Undo, SIGNAL(triggered(bool)), this, SLOT(undo()));
connect(ui->action_Redo, SIGNAL(triggered(bool)), this, SLOT(redo()));
connect(ui->actionCu_t, SIGNAL(triggered(bool)), this, SLOT(cut()));
connect(ui->actionCop_y, SIGNAL(triggered(bool)), this, SLOT(copy()));
connect(ui->action_Paste, SIGNAL(triggered(bool)), this, SLOT(paste()));
}
MainWindow::~MainWindow() {
@@ -309,6 +312,24 @@ void MainWindow::openSpeedDialog() {
}
}
void MainWindow::cut() {
if (panel_timeline->focused() && sequence != NULL) {
panel_timeline->copy(true);
}
}
void MainWindow::copy() {
if (panel_timeline->focused() && sequence != NULL) {
panel_timeline->copy(false);
}
}
void MainWindow::paste() {
if (panel_timeline->focused() && sequence != NULL) {
panel_timeline->paste();
}
}
void MainWindow::on_actionSplit_at_Playhead_triggered()
{
if (panel_timeline->focused()) {
@@ -316,27 +337,6 @@ void MainWindow::on_actionSplit_at_Playhead_triggered()
}
}
void MainWindow::on_actionCu_t_triggered()
{
if (panel_timeline->focused()) {
panel_timeline->copy(true);
}
}
void MainWindow::on_actionCop_y_triggered()
{
if (panel_timeline->focused()) {
panel_timeline->copy(false);
}
}
void MainWindow::on_action_Paste_triggered()
{
if (panel_timeline->focused()) {
panel_timeline->paste();
}
}
void MainWindow::autorecover_interval() {
if (isWindowModified()) {
panel_project->save_project(true);
@@ -456,37 +456,43 @@ void MainWindow::on_actionReset_to_default_layout_triggered()
void MainWindow::on_actionGo_to_start_triggered()
{
if (sequence != NULL && (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus())) {
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
panel_sequence_viewer->go_to_start();
} else if (panel_footage_viewer->is_focused()) {
panel_footage_viewer->go_to_start();
}
}
void MainWindow::on_actionPrevious_Frame_triggered()
{
if (sequence != NULL && (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus())) {
void MainWindow::on_actionPrevious_Frame_triggered() {
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
panel_sequence_viewer->previous_frame();
}
} else if (panel_footage_viewer->is_focused()) {
panel_footage_viewer->previous_frame();
}
}
void MainWindow::on_actionNext_Frame_triggered()
{
if (sequence != NULL && (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus())) {
void MainWindow::on_actionNext_Frame_triggered() {
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
panel_sequence_viewer->next_frame();
}
} else if (panel_footage_viewer->is_focused()) {
panel_footage_viewer->next_frame();
}
}
void MainWindow::on_actionGo_to_End_triggered()
{
if (sequence != NULL && (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus())) {
void MainWindow::on_actionGo_to_End_triggered() {
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
panel_sequence_viewer->go_to_end();
}
} else if (panel_footage_viewer->is_focused()) {
panel_footage_viewer->go_to_end();
}
}
void MainWindow::on_actionPlay_Pause_triggered()
{
if (sequence != NULL && (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus())) {
void MainWindow::on_actionPlay_Pause_triggered() {
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
panel_sequence_viewer->toggle_play();
}
} else if (panel_footage_viewer->is_focused()) {
panel_footage_viewer->toggle_play();
}
}
void MainWindow::on_actionEdit_Tool_triggered()
+4 -7
View File
@@ -26,6 +26,9 @@ public slots:
void undo();
void redo();
void openSpeedDialog();
void cut();
void copy();
void paste();
protected:
void closeEvent(QCloseEvent *);
@@ -60,13 +63,7 @@ private slots:
void on_actionRipple_Delete_triggered();
void on_actionSplit_at_Playhead_triggered();
void on_actionCu_t_triggered();
void on_actionCop_y_triggered();
void on_action_Paste_triggered();
void on_actionSplit_at_Playhead_triggered();
void on_action_Save_Project_triggered();
+38 -20
View File
@@ -29,7 +29,8 @@ Viewer::Viewer(QWidget *parent) :
ui(new Ui::Viewer),
seq(NULL),
playing(false),
created_sequence(false)
created_sequence(false),
disable_viewer(false)
{
ui->setupUi(this);
ui->headers->viewer = this;
@@ -162,15 +163,31 @@ void Viewer::seek(long p) {
}
void Viewer::go_to_start() {
seek(0);
if (seq != NULL) {
if (seq->using_workarea && seq->playhead != seq->workarea_in) {
seek(seq->workarea_in);
} else {
seek(0);
}
}
}
void Viewer::previous_frame() {
if (seq->playhead > 0) seek(seq->playhead-1);
if (seq != NULL && seq->playhead > 0) seek(seq->playhead-1);
}
void Viewer::next_frame() {
seek(seq->playhead+1);
if (seq != NULL) seek(seq->playhead+1);
}
void Viewer::go_to_end() {
if (seq != NULL) {
if (seq->using_workarea && seq->playhead != seq->workarea_out) {
seek(seq->workarea_out);
} else {
seek(seq->getEndFrame());
}
}
}
void Viewer::toggle_play() {
@@ -182,17 +199,19 @@ void Viewer::toggle_play() {
}
void Viewer::play() {
if (audio_output->format().sampleRate() != seq->audio_frequency
|| audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) {
init_audio(seq);
if (seq != NULL) {
if (audio_output->format().sampleRate() != seq->audio_frequency
|| audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) {
init_audio(seq);
}
reset_all_audio();
playhead_start = seq->playhead;
start_msecs = QDateTime::currentMSecsSinceEpoch();
playback_updater.start();
playing = true;
set_playpause_icon(false);
audio_thread->notifyReceiver();
}
reset_all_audio();
playhead_start = seq->playhead;
start_msecs = QDateTime::currentMSecsSinceEpoch();
playback_updater.start();
playing = true;
set_playpause_icon(false);
audio_thread->notifyReceiver();
}
void Viewer::pause() {
@@ -201,10 +220,6 @@ void Viewer::pause() {
playback_updater.stop();
}
void Viewer::go_to_end() {
seek(seq->getEndFrame());
}
void Viewer::update_playhead_timecode(long p) {
ui->currentTimecode->set_value(p, false);
}
@@ -287,6 +302,7 @@ void Viewer::set_media(int type, void* media) {
seq->width = 1920;
seq->height = 1080;
seq->frame_rate = 30;
disable_viewer = true;
}
if (footage->audio_tracks.size() > 0) {
@@ -348,6 +364,8 @@ void Viewer::timer_update() {
}
void Viewer::clean_created_seq() {
disable_viewer = false;
if (created_sequence) {
// TODO delete undo commands referencing this sequence to avoid crashes
/*for (int i=0;i<undo_stack.count();i++) {
@@ -370,8 +388,8 @@ void Viewer::set_sequence(bool main, Sequence *s) {
ui->headers->setEnabled(!null_sequence);
ui->currentTimecode->setEnabled(!null_sequence);
ui->openGLWidget->setEnabled(!null_sequence);
ui->openGLWidget->setVisible(!null_sequence);
ui->openGLWidget->setEnabled(!null_sequence && !disable_viewer);
ui->openGLWidget->setVisible(!null_sequence && !disable_viewer);
ui->pushButton->setEnabled(!null_sequence);
ui->pushButton_2->setEnabled(!null_sequence);
ui->pushButton_3->setEnabled(!null_sequence);
+1
View File
@@ -74,6 +74,7 @@ private:
void set_sequence(bool main, Sequence* s);
bool main_sequence;
bool created_sequence;
bool disable_viewer;
};
#endif // VIEWER_H
+64 -69
View File
@@ -71,8 +71,6 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_
}
void cache_audio_worker(Clip* c, Clip* nest) {
qDebug() << "caw called";
long timeline_in = c->timeline_in;
long timeline_out = c->timeline_out;
if (nest != NULL) {
@@ -358,39 +356,71 @@ void cache_audio_worker(Clip* c, Clip* nest) {
void cache_video_worker(Clip* c) {
int read_ret, send_ret, retr_ret;
while (c->queue.size() < c->max_queue_size) {
AVFrame* frame = av_frame_alloc();
// SKIP TYPE SEEK
// SKIP TYPE SEEK
while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) {
AVFrame* send_frame = c->frame;
read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame);
c->use_existing_frame = false;
if (read_ret >= 0 || read_ret == AVERROR_EOF) {
if (read_ret == AVERROR_EOF) send_frame = NULL;
if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret;
break;
}
if (read_ret >= 0) {
av_frame_unref(c->frame);
}
} else {
qDebug() << "[ERROR] Failed to read frame." << read_ret;
break;
int limit = c->max_queue_size;
if (c->reverse) limit *= 2;
if (c->queue.size() < limit) {
int64_t smallest_pts = INT64_MAX;
if (c->reverse && c->queue.size() > 0) {
int64_t quarter_sec = qRound(av_q2d(av_inv_q(c->stream->time_base))) >> 2;
for (int i=0;i<c->queue.size();i++) {
smallest_pts = qMin(smallest_pts, c->queue.at(i)->pts);
}
avcodec_flush_buffers(c->codecCtx);
av_seek_frame(c->formatCtx, c->stream->index, qMax(static_cast<int64_t>(0), smallest_pts - quarter_sec), AVSEEK_FLAG_BACKWARD);
} else {
smallest_pts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, c->cacher->playhead));
}
if (retr_ret < 0) {
if (retr_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to retrieve frame from buffersink." << retr_ret;
av_frame_free(&frame);
break;
} else {
// thread-safety while adding frame to the queue
c->queue_lock.lock();
c->queue.append(frame);
c->queue_lock.unlock();
while (true) {
AVFrame* frame = av_frame_alloc();
while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) {
AVFrame* send_frame = c->frame;
read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame);
c->use_existing_frame = false;
if (read_ret >= 0) {
/*if (read_ret == AVERROR_EOF) send_frame = NULL;
if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret;
break;
}
if (read_ret >= 0) {
av_frame_unref(c->frame);
}*/
if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret;
break;
}
av_frame_unref(c->frame);
} else {
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to read frame." << read_ret;
break;
}
}
if (retr_ret < 0) {
if (retr_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to retrieve frame from buffersink." << retr_ret;
av_frame_free(&frame);
break;
} else {
if (c->reverse && smallest_pts != INT64_MAX && frame->pts >= smallest_pts) {
av_frame_free(&frame);
break;
} else {
// thread-safety while adding frame to the queue
c->queue_lock.lock();
c->queue.append(frame);
c->queue_lock.unlock();
qDebug() << "appended" << frame->pts << "/ min:" << smallest_pts;
if ((!c->reverse || smallest_pts == INT64_MAX) && c->queue.size() == limit) break;
}
}
}
}
}
@@ -409,10 +439,10 @@ void reset_cache(Clip* c, long target_frame) {
// seeks to nearest keyframe (target_frame represents internal clip frame)
int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
// if (ms->video_interlacing != VIDEO_PROGRESSIVE) target_ts *= 2;
int64_t seek_ts = target_ts;
int64_t timebase_half_second = qRound(av_q2d(av_inv_q(c->stream->time_base)));
if (c->reverse) seek_ts -= timebase_half_second;
while (true) {
// flush ffmpeg codecs
avcodec_flush_buffers(c->codecCtx);
@@ -420,23 +450,6 @@ void reset_cache(Clip* c, long target_frame) {
if (seek_ts > 0) {
av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD);
// play up to the frame we actually want
/*int ret;
do {
av_frame_unref(c->frame);
ret = retrieve_next_frame(c, c->frame);
if (ret < 0) {
qDebug() << "[WARNING] Seeking terminated prematurely";
break;
}
qDebug() << "ret pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "target:" << target_ts;
} while (c->frame->pts + c->frame->pkt_duration + c->frame->pkt_duration < target_ts);
if (c->frame->pts <= target_ts) {
c->use_existing_frame = true;
break;
} else {
seek_ts -= timebase_second;
}*/
av_frame_unref(c->frame);
int ret = retrieve_next_frame(c, c->frame);
if (ret < 0) {
@@ -523,7 +536,7 @@ void open_clip_worker(Clip* clip) {
clip->codecCtx = avcodec_alloc_context3(clip->codec);
avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar);
clip->max_queue_size = 30;
clip->max_queue_size = (ms->infinite_length) ? 1 : 15;
AVDictionary* opts = NULL;
@@ -604,26 +617,8 @@ void open_clip_worker(Clip* clip) {
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 = av_get_default_channel_layout(clip->stream->codecpar->channels);
// init resampling context
/*
clip->swr_ctx = swr_alloc_set_opts(
NULL,
sequence->audio_layout,
static_cast<AVSampleFormat>(sample_format),
sequence->audio_frequency / clip->speed,
clip->codecCtx->channel_layout,
static_cast<AVSampleFormat>(clip->stream->codecpar->format),
clip->stream->codecpar->sample_rate,
0,
NULL
);
swr_init(clip->swr_ctx);
*/
// set up cache
clip->queue.append(av_frame_alloc());
if (clip->reverse) {
@@ -713,7 +708,7 @@ void open_clip_worker(Clip* clip) {
qDebug() << "[INFO] Clip opened on track" << clip->track;
}
void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) {
void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) {
if (reset) {
// note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead
reset_cache(clip, playhead);
+44 -58
View File
@@ -115,69 +115,55 @@ void get_clip_frame(Clip* c, long playhead) {
}
AVFrame* target_frame = NULL;
// qDebug() << "target pts:" << target_pts;
bool reset = false;
/*while (c->queue.size() > 0) {
if (c->queue.first()->pts == target_pts) {
target_frame = c->queue.first();
qDebug() << "GCF ==> USE PRECISE";
break;
} else if (c->queue.size() > 1 && c->queue.at(1)->pts > target_pts) {
// use this frame
target_frame = c->queue.first();
qDebug() << "GCF ==> USE IMPRECISE";
break;
} else {
break;
} else {
// skip this frame
c->queue_mutex.lock(); // thread safety when removing from queue
av_frame_free(&c->queue.first()); // may be a little heavy for the UI thread?
c->queue.removeFirst();
c->queue_mutex.unlock();
qDebug() << "GCF ==> SKIP";
}
}*/
qDebug() << "QS:" << c->queue.size();
if (c->queue.size() > 0) {
// correct frame may be somewhere else in the queue
int closest_frame = 0;
for (int i=1;i<c->queue.size();i++) {
if (c->queue.at(i)->pts == target_pts) {
qDebug() << "GCF ==> USE PRECISE";
closest_frame = i;
break;
} else if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
closest_frame = i;
}
}
// remove all frames earlier than this one from the queue
target_frame = c->queue.at(closest_frame);
c->queue_lock.lock();
for (int i=0;i<c->queue.size();i++) {
if (c->queue.at(i)->pts < target_frame->pts) {
av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread?
c->queue.removeAt(i);
i--;
}
}
c->queue_lock.unlock();
// if this frame is more than one second out, we probably need to reset
if (qAbs(target_pts - target_frame->pts) > second_pts) {
target_frame = NULL;
// qDebug() << "GCF ==> COMPLACENT";
qDebug() << "GCF ==> RESET";
reset = true;
if (ms->infinite_length) {
target_frame = c->queue.at(0);
qDebug() << "GCF ==> USE PRECISE (INFINITE)";
} else {
qDebug() << "GCF ==> USE IMPRECISE";
// correct frame may be somewhere else in the queue
int closest_frame = 0;
for (int i=1;i<c->queue.size();i++) {
if (c->queue.at(i)->pts == target_pts) {
qDebug() << "GCF ==> USE PRECISE";
closest_frame = i;
break;
} else if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
closest_frame = i;
}
}
// remove all frames earlier than this one from the queue
target_frame = c->queue.at(closest_frame);
c->queue_lock.lock();
for (int i=0;i<c->queue.size();i++) {
if (c->queue.at(i)->pts != target_frame->pts) {
if ((c->queue.at(i)->pts < target_frame->pts) == (!c->reverse)) {
qDebug() << "removed frame whose pts was" << c->queue.at(i)->pts << "compared to target" << target_frame->pts;
av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread?
c->queue.removeAt(i);
i--;
}
}
}
c->queue_lock.unlock();
// if this frame is more than one second out, we probably need to reset
if (qAbs(target_pts - target_frame->pts) > second_pts) {
target_frame = NULL;
// qDebug() << "GCF ==> COMPLACENT";
qDebug() << "GCF ==> RESET";
reset = true;
} else {
qDebug() << "GCF ==> USE IMPRECISE";
}
}
} else {
reset = true;
}
// get more frames
@@ -186,7 +172,7 @@ void get_clip_frame(Clip* c, long playhead) {
if (target_frame == NULL || reset) {
// reset cache
texture_failed = true;
qDebug() << "[INFO] Cache couldn't keep up - either the user seeked or the system is overloaded";
qDebug() << "[INFO] Frame queue couldn't keep up - either the user seeked or the system is overloaded (queue size:" << c->queue.size() << ")";
}
if (target_frame != NULL) {
+1 -1
View File
@@ -320,7 +320,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) {
// draw sub-line markers
for (int j=1;j<sublineCount;j++) {
int sublineX = lineX+(qRound(j*interval/sublineCount)*zoom);
p.drawLine(sublineX, yoff, sublineX, yoff+(yoff>>1));
p.drawLine(sublineX, yoff, sublineX, yoff+(height()/4));
}
}
i++;
+3
View File
@@ -133,8 +133,11 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
menu.addAction("Sequence settings coming soon...");
} else {
QAction* cutAction = menu.addAction("C&ut");
connect(cutAction, SIGNAL(triggered(bool)), mainWindow, SLOT(cut()));
QAction* copyAction = menu.addAction("Cop&y");
connect(copyAction, SIGNAL(triggered(bool)), mainWindow, SLOT(copy()));
QAction* pasteAction = menu.addAction("&Paste");
connect(pasteAction, SIGNAL(triggered(bool)), mainWindow, SLOT(paste()));
menu.addSeparator();
QAction* speedAction = menu.addAction("&Speed/Duration");
connect(speedAction, SIGNAL(triggered(bool)), mainWindow, SLOT(openSpeedDialog()));