From 8462301cafb597588782576f07bf75caf2d7dc38 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 23 Jan 2019 18:57:52 +1100 Subject: [PATCH] jkl playback is functional --- io/exportthread.cpp | 6 ++-- mainwindow.cpp | 33 ++++++++++++++++-- mainwindow.h | 5 ++- panels/viewer.cpp | 41 +++++++++++++++++++--- panels/viewer.h | 6 ++++ playback/audio.cpp | 2 +- playback/cacher.cpp | 77 ++++++++++++++++++++++++++++-------------- playback/cacher.h | 9 ++--- playback/playback.cpp | 11 +++--- playback/playback.h | 6 ++-- ui/renderfunctions.cpp | 13 +++---- ui/renderfunctions.h | 5 +-- ui/renderthread.cpp | 6 ++-- ui/renderthread.h | 3 +- ui/viewerwidget.cpp | 2 +- 15 files changed, 162 insertions(+), 63 deletions(-) diff --git a/io/exportthread.cpp b/io/exportthread.cpp index f6b6dc134..a16a27ec7 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -351,7 +351,7 @@ void ExportThread::run() { start_time = QDateTime::currentMSecsSinceEpoch(); if (audio_enabled) { - compose_audio(nullptr, sequence, true); + compose_audio(nullptr, sequence, true, false); } if (video_enabled) { do { @@ -489,7 +489,7 @@ void ExportThread::run() { } void ExportThread::wake() { - mutex.lock(); + mutex.lock(); waitCond.wakeAll(); - mutex.unlock(); + mutex.unlock(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index 584d64931..7bb6fc90e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -744,9 +744,9 @@ void MainWindow::setup_menus() { playback_menu->addAction(tr("Go to In Point"), this, SLOT(go_to_in()), QKeySequence("Shift+I"))->setProperty("id", "gotoin"); playback_menu->addAction(tr("Go to Out Point"), this, SLOT(go_to_out()), QKeySequence("Shift+O"))->setProperty("id", "gotoout"); playback_menu->addSeparator(); - playback_menu->addAction(tr("Increase Speed"), this, SLOT(go_to_out()), QKeySequence("L"))->setProperty("id", "incspeed"); - playback_menu->addAction(tr("Pause"), panel_sequence_viewer, SLOT(pause()), QKeySequence("K"))->setProperty("id", "pause"); - playback_menu->addAction(tr("Decrease Speed"), this, SLOT(go_to_in()), QKeySequence("J"))->setProperty("id", "decspeed"); + playback_menu->addAction(tr("Decrease Speed"), this, SLOT(decrease_speed()), QKeySequence("J"))->setProperty("id", "decspeed"); + playback_menu->addAction(tr("Pause"), this, SLOT(pause()), QKeySequence("K"))->setProperty("id", "pause"); + playback_menu->addAction(tr("Increase Speed"), this, SLOT(increase_speed()), QKeySequence("L"))->setProperty("id", "incspeed"); loop_action = playback_menu->addAction(tr("Loop"), this, SLOT(toggle_bool_action())); loop_action->setProperty("id", "loop"); @@ -1136,6 +1136,33 @@ void MainWindow::playpause() { } } +void MainWindow::pause() { + QDockWidget* focused_panel = get_focused_panel(); + if (focused_panel == panel_footage_viewer) { + panel_footage_viewer->pause(); + } else { + panel_sequence_viewer->pause(); + } +} + +void MainWindow::increase_speed() { + QDockWidget* focused_panel = get_focused_panel(); + if (focused_panel == panel_footage_viewer) { + panel_footage_viewer->increase_speed(); + } else { + panel_sequence_viewer->increase_speed(); + } +} + +void MainWindow::decrease_speed() { + QDockWidget* focused_panel = get_focused_panel(); + if (focused_panel == panel_footage_viewer) { + panel_footage_viewer->decrease_speed(); + } else { + panel_sequence_viewer->decrease_speed(); + } +} + void MainWindow::prev_cut() { QDockWidget* focused_panel = get_focused_panel(); if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) { diff --git a/mainwindow.h b/mainwindow.h index 295834a15..85b49635b 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -41,7 +41,7 @@ public slots: protected: void closeEvent(QCloseEvent *); - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); private slots: void clear_undo_stack(); @@ -67,6 +67,9 @@ private slots: void go_to_start(); void prev_frame(); void playpause(); + void pause(); + void increase_speed(); + void decrease_speed(); void next_frame(); void go_to_end(); void prev_cut(); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index ac90174c3..25c0a5e73 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -44,7 +44,8 @@ Viewer::Viewer(QWidget *parent) : seq(nullptr), created_sequence(false), minimum_zoom(1.0), - cue_recording_internal(false) + cue_recording_internal(false), + playback_speed(0) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -101,15 +102,20 @@ void Viewer::set_main_sequence() { void Viewer::reset_all_audio() { // reset all clip audio if (seq != nullptr) { - audio_ibuffer_frame = seq->playhead; - audio_ibuffer_timecode = (double) audio_ibuffer_frame / seq->frame_rate; - + long last_frame = 0; for (int i=0;iclips.size();i++) { Clip* c = seq->clips.at(i); if (c != nullptr) { c->reset_audio(); + last_frame = qMax(last_frame, c->timeline_out); } } + + audio_ibuffer_frame = seq->playhead; + if (playback_speed < 0) { + audio_ibuffer_frame = last_frame - audio_ibuffer_frame; + } + audio_ibuffer_timecode = double(audio_ibuffer_frame) / seq->frame_rate; } clear_audio_ibuffer(); } @@ -324,6 +330,14 @@ void Viewer::toggle_play() { } } +void Viewer::increase_speed() { + set_playback_speed(playback_speed+1); +} + +void Viewer::decrease_speed() { + set_playback_speed(playback_speed-1); +} + void Viewer::play() { if (panel_sequence_viewer->playing) panel_sequence_viewer->pause(); if (panel_footage_viewer->playing) panel_footage_viewer->pause(); @@ -335,6 +349,10 @@ void Viewer::play() { seek(get_seq_in()); } + if (playback_speed == 0) { + playback_speed = 1; + } + reset_all_audio(); if (is_recording_cued() && !start_recording()) { qCritical() << "Failed to record audio"; @@ -363,6 +381,7 @@ void Viewer::pause() { just_played = false; set_playpause_icon(true); playback_updater.stop(); + playback_speed = 0; if (is_recording_cued()) { uncue_recording(); @@ -432,6 +451,10 @@ void Viewer::update_parents(bool reload_fx) { } } +int Viewer::get_playback_speed() { + return playback_speed; +} + void Viewer::resizeEvent(QResizeEvent *) { if (seq != nullptr) { set_sb_max(); @@ -519,6 +542,14 @@ void Viewer::set_sb_max() { headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); } +void Viewer::set_playback_speed(int s) { + pause(); + playback_speed = qMin(3, qMax(s, -3)); + if (playback_speed != 0) { + play(); + } +} + long Viewer::get_seq_in() { return (seq->using_workarea && seq->enable_workarea) ? seq->workarea_in @@ -713,7 +744,7 @@ void Viewer::update_playhead() { void Viewer::timer_update() { previous_playhead = seq->playhead; - seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate)); + seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed)); if (config.seek_also_selects) panel_timeline->select_from_playhead(); update_parents(config.seek_also_selects); diff --git a/panels/viewer.h b/panels/viewer.h index 85ddc063b..690695247 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -67,6 +67,8 @@ public: void reset_all_audio(); void update_parents(bool reload_fx = false); + int get_playback_speed(); + ViewerWidget* viewer_widget; Media* media; @@ -80,6 +82,8 @@ public slots: void go_to_in(); void previous_frame(); void toggle_play(); + void increase_speed(); + void decrease_speed(); void next_frame(); void go_to_out(); void go_to_end(); @@ -102,6 +106,7 @@ private: double minimum_zoom; void set_zoom_value(double d); void set_sb_max(); + void set_playback_speed(int s); long get_seq_in(); long get_seq_out(); @@ -126,6 +131,7 @@ private: QTimer recording_flasher; long previous_playhead; + int playback_speed; }; #endif // VIEWER_H diff --git a/playback/audio.cpp b/playback/audio.cpp index eef32402c..b5abc9faf 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -119,7 +119,7 @@ qint64 get_buffer_offset_from_frame(double framerate, long frame) { int multiplier = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); return qFloor((double(frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*multiplier; } else { - qWarning() << "Invalid values passed to get_buffer_offset_from_frame"; + qWarning() << "Invalid values passed to get_buffer_offset_from_frame" << frame << "<" << audio_ibuffer_frame; return 0; } } diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 915d01428..91f813153 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -86,11 +86,14 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_ #define AUDIO_BUFFER_PADDING 2048 -void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { +void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests, int playback_speed) { long timeline_in = c->get_timeline_in_with_transition(); long timeline_out = c->get_timeline_out_with_transition(); long target_frame = c->audio_target_frame; + bool temp_reverse = (playback_speed < 0); + bool reverse_audio = (c->reverse != temp_reverse); + long frame_skip = 0; double last_fr = c->sequence->frame_rate; if (!nests.isEmpty()) { @@ -113,6 +116,17 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { } } + if (temp_reverse) { + long seq_end = sequence->getEndFrame(); + timeline_in = seq_end - timeline_in; + timeline_out = seq_end - timeline_out; + target_frame = seq_end - target_frame; + + long temp = timeline_in; + timeline_in = timeline_out; + timeline_out = temp; + } + while (true) { AVFrame* frame; int nb_bytes = INT_MAX; @@ -147,7 +161,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { if (!c->reached_end) { int loop = 0; - if (c->reverse && !c->audio_just_reset) { + if (reverse_audio && !c->audio_just_reset) { avcodec_flush_buffers(c->codecCtx); c->reached_end = false; int64_t backtrack_seek = qMax(c->reverse_target - static_cast(av_q2d(av_inv_q(c->stream->time_base))), static_cast(0)); @@ -177,7 +191,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { dout << "reached EOF while reading"; #endif // TODO revise usage of reached_end in audio - if (!c->reverse) { + if (!reverse_audio) { c->reached_end = true; } else { } @@ -198,11 +212,11 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { #ifdef AUDIOWARNINGS dout << "reached EOF while pulling from filtergraph"; #endif - if (!c->reverse) break; + if (!reverse_audio) break; } } - if (c->reverse) { + if (reverse_audio) { if (loop > 1) { AVFrame* rev_frame = c->queue.at(1); if (ret != AVERROR_EOF) { @@ -313,7 +327,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase); dout << "fsi-calc:" << c->frame_sample_index; #endif - if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index; + if (reverse_audio) c->frame_sample_index = nb_bytes - c->frame_sample_index; c->audio_just_reset = false; } @@ -343,7 +357,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { } } - if (c->reverse) frame = c->queue.at(1); + if (reverse_audio) frame = c->queue.at(1); #ifdef AUDIOWARNINGS dout << "j" << c->frame_sample_index << nb_bytes; @@ -368,20 +382,29 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { audio_write_lock.lock(); + int audio_skip_resolution = 100; // FIXME magic number + + int sample_skip = 4*qMax(0, qAbs(playback_speed)-1); + int sample_byte_size = av_get_bytes_per_sample(static_cast(frame->format)); + while (c->frame_sample_index < nb_bytes && c->audio_buffer_write < audio_ibuffer_read+(audio_ibuffer_size>>1) && c->audio_buffer_write < buffer_timeline_out) { - int upper_byte_index = (c->audio_buffer_write+1)%audio_ibuffer_size; - int lower_byte_index = (c->audio_buffer_write)%audio_ibuffer_size; - qint16 old_sample = static_cast((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF)); - qint16 new_sample = static_cast((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF)); - qint16 mixed_sample = mix_audio_sample(old_sample, new_sample); + for (int i=0;ichannels;i++) { + int upper_byte_index = (c->audio_buffer_write+1)%audio_ibuffer_size; + int lower_byte_index = (c->audio_buffer_write)%audio_ibuffer_size; + qint16 old_sample = static_cast((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF)); + qint16 new_sample = static_cast((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF)); + qint16 mixed_sample = mix_audio_sample(old_sample, new_sample); - audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF); - audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF); + audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF); + audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF); - c->audio_buffer_write+=2; - c->frame_sample_index+=2; + c->audio_buffer_write+=sample_byte_size; + c->frame_sample_index+=sample_byte_size; + } + + c->frame_sample_index += sample_skip; if (c->audio_reset) break; } @@ -398,7 +421,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { if (audio_thread != nullptr) audio_thread->notifyReceiver(); } - if (c->frame_sample_index == nb_bytes) { + if (c->frame_sample_index >= nb_bytes) { c->frame_sample_index = -1; } else { // assume we have no more data to send @@ -544,7 +567,7 @@ void cache_video_worker(Clip* c, long playhead) { } } -void reset_cache(Clip* c, long target_frame) { +void reset_cache(Clip* c, long target_frame, int playback_speed) { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values if (c->media == nullptr) { if (c->track >= 0) { @@ -607,9 +630,12 @@ void reset_cache(Clip* c, long target_frame) { // seek (target_frame represents timeline timecode in frames, not clip timecode) +// bool reverse = (c->reverse != temp_reverse); + int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); - if (c->reverse) { + bool temp_reverse = (playback_speed < 0); + if (c->reverse != temp_reverse) { c->reverse_target = timestamp; timestamp -= av_q2d(av_inv_q(c->stream->time_base)); #ifdef AUDIOWARNINGS @@ -790,7 +816,8 @@ void open_clip_worker(Clip* clip) { // set up cache clip->queue.append(av_frame_alloc()); - if (clip->reverse) { +// if (clip->reverse) { + if (true) { AVFrame* reverse_frame = av_frame_alloc(); reverse_frame->format = sample_format; @@ -886,22 +913,22 @@ void open_clip_worker(Clip* clip) { qInfo() << "Clip opened on track" << clip->track; } -void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nests) { +void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nests, int playback_speed) { if (reset) { // note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead - reset_cache(clip, playhead); + reset_cache(clip, playhead, playback_speed); clip->audio_reset = false; } if (clip->media == nullptr) { if (clip->track >= 0) { - cache_audio_worker(clip, scrubbing, nests); + cache_audio_worker(clip, scrubbing, nests, playback_speed); } } else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { cache_video_worker(clip, playhead); } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - cache_audio_worker(clip, scrubbing, nests); + cache_audio_worker(clip, scrubbing, nests, playback_speed); } } } @@ -945,7 +972,7 @@ void Cacher::run() { break; } else { while (true) { - cache_clip_worker(clip, playhead, reset, scrubbing, nests); + cache_clip_worker(clip, playhead, reset, scrubbing, nests, playback_speed); if (clip->multithreaded && clip->cacher->interrupt && clip->track < 0) { clip->cacher->interrupt = false; } else { diff --git a/playback/cacher.h b/playback/cacher.h index ad6060681..4c3a985f2 100644 --- a/playback/cacher.h +++ b/playback/cacher.h @@ -11,15 +11,16 @@ class Cacher : public QThread // Q_OBJECT public: Cacher(Clip* c); - void run(); + void run(); bool caching; // must be set before caching long playhead; bool reset; - bool scrubbing; - bool interrupt; + bool scrubbing; + bool interrupt; + int playback_speed; QVector nests; private: @@ -27,7 +28,7 @@ private: }; void open_clip_worker(Clip* clip); -void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nest); +void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nest, int playback_speed); void close_clip_worker(Clip* clip); #endif // CACHER_H diff --git a/playback/playback.cpp b/playback/playback.cpp index 4c3ed3cc8..62fcc9454 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -105,18 +105,19 @@ void close_clip(Clip* clip, bool wait) { } } -void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector& nests) { +void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector& nests, int playback_speed) { if (clip_uses_cacher(clip)) { if (clip->multithreaded) { clip->cacher->playhead = playhead; clip->cacher->reset = reset; clip->cacher->nests = nests; clip->cacher->scrubbing = scrubbing; + clip->cacher->playback_speed = playback_speed; if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true; clip->can_cache.wakeAll(); } else { - cache_clip_worker(clip, playhead, reset, scrubbing, nests); + cache_clip_worker(clip, playhead, reset, scrubbing, nests, playback_speed); } } } @@ -264,13 +265,13 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) { uint8_t* data_buffer_1 = target_frame->data[0]; uint8_t* data_buffer_2 = nullptr; - int frame_size; + size_t frame_size; for (int i=0;ieffects.size();i++) { Effect* e = c->effects.at(i); if (e->enable_image && e->is_enabled()) { if (data_buffer_1 == target_frame->data[0]) { - frame_size = target_frame->linesize[0]*target_frame->height; + frame_size = size_t(target_frame->linesize[0])*size_t(target_frame->height); data_buffer_1 = new uint8_t[frame_size]; data_buffer_2 = new uint8_t[frame_size]; @@ -296,7 +297,7 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) { // get more frames QVector empty; - if (cache) cache_clip(c, playhead, reset, false, empty); + if (cache) cache_clip(c, playhead, reset, false, empty, false); } } diff --git a/playback/playback.h b/playback/playback.h index 2c0a6cd17..2758e9ead 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -12,12 +12,10 @@ struct AVFrame; long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); bool clip_uses_cacher(Clip* clip); void open_clip(Clip* clip, bool multithreaded); -void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector &nests); +void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector &nests, int playback_speed); void close_clip(Clip* clip, bool wait); -void cache_audio_worker(Clip* c, bool write_A); -void cache_video_worker(Clip* c, long playhead); void handle_media(Sequence* sequence, long playhead, bool multithreaded); -void reset_cache(Clip* c, long target_frame); +void reset_cache(Clip* c, long target_frame, int playback_speed); void get_clip_frame(Clip* c, long playhead, bool &texture_failed); double get_timecode(Clip* c, long playhead); diff --git a/ui/renderfunctions.cpp b/ui/renderfunctions.cpp index ec38146c3..bbab2bd2b 100644 --- a/ui/renderfunctions.cpp +++ b/ui/renderfunctions.cpp @@ -119,7 +119,8 @@ GLuint compose_sequence(Viewer* viewer, bool render_audio, Effect** gizmos, bool& texture_failed, - bool rendering) { + bool rendering, + int playback_speed) { GLint current_fbo = 0; if (video) { glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); @@ -286,7 +287,7 @@ GLuint compose_sequence(Viewer* viewer, // for nested sequences if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) { nests.append(c); - textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering); + textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering, false); nests.removeLast(); fbo_switcher = true; } @@ -440,12 +441,12 @@ GLuint compose_sequence(Viewer* viewer, if (render_audio || (config.enable_audio_scrubbing && audio_scrub && seq->playhead > c->timeline_in)) { if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) { nests.append(c); - compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering); + compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering, playback_speed); nests.removeLast(); } else { if (c->lock.tryLock()) { // clip is not caching, start caching audio - cache_clip(c, playhead, c->audio_reset, !render_audio, nests); + cache_clip(c, playhead, c->audio_reset, !render_audio, nests, playback_speed); c->lock.unlock(); } } @@ -484,8 +485,8 @@ GLuint compose_sequence(Viewer* viewer, return 0; } -void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio) { +void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed) { QVector nests; bool texture_failed; - compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering); + compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering, playback_speed); } diff --git a/ui/renderfunctions.h b/ui/renderfunctions.h index de405bf8c..6523a16ba 100644 --- a/ui/renderfunctions.h +++ b/ui/renderfunctions.h @@ -17,9 +17,10 @@ GLuint compose_sequence(Viewer* viewer, bool render_audio, Effect **gizmos, bool &texture_failed, - bool rendering); + bool rendering, + int playback_speed); -void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio); +void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed); void viewport_render(); diff --git a/ui/renderthread.cpp b/ui/renderthread.cpp index c1ead032d..0ec8d4c46 100644 --- a/ui/renderthread.cpp +++ b/ui/renderthread.cpp @@ -110,7 +110,7 @@ void RenderThread::paint() { gizmos = nullptr; QVector nests; - compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false); + compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false, temp_reverse); if (!save_fn.isEmpty()) { if (texture_failed) { @@ -138,12 +138,14 @@ void RenderThread::paint() { glDisable(GL_TEXTURE_2D); } -void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider) { +void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider, bool itemp_reverse) { seq = s; // stall any dependent actions texture_failed = true; + temp_reverse = itemp_reverse; + if (share != nullptr && (ctx == nullptr || ctx->shareContext() != share_ctx)) { share_ctx = share; delete_ctx(); diff --git a/ui/renderthread.h b/ui/renderthread.h index cd4f1aec3..54eecd646 100644 --- a/ui/renderthread.h +++ b/ui/renderthread.h @@ -22,7 +22,7 @@ public: GLuint texColorBuffer; Effect* gizmos; void paint(); - void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0); + void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0, bool itemp_reverse = false); bool did_texture_fail(); void cancel(); @@ -47,6 +47,7 @@ private: bool queued; bool texture_failed; bool running; + bool temp_reverse; QString save_fn; GLvoid *pixel_buffer; }; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 50530edf9..196968d58 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -230,7 +230,7 @@ void ViewerWidget::frame_update() { } // render the audio - compose_audio(viewer, viewer->seq, render_audio); + compose_audio(viewer, viewer->seq, render_audio, viewer->get_playback_speed()); } }