diff --git a/mainwindow.cpp b/mainwindow.cpp index a627a5d13..cd20e2cc8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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() diff --git a/mainwindow.h b/mainwindow.h index 30cb28fcf..d7a8bebd8 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -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(); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 232e83d86..94e7ea423 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -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;iheaders->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); diff --git a/panels/viewer.h b/panels/viewer.h index 54806764b..b4148c18e 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -74,6 +74,7 @@ private: void set_sequence(bool main, Sequence* s); bool main_sequence; bool created_sequence; + bool disable_viewer; }; #endif // VIEWER_H diff --git a/playback/cacher.cpp b/playback/cacher.cpp index f566ebc26..fc2e531be 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -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;iqueue.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(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(sample_format), - sequence->audio_frequency / clip->speed, - clip->codecCtx->channel_layout, - static_cast(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); diff --git a/playback/playback.cpp b/playback/playback.cpp index c78706f54..cc75a0207 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -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;iqueue.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;iqueue.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;iqueue.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;iqueue.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) { diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index fdad4cc82..8ad183855 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -320,7 +320,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) { // draw sub-line markers for (int j=1;j>1)); + p.drawLine(sublineX, yoff, sublineX, yoff+(height()/4)); } } i++; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 2f62e27fb..b12142ce0 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -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()));