viewer: improved playback ux
This commit is contained in:
@@ -58,7 +58,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
|||||||
time_changed_from_timer_(false),
|
time_changed_from_timer_(false),
|
||||||
prequeuing_video_(false),
|
prequeuing_video_(false),
|
||||||
prequeuing_audio_(false),
|
prequeuing_audio_(false),
|
||||||
active_queue_jobs_(0),
|
|
||||||
audio_playback_device_(nullptr)
|
audio_playback_device_(nullptr)
|
||||||
{
|
{
|
||||||
// Set up main layout
|
// Set up main layout
|
||||||
@@ -515,6 +514,16 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
|||||||
delete watcher;
|
delete watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewerWidget::ForceRequeueFromCurrentTime()
|
||||||
|
{
|
||||||
|
ClearVideoAutoCacherQueue();
|
||||||
|
int queue = DeterminePlaybackQueueSize();
|
||||||
|
playback_queue_next_frame_ = GetTimestamp() + playback_speed_;
|
||||||
|
for (int i=queue_watchers_.size(); i<queue; i++) {
|
||||||
|
RequestNextFrameForQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ViewerWidget::UpdateTextureFromNode()
|
void ViewerWidget::UpdateTextureFromNode()
|
||||||
{
|
{
|
||||||
rational time = GetTime();
|
rational time = GetTime();
|
||||||
@@ -529,11 +538,9 @@ void ViewerWidget::UpdateTextureFromNode()
|
|||||||
bool popped = false;
|
bool popped = false;
|
||||||
|
|
||||||
if (playback_queue_.empty()) {
|
if (playback_queue_.empty()) {
|
||||||
int queue = DeterminePlaybackQueueSize();
|
|
||||||
playback_queue_next_frame_ = GetTimestamp() + playback_speed_;
|
ForceRequeueFromCurrentTime();
|
||||||
for (int i=active_queue_jobs_; i<queue; i++) {
|
|
||||||
RequestNextFrameForQueue();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
while (!playback_queue_.empty()) {
|
while (!playback_queue_.empty()) {
|
||||||
|
|
||||||
@@ -545,9 +552,15 @@ void ViewerWidget::UpdateTextureFromNode()
|
|||||||
SetDisplayImage(pf.frame, true);
|
SetDisplayImage(pf.frame, true);
|
||||||
return;
|
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 {
|
} else {
|
||||||
|
|
||||||
// Skip this frame
|
// Frame was too old, skip this frame
|
||||||
PopOldestFrameFromPlaybackQueue();
|
PopOldestFrameFromPlaybackQueue();
|
||||||
if (popped) {
|
if (popped) {
|
||||||
// We've already popped a frame in this loop, meaning a frame has been skipped
|
// We've already popped a frame in this loop, meaning a frame has been skipped
|
||||||
@@ -559,21 +572,16 @@ void ViewerWidget::UpdateTextureFromNode()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (playback_queue_.empty()) {
|
if (playback_queue_.empty()) {
|
||||||
// This was the last frame left in the queue. Even though it's old, we'll show it
|
|
||||||
// just for improved user feedback
|
ForceRequeueFromCurrentTime();
|
||||||
SetDisplayImage(pf.frame, true);
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only show warning if frame actually exists
|
|
||||||
if (frame_exists_at_time && !frame_might_be_still) {
|
|
||||||
//qWarning() << "Playback queue failed to keep up";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_exists_at_time || frame_might_be_still) {
|
if (frame_exists_at_time || frame_might_be_still) {
|
||||||
@@ -688,6 +696,9 @@ void ViewerWidget::PauseInternal()
|
|||||||
window->Pause();
|
window->Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qDeleteAll(queue_watchers_);
|
||||||
|
queue_watchers_.clear();
|
||||||
|
|
||||||
playback_queue_.clear();
|
playback_queue_.clear();
|
||||||
playback_backup_timer_.stop();
|
playback_backup_timer_.stop();
|
||||||
|
|
||||||
@@ -790,7 +801,7 @@ void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment)
|
|||||||
watcher->setProperty("time", QVariant::fromValue(next_time));
|
watcher->setProperty("time", QVariant::fromValue(next_time));
|
||||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue);
|
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue);
|
||||||
watcher->SetTicket(GetFrame(next_time, prioritize));
|
watcher->SetTicket(GetFrame(next_time, prioritize));
|
||||||
active_queue_jobs_++;
|
queue_watchers_.append(watcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -959,28 +970,30 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
|
|||||||
{
|
{
|
||||||
RenderTicketWatcher* watcher = static_cast<RenderTicketWatcher*>(sender());
|
RenderTicketWatcher* watcher = static_cast<RenderTicketWatcher*>(sender());
|
||||||
|
|
||||||
if (watcher->HasResult()) {
|
if (queue_watchers_.contains(watcher)) {
|
||||||
QVariant frame = watcher->Get();
|
queue_watchers_.removeOne(watcher);
|
||||||
|
|
||||||
// Ignore this signal if we've paused now
|
if (watcher->HasResult()) {
|
||||||
if (IsPlaying() || prequeuing_video_) {
|
QVariant frame = watcher->Get();
|
||||||
rational ts = watcher->property("time").value<rational>();
|
|
||||||
|
|
||||||
playback_queue_.AppendTimewise({ts, frame}, playback_speed_);
|
// Ignore this signal if we've paused now
|
||||||
|
if (IsPlaying() || prequeuing_video_) {
|
||||||
|
rational ts = watcher->property("time").value<rational>();
|
||||||
|
|
||||||
foreach (ViewerWindow* window, windows_) {
|
playback_queue_.AppendTimewise({ts, frame}, playback_speed_);
|
||||||
window->queue()->AppendTimewise({ts, frame}, playback_speed_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prequeuing_video_ && int(playback_queue_.size()) == prequeue_length_) {
|
foreach (ViewerWindow* window, windows_) {
|
||||||
prequeuing_video_ = false;
|
window->queue()->AppendTimewise({ts, frame}, playback_speed_);
|
||||||
FinishPlayPreprocess();
|
}
|
||||||
|
|
||||||
|
if (prequeuing_video_ && int(playback_queue_.size()) == prequeue_length_) {
|
||||||
|
prequeuing_video_ = false;
|
||||||
|
FinishPlayPreprocess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
active_queue_jobs_--;
|
|
||||||
|
|
||||||
delete watcher;
|
delete watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ private:
|
|||||||
|
|
||||||
PreviewAutoCacher auto_cacher_;
|
PreviewAutoCacher auto_cacher_;
|
||||||
|
|
||||||
int active_queue_jobs_;
|
QVector<RenderTicketWatcher*> queue_watchers_;
|
||||||
|
|
||||||
std::shared_ptr<PreviewAudioDevice> audio_playback_device_;
|
std::shared_ptr<PreviewAudioDevice> audio_playback_device_;
|
||||||
std::list<RenderTicketWatcher*> audio_playback_queue_;
|
std::list<RenderTicketWatcher*> audio_playback_queue_;
|
||||||
@@ -317,6 +317,8 @@ private slots:
|
|||||||
|
|
||||||
void ReceivedAudioBufferForScrubbing();
|
void ReceivedAudioBufferForScrubbing();
|
||||||
|
|
||||||
|
void ForceRequeueFromCurrentTime();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user