viewer: improved frame queue and linked window
Improves minor issues with frame queue implemented in earlier commit. Also links ViewerWindow with the queue and a separate timer to make it much smoother.
This commit is contained in:
@@ -111,4 +111,18 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type)
|
||||
vw->ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerPanelBase::showEvent(QShowEvent *e)
|
||||
{
|
||||
static_cast<ViewerWidget*>(GetTimeBasedWidget())->Pause();
|
||||
|
||||
TimeBasedPanel::showEvent(e);
|
||||
}
|
||||
|
||||
void ViewerPanelBase::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
static_cast<ViewerWidget*>(GetTimeBasedWidget())->Pause();
|
||||
|
||||
TimeBasedPanel::closeEvent(e);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -62,6 +62,10 @@ public slots:
|
||||
protected:
|
||||
void CreateScopePanel(ScopePanel::Type type);
|
||||
|
||||
virtual void showEvent(QShowEvent* e) override;
|
||||
|
||||
virtual void closeEvent(QCloseEvent* e) override;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -26,6 +26,9 @@ set(OLIVE_SOURCES
|
||||
widget/viewer/viewer.cpp
|
||||
widget/viewer/viewerdisplay.h
|
||||
widget/viewer/viewerdisplay.cpp
|
||||
widget/viewer/viewerplaybacktimer.h
|
||||
widget/viewer/viewerplaybacktimer.cpp
|
||||
widget/viewer/viewerqueue.h
|
||||
widget/viewer/viewersafemargininfo.h
|
||||
widget/viewer/viewersizer.h
|
||||
widget/viewer/viewersizer.cpp
|
||||
|
||||
@@ -35,7 +35,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
|
||||
audio_node_ = new AudioInput();
|
||||
viewer_node_ = new ViewerOutput();
|
||||
|
||||
connect(main_gl_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
|
||||
controls_->SetAudioVideoDragButtonsVisible(true);
|
||||
connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag);
|
||||
|
||||
+106
-70
@@ -63,15 +63,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
sizer_ = new ViewerSizer();
|
||||
stack_->addWidget(sizer_);
|
||||
|
||||
ViewerDisplayWidget* main_widget = new ViewerDisplayWidget();
|
||||
connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor);
|
||||
connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer);
|
||||
connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
|
||||
connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
|
||||
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix);
|
||||
sizer_->SetWidget(main_widget);
|
||||
gl_widgets_.append(main_widget);
|
||||
display_widget_ = new ViewerDisplayWidget();
|
||||
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(sizer_, &ViewerSizer::RequestMatrix, display_widget_, &ViewerDisplayWidget::SetMatrix);
|
||||
sizer_->SetWidget(display_widget_);
|
||||
|
||||
// Create waveform view when audio is connected and video isn't
|
||||
waveform_view_ = new AudioWaveformView();
|
||||
@@ -117,6 +115,15 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
SetAutoMaxScrollBar(true);
|
||||
}
|
||||
|
||||
ViewerWidget::~ViewerWidget()
|
||||
{
|
||||
QList<ViewerWindow*> windows = windows_;
|
||||
|
||||
foreach (ViewerWindow* window, windows) {
|
||||
delete window;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::TimeChangedEvent(const int64_t &i)
|
||||
{
|
||||
if (!time_changed_from_timer_) {
|
||||
@@ -133,7 +140,7 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i)
|
||||
|
||||
PushScrubbedAudio();
|
||||
|
||||
main_gl_widget()->SetTime(time_set);
|
||||
display_widget_->SetTime(time_set);
|
||||
}
|
||||
|
||||
last_time_ = i;
|
||||
@@ -170,8 +177,9 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
using_manager = nullptr;
|
||||
}
|
||||
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->ConnectColorManager(using_manager);
|
||||
display_widget_->ConnectColorManager(using_manager);
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->display_widget()->ConnectColorManager(using_manager);
|
||||
}
|
||||
|
||||
divider_ = CalculateDivider();
|
||||
@@ -202,8 +210,9 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
// Effectively disables the viewer and clears the state
|
||||
SizeChangedSlot(0, 0);
|
||||
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->DisconnectColorManager();
|
||||
display_widget_->DisconnectColorManager();
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->display_widget()->DisconnectColorManager();
|
||||
}
|
||||
|
||||
waveform_view_->ConnectTimelinePoints(nullptr);
|
||||
@@ -236,14 +245,9 @@ void ViewerWidget::resizeEvent(QResizeEvent *event)
|
||||
UpdateMinimumScale();
|
||||
}
|
||||
|
||||
const QList<ViewerDisplayWidget*> &ViewerWidget::gl_widgets() const
|
||||
ViewerDisplayWidget *ViewerWidget::display_widget() const
|
||||
{
|
||||
return gl_widgets_;
|
||||
}
|
||||
|
||||
ViewerDisplayWidget *ViewerWidget::main_gl_widget() const
|
||||
{
|
||||
return gl_widgets_.first();
|
||||
return display_widget_;
|
||||
}
|
||||
|
||||
void ViewerWidget::TogglePlayPause()
|
||||
@@ -282,8 +286,9 @@ void ViewerWidget::SetOverrideSize(int width, int height)
|
||||
|
||||
void ViewerWidget::SetMatrix(const QMatrix4x4 &mat)
|
||||
{
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->SetMatrix(mat);
|
||||
display_widget_->SetMatrix(mat);
|
||||
foreach (ViewerWindow* vw, windows_) {
|
||||
vw->display_widget()->SetMatrix(mat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,19 +311,19 @@ void ViewerWidget::SetFullScreen(QScreen *screen)
|
||||
|
||||
ViewerWindow* vw = new ViewerWindow(this);
|
||||
|
||||
vw->showFullScreen();
|
||||
vw->setGeometry(screen->geometry());
|
||||
vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager());
|
||||
main_gl_widget()->ConnectSibling(vw->gl_widget());
|
||||
vw->showFullScreen();
|
||||
vw->display_widget()->ConnectColorManager(color_manager());
|
||||
connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose);
|
||||
connect(vw->gl_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(vw->display_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
|
||||
if (GetConnectedNode()) {
|
||||
vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height());
|
||||
}
|
||||
|
||||
vw->display_widget()->SetImage(display_widget_->last_loaded_buffer());
|
||||
|
||||
windows_.append(vw);
|
||||
gl_widgets_.append(vw->gl_widget());
|
||||
}
|
||||
|
||||
void ViewerWidget::ForceUpdate()
|
||||
@@ -334,27 +339,29 @@ VideoRenderBackend *ViewerWidget::video_renderer() const
|
||||
|
||||
ColorManager *ViewerWidget::color_manager() const
|
||||
{
|
||||
return main_gl_widget()->color_manager();
|
||||
return display_widget_->color_manager();
|
||||
}
|
||||
|
||||
void ViewerWidget::SetGizmos(Node *node)
|
||||
{
|
||||
main_gl_widget()->SetTimeTarget(GetConnectedNode());
|
||||
main_gl_widget()->SetGizmos(node);
|
||||
display_widget_->SetTimeTarget(GetConnectedNode());
|
||||
display_widget_->SetGizmos(node);
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(&playback_frame_queue_lock_);
|
||||
while (!playback_frame_queue_.isEmpty()) {
|
||||
PlaybackFrame pf = playback_frame_queue_.takeFirst();
|
||||
QMutexLocker locker(playback_queue_.lock());
|
||||
while (!playback_queue_.isEmpty()) {
|
||||
const ViewerPlaybackFrame& pf = playback_queue_.first();
|
||||
|
||||
if (pf.timestamp == time) {
|
||||
// Frame was in queue, no need to decode anything
|
||||
main_gl_widget()->SetImageFromLoadBuffer(pf.frame.get());
|
||||
SetDisplayImage(pf.frame, true);
|
||||
QtConcurrent::run(this, &ViewerWidget::FillPlaybackQueue);
|
||||
return;
|
||||
} else {
|
||||
playback_queue_.removeFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,10 +372,10 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
|
||||
if (!frame_fn.isEmpty()) {
|
||||
FramePtr f = DecodeCachedImage(frame_fn);
|
||||
main_gl_widget()->SetImageFromLoadBuffer(f.get());
|
||||
SetDisplayImage(f, false);
|
||||
}
|
||||
} else {
|
||||
main_gl_widget()->SetImageFromLoadBuffer(nullptr);
|
||||
SetDisplayImage(nullptr, false);
|
||||
video_renderer_->UpdateLastRequestedTime(time);
|
||||
}
|
||||
}
|
||||
@@ -393,9 +400,12 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
playback_speed_);
|
||||
}
|
||||
|
||||
start_msec_ = QDateTime::currentMSecsSinceEpoch();
|
||||
start_timestamp_ = ruler()->GetTime();
|
||||
playback_frame_queue_next_frame_ = start_timestamp_;
|
||||
playback_queue_next_frame_ = ruler()->GetTime();
|
||||
playback_timer_.Start(playback_queue_next_frame_, playback_speed_, timebase_dbl());
|
||||
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->Play(playback_queue_next_frame_, playback_speed_, timebase());
|
||||
}
|
||||
|
||||
controls_->ShowPauseButton();
|
||||
|
||||
@@ -404,10 +414,10 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
if (playback_is_audio_only_) {
|
||||
connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
} else {
|
||||
FillPlaybackQueue();
|
||||
|
||||
connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
}
|
||||
|
||||
FillPlaybackQueue();
|
||||
}
|
||||
|
||||
void ViewerWidget::PushScrubbedAudio()
|
||||
@@ -436,7 +446,7 @@ int ViewerWidget::CalculateDivider()
|
||||
{
|
||||
if (GetConnectedNode() && Config::Current()["AutoSelectDivider"].toBool()) {
|
||||
int long_side_of_video = qMax(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height());
|
||||
int long_side_of_widget = qMax(main_gl_widget()->width(), main_gl_widget()->height());
|
||||
int long_side_of_widget = qMax(display_widget_->width(), display_widget_->height());
|
||||
|
||||
return qMax(1, long_side_of_video / long_side_of_widget);
|
||||
}
|
||||
@@ -465,12 +475,16 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp
|
||||
|
||||
void ViewerWidget::FillPlaybackQueue()
|
||||
{
|
||||
playback_frame_queue_lock_.lock();
|
||||
while (playback_frame_queue_.size() < 8) {
|
||||
playback_queue_.lock()->lock();
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->lock()->lock();
|
||||
}
|
||||
|
||||
while (playback_queue_.size() < 8) {
|
||||
// Load frame from cache
|
||||
FramePtr frame = nullptr;
|
||||
|
||||
rational rtime = Timecode::timestamp_to_time(playback_frame_queue_next_frame_,
|
||||
rational rtime = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
|
||||
QString frame_fn = GetCachedFilenameFromTime(rtime);
|
||||
@@ -479,15 +493,25 @@ void ViewerWidget::FillPlaybackQueue()
|
||||
frame = DecodeCachedImage(frame_fn);
|
||||
}
|
||||
|
||||
playback_frame_queue_.append({rtime, frame});
|
||||
ViewerPlaybackFrame f = {rtime, frame};
|
||||
|
||||
playback_queue_.append(f);
|
||||
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->append(f);
|
||||
}
|
||||
|
||||
if (!playback_speed_) {
|
||||
break;
|
||||
}
|
||||
|
||||
playback_frame_queue_next_frame_ += playback_speed_;
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
}
|
||||
playback_frame_queue_lock_.unlock();
|
||||
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->lock()->unlock();
|
||||
}
|
||||
playback_queue_.lock()->unlock();
|
||||
}
|
||||
|
||||
QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
|
||||
@@ -543,6 +567,19 @@ FramePtr ViewerWidget::DecodeCachedImage(const QString &fn)
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
|
||||
{
|
||||
display_widget_->SetImage(frame);
|
||||
|
||||
if (!main_only) {
|
||||
foreach (ViewerWindow* vw, windows_) {
|
||||
vw->display_widget()->SetImage(frame);
|
||||
}
|
||||
}
|
||||
|
||||
emit LoadedBuffer(frame.get());
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
{
|
||||
if (GetConnectedNode()
|
||||
@@ -616,10 +653,7 @@ void ViewerWidget::ContextMenuSetCustomSafeMargins()
|
||||
|
||||
void ViewerWidget::WindowAboutToClose()
|
||||
{
|
||||
ViewerWindow* vw = static_cast<ViewerWindow*>(sender());
|
||||
|
||||
windows_.removeAll(vw);
|
||||
gl_widgets_.removeAll(vw->gl_widget());
|
||||
windows_.removeOne(static_cast<ViewerWindow*>(sender()));
|
||||
}
|
||||
|
||||
void ViewerWidget::ContextMenuScopeTriggered(QAction *action)
|
||||
@@ -629,9 +663,7 @@ void ViewerWidget::ContextMenuScopeTriggered(QAction *action)
|
||||
|
||||
void ViewerWidget::RendererGeneratedFrame(FramePtr f)
|
||||
{
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->SetImageFromLoadBuffer(f.get());
|
||||
}
|
||||
SetDisplayImage(f, false);
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateRendererParameters()
|
||||
@@ -652,7 +684,7 @@ void ViewerWidget::UpdateRendererParameters()
|
||||
video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr);
|
||||
}
|
||||
|
||||
main_gl_widget()->SetVideoParams(vparam);
|
||||
display_widget_->SetVideoParams(vparam);
|
||||
|
||||
AudioRenderingParams aparam(GetConnectedNode()->audio_params(),
|
||||
SampleFormat::kInternalFormat);
|
||||
@@ -809,12 +841,18 @@ void ViewerWidget::Pause()
|
||||
if (playback_is_audio_only_) {
|
||||
disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
} else {
|
||||
disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
}
|
||||
|
||||
playback_frame_queue_lock_.lock();
|
||||
playback_frame_queue_.clear();
|
||||
playback_frame_queue_lock_.unlock();
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->Pause();
|
||||
}
|
||||
|
||||
{
|
||||
playback_queue_.lock()->lock();
|
||||
playback_queue_.clear();
|
||||
playback_queue_.lock()->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,13 +897,15 @@ void ViewerWidget::ShuttleRight()
|
||||
|
||||
void ViewerWidget::SetColorTransform(const ColorTransform &transform)
|
||||
{
|
||||
SetColorTransform(transform, main_gl_widget());
|
||||
SetColorTransform(transform, display_widget_);
|
||||
}
|
||||
|
||||
void ViewerWidget::SetSignalCursorColorEnabled(bool e)
|
||||
{
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->SetSignalCursorColorEnabled(e);
|
||||
display_widget_->SetSignalCursorColorEnabled(e);
|
||||
|
||||
foreach (ViewerWindow* vw, windows_) {
|
||||
vw->display_widget()->SetSignalCursorColorEnabled(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -881,11 +921,7 @@ void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
|
||||
|
||||
void ViewerWidget::PlaybackTimerUpdate()
|
||||
{
|
||||
int64_t real_time = QDateTime::currentMSecsSinceEpoch() - start_msec_;
|
||||
|
||||
int64_t frames_since_start = qRound(static_cast<double>(real_time) / (timebase_dbl() * 1000));
|
||||
|
||||
int64_t current_time = start_timestamp_ + frames_since_start * playback_speed_;
|
||||
int64_t current_time = playback_timer_.GetTimestampNow();
|
||||
|
||||
int64_t min_time, max_time;
|
||||
|
||||
|
||||
+12
-14
@@ -36,6 +36,8 @@
|
||||
#include "render/backend/opengl/opengltexture.h"
|
||||
#include "render/backend/audio/audiobackend.h"
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
#include "viewerqueue.h"
|
||||
#include "viewersizer.h"
|
||||
#include "viewerwindow.h"
|
||||
#include "widget/playbackcontrols/playbackcontrols.h"
|
||||
@@ -52,6 +54,8 @@ class ViewerWidget : public TimeBasedWidget
|
||||
public:
|
||||
ViewerWidget(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~ViewerWidget() override;
|
||||
|
||||
void SetPlaybackControlsEnabled(bool enabled);
|
||||
|
||||
void SetTimeRulerEnabled(bool enabled);
|
||||
@@ -116,7 +120,7 @@ signals:
|
||||
void CursorColor(const Color& reference, const Color& display);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::LoadedBuffer()
|
||||
* @brief Signal emitted when a new frame is loaded
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
@@ -154,8 +158,7 @@ protected:
|
||||
|
||||
PlaybackControls* controls_;
|
||||
|
||||
const QList<ViewerDisplayWidget *> &gl_widgets() const;
|
||||
ViewerDisplayWidget* main_gl_widget() const;
|
||||
ViewerDisplayWidget* display_widget() const;
|
||||
|
||||
private:
|
||||
void UpdateTimeInternal(int64_t i);
|
||||
@@ -180,13 +183,12 @@ private:
|
||||
|
||||
FramePtr DecodeCachedImage(const QString& fn);
|
||||
|
||||
void SetDisplayImage(FramePtr frame, bool main_only);
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
|
||||
qint64 start_msec_;
|
||||
int64_t start_timestamp_;
|
||||
|
||||
QAtomicInt playback_speed_;
|
||||
|
||||
qint64 frame_cache_job_time_;
|
||||
@@ -209,18 +211,14 @@ private:
|
||||
|
||||
QList<ViewerWindow*> windows_;
|
||||
|
||||
QList<ViewerDisplayWidget*> gl_widgets_;
|
||||
ViewerDisplayWidget* display_widget_;
|
||||
|
||||
ViewerDisplayWidget* context_menu_widget_;
|
||||
|
||||
struct PlaybackFrame {
|
||||
rational timestamp;
|
||||
FramePtr frame;
|
||||
};
|
||||
ViewerPlaybackTimer playback_timer_;
|
||||
|
||||
QMutex playback_frame_queue_lock_;
|
||||
QLinkedList<PlaybackFrame> playback_frame_queue_;
|
||||
int64_t playback_frame_queue_next_frame_;
|
||||
ViewerQueue playback_queue_;
|
||||
int64_t playback_queue_next_frame_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
@@ -44,10 +44,10 @@ bool ViewerDisplayWidget::nouveau_check_done_ = false;
|
||||
|
||||
ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
ManagedDisplayWidget(parent),
|
||||
has_image_(false),
|
||||
signal_cursor_color_(false),
|
||||
gizmos_(nullptr),
|
||||
gizmo_click_(false)
|
||||
gizmo_click_(false),
|
||||
last_loaded_buffer_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -68,18 +68,18 @@ void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e)
|
||||
setMouseTracking(e);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
void ViewerDisplayWidget::SetImage(FramePtr in_buffer)
|
||||
{
|
||||
has_image_ = in_buffer;
|
||||
last_loaded_buffer_ = in_buffer;
|
||||
|
||||
if (has_image_) {
|
||||
if (last_loaded_buffer_) {
|
||||
makeCurrent();
|
||||
|
||||
if (!texture_.IsCreated()
|
||||
|| texture_.width() != in_buffer->width()
|
||||
|| texture_.height() != in_buffer->height()
|
||||
|| texture_.format() != in_buffer->format()) {
|
||||
texture_.Create(context(), in_buffer->video_params(), in_buffer->data(), load_buffer_.linesize_pixels());
|
||||
texture_.Create(context(), in_buffer->video_params(), in_buffer->data(), in_buffer->linesize_pixels());
|
||||
} else {
|
||||
texture_.Upload(in_buffer);
|
||||
}
|
||||
@@ -88,14 +88,6 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
emit LoadedBuffer(in_buffer);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling)
|
||||
{
|
||||
connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection);
|
||||
sibling->SetImageFromLoadBuffer(&load_buffer_);
|
||||
}
|
||||
|
||||
const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const
|
||||
@@ -135,6 +127,11 @@ void ViewerDisplayWidget::SetTime(const rational &time)
|
||||
}
|
||||
}
|
||||
|
||||
FramePtr ViewerDisplayWidget::last_loaded_buffer() const
|
||||
{
|
||||
return last_loaded_buffer_;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (gizmos_
|
||||
@@ -161,17 +158,17 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
if (signal_cursor_color_) {
|
||||
Color reference, display;
|
||||
|
||||
if (has_image_) {
|
||||
if (last_loaded_buffer_) {
|
||||
QVector3D pixel_pos(static_cast<float>(event->x()) / static_cast<float>(width()) * 2.0f - 1.0f,
|
||||
static_cast<float>(event->y()) / static_cast<float>(height()) * 2.0f - 1.0f,
|
||||
0);
|
||||
|
||||
pixel_pos = pixel_pos * matrix_.inverted();
|
||||
|
||||
int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * load_buffer_.width());
|
||||
int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * load_buffer_.height());
|
||||
int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * last_loaded_buffer_->width());
|
||||
int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * last_loaded_buffer_->height());
|
||||
|
||||
reference = load_buffer_.get_pixel(frame_x, frame_y);
|
||||
reference = last_loaded_buffer_->get_pixel(frame_x, frame_y);
|
||||
display = color_service()->ConvertColor(reference);
|
||||
}
|
||||
|
||||
@@ -223,7 +220,7 @@ void ViewerDisplayWidget::paintGL()
|
||||
f->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// We only draw if we have a pipeline
|
||||
if (has_image_ && color_service()) {
|
||||
if (last_loaded_buffer_ && color_service()) {
|
||||
|
||||
// Bind retrieved texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, texture_.texture());
|
||||
|
||||
@@ -68,8 +68,6 @@ public:
|
||||
|
||||
const QMatrix4x4& GetMatrix();
|
||||
|
||||
void ConnectSibling(ViewerDisplayWidget* sibling);
|
||||
|
||||
const ViewerSafeMarginInfo& GetSafeMargin() const;
|
||||
void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin);
|
||||
|
||||
@@ -77,6 +75,8 @@ public:
|
||||
void SetVideoParams(const VideoRenderingParams& params);
|
||||
void SetTime(const rational& time);
|
||||
|
||||
FramePtr last_loaded_buffer() const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Set the transformation matrix to draw with
|
||||
@@ -100,7 +100,7 @@ public slots:
|
||||
* If there are multiple ViewerGLWidgets showing the same thing, this is faster than decoding the image from file
|
||||
* each time.
|
||||
*/
|
||||
void SetImageFromLoadBuffer(Frame* in_buffer);
|
||||
void SetImage(FramePtr in_buffer);
|
||||
|
||||
signals:
|
||||
/**
|
||||
@@ -113,15 +113,6 @@ signals:
|
||||
*/
|
||||
void CursorColor(const Color& reference, const Color& display);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a buffer is loaded from file into memory
|
||||
*
|
||||
* This buffer will be the direct output of the renderer in reference space in CPU memory.
|
||||
*
|
||||
* Connect this to the SetImageFromLoadBuffer() slot of another ViewerGLWidget to show the same thing
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override the mouse press event for the DragStarted() signal and gizmos
|
||||
@@ -169,17 +160,10 @@ private:
|
||||
*/
|
||||
QMatrix4x4 matrix_;
|
||||
|
||||
/**
|
||||
* @brief Buffer to load images into RAM before sending them to the display
|
||||
*/
|
||||
Frame load_buffer_;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
static bool nouveau_check_done_;
|
||||
#endif
|
||||
|
||||
bool has_image_;
|
||||
|
||||
bool signal_cursor_color_;
|
||||
|
||||
ViewerSafeMarginInfo safe_margin_;
|
||||
@@ -192,6 +176,8 @@ private:
|
||||
|
||||
rational time_;
|
||||
|
||||
FramePtr last_loaded_buffer_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot to connect just before the OpenGL context is destroyed to clean up resources
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "viewerplaybacktimer.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
void ViewerPlaybackTimer::Start(const int64_t &start_timestamp, const int &playback_speed, const double &timebase)
|
||||
{
|
||||
start_msec_ = QDateTime::currentMSecsSinceEpoch();
|
||||
start_timestamp_ = start_timestamp;
|
||||
playback_speed_ = playback_speed;
|
||||
timebase_ = timebase;
|
||||
}
|
||||
|
||||
int64_t ViewerPlaybackTimer::GetTimestampNow() const
|
||||
{
|
||||
int64_t real_time = QDateTime::currentMSecsSinceEpoch() - start_msec_;
|
||||
|
||||
int64_t frames_since_start = qRound(static_cast<double>(real_time) / (timebase_ * 1000));
|
||||
|
||||
return start_timestamp_ + frames_since_start * playback_speed_;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,48 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VIEWERPLAYBACKTIMER_H
|
||||
#define VIEWERPLAYBACKTIMER_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class ViewerPlaybackTimer {
|
||||
public:
|
||||
void Start(const int64_t& start_timestamp, const int& playback_speed, const double& timebase);
|
||||
|
||||
int64_t GetTimestampNow() const;
|
||||
|
||||
private:
|
||||
qint64 start_msec_;
|
||||
int64_t start_timestamp_;
|
||||
|
||||
int playback_speed_;
|
||||
|
||||
double timebase_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // VIEWERPLAYBACKTIMER_H
|
||||
@@ -0,0 +1,50 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VIEWERQUEUE_H
|
||||
#define VIEWERQUEUE_H
|
||||
|
||||
#include <QLinkedList>
|
||||
|
||||
#include "codec/frame.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
struct ViewerPlaybackFrame {
|
||||
rational timestamp;
|
||||
FramePtr frame;
|
||||
};
|
||||
|
||||
class ViewerQueue : public QLinkedList<ViewerPlaybackFrame> {
|
||||
public:
|
||||
ViewerQueue() = default;
|
||||
|
||||
QMutex* lock() {
|
||||
return &queue_lock_;
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex queue_lock_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // VIEWERQUEUE_H
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/timecodefunctions.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ViewerWindow::ViewerWindow(QWidget *parent) :
|
||||
@@ -32,13 +34,13 @@ ViewerWindow::ViewerWindow(QWidget *parent) :
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
gl_widget_ = new ViewerDisplayWidget();
|
||||
layout->addWidget(gl_widget_);
|
||||
display_widget_ = new ViewerDisplayWidget();
|
||||
layout->addWidget(display_widget_);
|
||||
}
|
||||
|
||||
ViewerDisplayWidget *ViewerWindow::gl_widget() const
|
||||
ViewerDisplayWidget *ViewerWindow::display_widget() const
|
||||
{
|
||||
return gl_widget_;
|
||||
return display_widget_;
|
||||
}
|
||||
|
||||
void ViewerWindow::SetResolution(int width, int height)
|
||||
@@ -57,7 +59,26 @@ void ViewerWindow::SetResolution(int width, int height)
|
||||
mat.scale(1.0f, window_ar / image_ar, 1.0f);
|
||||
}
|
||||
|
||||
gl_widget_->SetMatrix(mat);
|
||||
display_widget_->SetMatrix(mat);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
QMutexLocker locker(queue_.lock());
|
||||
queue_.clear();
|
||||
}
|
||||
|
||||
void ViewerWindow::keyPressEvent(QKeyEvent *e)
|
||||
@@ -76,4 +97,24 @@ void ViewerWindow::closeEvent(QCloseEvent *e)
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void ViewerWindow::UpdateFromQueue()
|
||||
{
|
||||
int64_t t = timer_.GetTimestampNow();
|
||||
|
||||
rational time = Timecode::timestamp_to_time(t, playback_timebase_);
|
||||
|
||||
QMutexLocker locker(queue_.lock());
|
||||
while (!queue_.isEmpty()) {
|
||||
const ViewerPlaybackFrame& pf = queue_.first();
|
||||
|
||||
if (pf.timestamp == time) {
|
||||
// Frame was in queue, no need to decode anything
|
||||
display_widget_->SetImage(pf.frame);
|
||||
return;
|
||||
} else {
|
||||
queue_.removeFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
#include "viewerqueue.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -32,20 +34,37 @@ class ViewerWindow : public QWidget
|
||||
public:
|
||||
ViewerWindow(QWidget* parent = nullptr);
|
||||
|
||||
ViewerDisplayWidget* gl_widget() const;
|
||||
ViewerDisplayWidget* display_widget() const;
|
||||
|
||||
/**
|
||||
* @brief Used to adjust resulting picture to be the right aspect ratio
|
||||
*/
|
||||
void SetResolution(int width, int height);
|
||||
|
||||
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:
|
||||
ViewerDisplayWidget* gl_widget_;
|
||||
ViewerDisplayWidget* display_widget_;
|
||||
|
||||
ViewerQueue queue_;
|
||||
|
||||
ViewerPlaybackTimer timer_;
|
||||
|
||||
rational playback_timebase_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user