reworked viewer timers

This commit is contained in:
itsmattkc
2022-01-02 16:56:11 -08:00
parent ee3ea5990b
commit 2afa7ecafe
9 changed files with 183 additions and 222 deletions
-7
View File
@@ -101,11 +101,4 @@ void ViewerPanelBase::SetViewerWidget(ViewerWidget *vw)
SetTimeBasedWidget(vw);
}
void ViewerPanelBase::closeEvent(QCloseEvent *e)
{
static_cast<ViewerWidget*>(GetTimeBasedWidget())->Pause();
TimeBasedPanel::closeEvent(e);
}
}
-2
View File
@@ -88,8 +88,6 @@ signals:
protected:
void SetViewerWidget(ViewerWidget *vw);
virtual void closeEvent(QCloseEvent* e) override;
};
}
+64 -142
View File
@@ -81,13 +81,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
display_widget_ = new ViewerDisplayWidget();
display_widget_->setAcceptDrops(true);
display_widget_->SetShowWidgetBackground(true);
playback_devices_.append(display_widget_);
connect(display_widget_, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(display_widget_, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(display_widget_, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
connect(display_widget_, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered);
connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped);
connect(display_widget_, &ViewerDisplayWidget::VisibilityChanged, this, &ViewerWidget::Pause);
connect(display_widget_, &ViewerDisplayWidget::TextureChanged, this, &ViewerWidget::TextureChanged);
connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom);
connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate);
@@ -207,9 +207,8 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n)
ColorManager* color_manager = n->project()->color_manager();
display_widget_->ConnectColorManager(color_manager);
foreach (ViewerWindow* window, windows_) {
window->display_widget()->ConnectColorManager(color_manager);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->ConnectColorManager(color_manager);
}
UpdateStack();
@@ -247,9 +246,8 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
// Effectively disables the viewer and clears the state
SetViewerResolution(0, 0);
display_widget_->DisconnectColorManager();
foreach (ViewerWindow* window, windows_) {
window->display_widget()->DisconnectColorManager();
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->DisconnectColorManager();
}
waveform_view_->SetViewer(nullptr);
@@ -299,9 +297,8 @@ void ViewerWidget::SetColorMenuEnabled(bool enabled)
void ViewerWidget::SetMatrix(const QMatrix4x4 &mat)
{
display_widget_->SetMatrixCrop(mat);
foreach (ViewerWindow* vw, windows_) {
vw->display_widget()->SetMatrixCrop(mat);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetMatrixCrop(mat);
}
}
@@ -343,9 +340,11 @@ void ViewerWidget::SetFullScreen(QScreen *screen)
vw->display_widget()->SetImage(QVariant::fromValue(display_widget()->GetCurrentTexture()));
(*vw->queue()) = playback_queue_;
playback_devices_.append(vw->display_widget());
(*vw->display_widget()->queue()) = *playback_devices_.first()->queue();
if (IsPlaying()) {
vw->Play(GetTimestamp(), playback_speed_, timebase());
vw->display_widget()->Play(GetTimestamp(), playback_speed_, timebase());
}
windows_.insert(screen, vw);
@@ -402,10 +401,8 @@ bool ViewerWidget::ShouldForceWaveform() const
void ViewerWidget::SetEmptyImage()
{
display_widget()->SetBlank();
foreach (ViewerWindow *vw, windows_) {
vw->display_widget()->SetBlank();
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetBlank();
}
}
@@ -539,85 +536,30 @@ void ViewerWidget::UpdateTextureFromNode()
return;
}
if (IsPlaying()) {
qDebug() << "UpdateTextureFromNode called while playing";
return;
}
rational time = GetTime();
bool frame_exists_at_time = FrameExistsAtTime(time);
bool frame_might_be_still = ViewerMightBeAStill();
// Check playback queue for a frame
if (IsPlaying()) {
// We still run the playback queue even when FrameExistsAtTime returns false because we might be
// playing backwards and about to start showing frames, so the queue should be prepared for
// that.
bool popped = false;
if (playback_queue_.empty()) {
ForceRequeueFromCurrentTime();
} else {
while (!playback_queue_.empty()) {
ViewerPlaybackFrame pf = playback_queue_.front();
if (pf.timestamp == time) {
// Frame was in queue, no need to decode anything
SetDisplayImage(pf.frame, true);
return;
} else if (pf.timestamp > time) {
// The next frame in the queue is too new, so just do a regular update. Either the
// frame we want will arrive in time, or we'll just have to skip it.
break;
} else {
// Frame was too old, skip this frame
PopOldestFrameFromPlaybackQueue();
if (popped) {
// We've already popped a frame in this loop, meaning a frame has been skipped
display_widget_->IncrementSkippedFrames();
} else {
// Shown a frame and progressed to the next one
display_widget_->IncrementFrameCount();
popped = true;
}
if (playback_queue_.empty()) {
ForceRequeueFromCurrentTime();
break;
}
}
}
}
}
if (frame_exists_at_time || frame_might_be_still) {
// Frame was not in queue, will require rendering or decoding from cache
if (IsPlaying()) {
// Is playing, yet the queue above failed to retrieve the frame. We effectively do a quick
// reboot of the queue here, assuming the above loop has emptied it so far.
display_widget_->update();
} else {
// Not playing, run a task to get the frame either from the cache or the renderer
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("start", QDateTime::currentMSecsSinceEpoch());
watcher->setProperty("time", QVariant::fromValue(time));
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame);
nonqueue_watchers_.append(watcher);
// Not playing, run a task to get the frame either from the cache or the renderer
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("start", QDateTime::currentMSecsSinceEpoch());
watcher->setProperty("time", QVariant::fromValue(time));
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame);
nonqueue_watchers_.append(watcher);
// Clear queue because we want this frame more than any others
if (!GetConnectedNode()->GetVideoAutoCacheEnabled() && !auto_cacher_.IsRenderingCustomRange()) {
ClearVideoAutoCacherQueue();
}
watcher->SetTicket(GetFrame(time, true));
// Clear queue because we want this frame more than any others
if (!GetConnectedNode()->GetVideoAutoCacheEnabled() && !auto_cacher_.IsRenderingCustomRange()) {
ClearVideoAutoCacherQueue();
}
watcher->SetTicket(GetFrame(time, true));
} else {
// There is definitely no frame here, we can immediately flip to showing nothing
nonqueue_watchers_.clear();
@@ -673,6 +615,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
if (prequeue_length_ > 0) {
prequeuing_video_ = true;
prequeue_count_ = 0;
// We "prioritize" the frames, which means they're pushed to the top of the render queue,
// we queue in reverse so that they're still queued in order
@@ -715,16 +658,13 @@ void ViewerWidget::PauseInternal()
playback_speed_ = 0;
controls_->ShowPlayButton();
disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
foreach (ViewerWindow* window, windows_) {
window->Pause();
foreach (ViewerDisplayWidget *dw, playback_devices_){
dw->Pause();
}
qDeleteAll(queue_watchers_);
queue_watchers_.clear();
playback_queue_.clear();
playback_backup_timer_.stop();
// Handle audio
@@ -811,14 +751,10 @@ bool ViewerWidget::ViewerMightBeAStill()
return GetConnectedNode() && GetConnectedNode()->GetConnectedTextureOutput() && GetConnectedNode()->GetVideoLength().isNull();
}
void ViewerWidget::SetDisplayImage(QVariant frame, bool main_only)
void ViewerWidget::SetDisplayImage(QVariant frame)
{
display_widget_->SetImage(frame);
if (!main_only) {
foreach (ViewerWindow* vw, windows_) {
vw->display_widget()->SetImage(frame);
}
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetImage(frame);
}
}
@@ -876,19 +812,15 @@ void ViewerWidget::FinishPlayPreprocess()
GetTime(), playback_speed_);
}
playback_timer_.Start(playback_start_time, playback_speed_, timebase_dbl());
display_widget_->ResetFPSTimer();
foreach (ViewerWindow* window, windows_) {
window->Play(playback_start_time, playback_speed_, timebase());
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->Play(playback_start_time, playback_speed_, timebase());
}
if (display_widget_->isVisible()) {
connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
} else {
playback_backup_timer_.setInterval(qFloor(timebase_dbl()));
playback_backup_timer_.start();
}
// This is our timer for loading the queue and setting the time
playback_backup_timer_.setInterval(qFloor(timebase_dbl()));
playback_backup_timer_.start();
PlaybackTimerUpdate();
}
@@ -909,15 +841,6 @@ int ViewerWidget::DeterminePlaybackQueueSize()
return qMin(kMaxPreQueueSize, remaining_frames);
}
void ViewerWidget::PopOldestFrameFromPlaybackQueue()
{
playback_queue_.pop_front();
if (int(playback_queue_.size()) < DeterminePlaybackQueueSize()) {
RequestNextFrameForQueue();
}
}
void ViewerWidget::UpdateStack()
{
rational new_tb;
@@ -970,7 +893,9 @@ void ViewerWidget::ContextMenuSetCustomSafeMargins()
void ViewerWidget::WindowAboutToClose()
{
windows_.remove(windows_.key(static_cast<ViewerWindow*>(sender())));
ViewerWindow *vw = static_cast<ViewerWindow*>(sender());
windows_.remove(windows_.key(vw));
playback_devices_.removeOne(vw->display_widget());
}
void ViewerWidget::RendererGeneratedFrame()
@@ -1017,13 +942,12 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
if (IsPlaying() || prequeuing_video_) {
rational ts = watcher->property("time").value<rational>();
playback_queue_.AppendTimewise({ts, frame}, playback_speed_);
foreach (ViewerWindow* window, windows_) {
window->queue()->AppendTimewise({ts, frame}, playback_speed_);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->queue()->AppendTimewise({ts, frame}, playback_speed_);
}
prequeue_count_++;
if (prequeuing_video_ && int(playback_queue_.size()) == prequeue_length_) {
if (prequeuing_video_ && prequeue_count_ == prequeue_length_) {
prequeuing_video_ = false;
FinishPlayPreprocess();
}
@@ -1251,10 +1175,8 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform)
void ViewerWidget::SetSignalCursorColorEnabled(bool e)
{
display_widget_->SetSignalCursorColorEnabled(e);
foreach (ViewerWindow* vw, windows_) {
vw->display_widget()->SetSignalCursorColorEnabled(e);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetSignalCursorColorEnabled(e);
}
}
@@ -1270,7 +1192,7 @@ void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
void ViewerWidget::PlaybackTimerUpdate()
{
rational current_time = Timecode::timestamp_to_time(playback_timer_.GetTimestampNow(), timebase());
rational current_time = Timecode::timestamp_to_time(display_widget_->timer()->GetTimestampNow(), timebase());
rational min_time, max_time;
@@ -1352,14 +1274,15 @@ void ViewerWidget::PlaybackTimerUpdate()
}
}
if (display_widget_->isVisible()) {
// Updating display widget
UpdateTextureFromNode();
} else if (!windows_.empty()) {
// We still run the queue if windows are visible even if our own display widget isn't visible
while (!playback_queue_.empty() && playback_queue_.front().timestamp != GetTime()) {
PopOldestFrameFromPlaybackQueue();
}
// We still run the queue if windows are visible even if our own display widget isn't visible
/*while (!playback_queue_.empty() && playback_queue_.front().timestamp != GetTime()) {
PopOldestFrameFromPlaybackQueue();
}*/
// NOTE: There should be some calculation here to determine how many frames need to be queued
RequestNextFrameForQueue();
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->queue()->PurgeBefore(current_time, playback_speed_);
}
}
@@ -1398,10 +1321,10 @@ void ViewerWidget::LengthChangedSlot(const rational &length)
void ViewerWidget::InterlacingChangedSlot(VideoParams::Interlacing interlacing)
{
// Automatically set a "sane" deinterlacing option
display_widget_->SetDeinterlacing(interlacing != VideoParams::kInterlaceNone);
bool deint = interlacing != VideoParams::kInterlaceNone;
foreach (ViewerWindow* vw, windows_) {
vw->display_widget()->SetDeinterlacing(interlacing != VideoParams::kInterlaceNone);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetDeinterlacing(deint);
}
}
@@ -1409,9 +1332,8 @@ void ViewerWidget::UpdateRendererVideoParameters()
{
VideoParams vp = GetConnectedNode()->GetVideoParams();
display_widget_->SetVideoParams(vp);
foreach (ViewerWindow* window, windows_) {
window->display_widget()->SetVideoParams(vp);
foreach (ViewerDisplayWidget *dw, playback_devices_) {
dw->SetVideoParams(vp);
}
}
+3 -7
View File
@@ -37,8 +37,6 @@
#include "render/previewautocacher.h"
#include "threading/threadticketwatcher.h"
#include "viewerdisplay.h"
#include "viewerplaybacktimer.h"
#include "viewerqueue.h"
#include "viewersizer.h"
#include "viewerwindow.h"
#include "widget/playbackcontrols/playbackcontrols.h"
@@ -183,7 +181,7 @@ private:
bool ViewerMightBeAStill();
void SetDisplayImage(QVariant frame, bool main_only = false);
void SetDisplayImage(QVariant frame);
void RequestNextFrameForQueue(bool prioritize = false, bool increment = true);
@@ -193,8 +191,6 @@ private:
int DeterminePlaybackQueueSize();
void PopOldestFrameFromPlaybackQueue();
static FramePtr DecodeCachedImage(const QString &cache_path, const QByteArray &hash, const rational& time);
static void DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QByteArray &hash, const rational& time);
@@ -231,11 +227,10 @@ private:
ViewerDisplayWidget* context_menu_widget_;
ViewerPlaybackTimer playback_timer_;
QTimer playback_backup_timer_;
ViewerQueue playback_queue_;
int64_t playback_queue_next_frame_;
QVector<ViewerDisplayWidget*> playback_devices_;
bool prequeuing_video_;
int prequeuing_audio_;
@@ -245,6 +240,7 @@ private:
rational last_length_;
int prequeue_length_;
int prequeue_count_;
PreviewAutoCacher auto_cacher_;
+77 -4
View File
@@ -313,16 +313,20 @@ void ViewerDisplayWidget::dropEvent(QDropEvent *event)
void ViewerDisplayWidget::showEvent(QShowEvent *event)
{
emit VisibilityChanged(true);
super::showEvent(event);
if (isVisible()) {
emit VisibilityChanged(true);
}
}
void ViewerDisplayWidget::hideEvent(QHideEvent *event)
{
emit VisibilityChanged(false);
super::hideEvent(event);
if (!isVisible()) {
emit VisibilityChanged(false);
}
}
void ViewerDisplayWidget::OnPaint()
@@ -602,4 +606,73 @@ void ViewerDisplayWidget::SetShowFPS(bool e)
update();
}
void ViewerDisplayWidget::Play(const int64_t &start_timestamp, const int &playback_speed, const rational &timebase)
{
playback_timebase_ = timebase;
timer_.Start(start_timestamp, playback_speed, timebase.toDouble());
connect(this, &ViewerDisplayWidget::frameSwapped, this, &ViewerDisplayWidget::UpdateFromQueue);
update();
}
void ViewerDisplayWidget::Pause()
{
disconnect(this, &ViewerDisplayWidget::frameSwapped, this, &ViewerDisplayWidget::UpdateFromQueue);
queue_.clear();
}
void ViewerDisplayWidget::UpdateFromQueue()
{
int64_t t = timer_.GetTimestampNow();
rational time = Timecode::timestamp_to_time(t, playback_timebase_);
bool popped = false;
if (queue_.empty()) {
//ForceRequeueFromCurrentTime();
} else {
while (!queue_.empty()) {
const ViewerPlaybackFrame& pf = queue_.front();
if (pf.timestamp == time) {
// Frame was in queue, no need to decode anything
SetImage(pf.frame);
return;
} else if (pf.timestamp > time) {
// The next frame in the queue is too new, so just do a regular update. Either the
// frame we want will arrive in time, or we'll just have to skip it.
break;
} else {
queue_.pop_front();
if (popped) {
// We've already popped a frame in this loop, meaning a frame has been skipped
IncrementSkippedFrames();
} else {
// Shown a frame and progressed to the next one
IncrementFrameCount();
popped = true;
}
if (queue_.empty()) {
//ForceRequeueFromCurrentTime();
break;
}
}
}
}
update();
}
}
+27
View File
@@ -28,6 +28,8 @@
#include "node/node.h"
#include "render/color.h"
#include "tool/tool.h"
#include "viewerplaybacktimer.h"
#include "viewerqueue.h"
#include "viewersafemargininfo.h"
#include "widget/manageddisplay/manageddisplay.h"
#include "widget/timetarget/timetarget.h"
@@ -107,6 +109,20 @@ public:
return texture_;
}
void Play(const int64_t &start_timestamp, const int &playback_speed, const rational &timebase);
void Pause();
ViewerQueue* queue()
{
return &queue_;
}
ViewerPlaybackTimer *timer()
{
return &timer_;
}
public slots:
/**
* @brief Set the transformation matrix to draw with
@@ -210,6 +226,8 @@ protected:
virtual void hideEvent(QHideEvent* event) override;
//virtual void closeEvent(QCloseEvent* event) override;
protected slots:
/**
* @brief Paint function to display the texture (received in SetTexture()) on screen.
@@ -334,9 +352,18 @@ private:
PushMode push_mode_;
// Playback
ViewerQueue queue_;
ViewerPlaybackTimer timer_;
rational playback_timebase_;
private slots:
void EmitColorAtCursor(QMouseEvent* e);
void UpdateFromQueue();
};
}
+10
View File
@@ -50,6 +50,16 @@ public:
}
}
void PurgeBefore(const rational &time, int playback_speed)
{
int popped = 0;
while (!this->empty() && this->front().timestamp < time) {
popped++;
this->pop_front();
}
if (popped > 0) qDebug() << "Purged" << popped;
}
};
}
-39
View File
@@ -68,24 +68,6 @@ void ViewerWindow::SetPixelAspectRatio(const rational &pixel_aspect)
UpdateMatrix();
}
void ViewerWindow::Play(const int64_t& start_timestamp, const int& playback_speed, const rational &timebase)
{
timer_.Start(start_timestamp, playback_speed, timebase.toDouble());
playback_timebase_ = timebase;
connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWindow::UpdateFromQueue);
display_widget_->update();
}
void ViewerWindow::Pause()
{
disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWindow::UpdateFromQueue);
queue_.clear();
}
void ViewerWindow::keyPressEvent(QKeyEvent *e)
{
QWidget::keyPressEvent(e);
@@ -102,27 +84,6 @@ void ViewerWindow::closeEvent(QCloseEvent *e)
deleteLater();
}
void ViewerWindow::UpdateFromQueue()
{
int64_t t = timer_.GetTimestampNow();
rational time = Timecode::timestamp_to_time(t, playback_timebase_);
while (!queue_.empty()) {
const ViewerPlaybackFrame& pf = queue_.front();
if (pf.timestamp == time) {
// Frame was in queue, no need to decode anything
display_widget_->SetImage(pf.frame);
return;
} else {
queue_.pop_front();
}
}
display_widget_->update();
}
void ViewerWindow::UpdateMatrix()
{
// Set GL widget matrix to maintain this texture's aspect ratio
+2 -21
View File
@@ -24,8 +24,6 @@
#include <QWidget>
#include "viewerdisplay.h"
#include "viewerplaybacktimer.h"
#include "viewerqueue.h"
namespace olive {
@@ -54,37 +52,20 @@ public:
*/
void SetPixelAspectRatio(const rational& pixel_aspect);
ViewerQueue* queue() {
return &queue_;
}
void Play(const int64_t &start_timestamp, const int &playback_speed, const rational &timebase);
void Pause();
protected:
virtual void keyPressEvent(QKeyEvent* e) override;
virtual void closeEvent(QCloseEvent* e) override;
private slots:
void UpdateFromQueue();
private:
void UpdateMatrix();
ViewerDisplayWidget* display_widget_;
ViewerQueue queue_;
ViewerPlaybackTimer timer_;
rational playback_timebase_;
int width_;
int height_;
ViewerDisplayWidget* display_widget_;
rational pixel_aspect_;
};