Update FFmpeg handling and focus management

Refactor FFmpeg frame processing and improve focus management in panels. Add memory sanitization for debug builds and update KDDockWidgets integration.
This commit is contained in:
2025-11-23 16:35:09 +08:00
parent 2ea53667c0
commit 1e33a31b73
15 changed files with 75 additions and 27 deletions
+1
View File
@@ -5,3 +5,4 @@
[submodule "ext/KDDockWidgets"] [submodule "ext/KDDockWidgets"]
path = ext/KDDockWidgets path = ext/KDDockWidgets
url = https://github.com/olive-editor/KDDockWidgets.git url = https://github.com/olive-editor/KDDockWidgets.git
branch = main
+6 -2
View File
@@ -17,7 +17,11 @@
cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(olive-editor VERSION 0.2.0 LANGUAGES CXX) project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
if(${CMAKE_BUILD_TYPE} EQUAL Debug)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory)
endif ()
option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON) option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON)
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF) option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
option(BUILD_TESTS "Build unit tests" OFF) option(BUILD_TESTS "Build unit tests" OFF)
@@ -168,7 +172,7 @@ list(APPEND OLIVE_LIBRARIES
) )
# Link FFmpeg # Link FFmpeg
find_package(FFMPEG 3.0 REQUIRED find_package(FFMPEG REQUIRED
COMPONENTS COMPONENTS
avutil avutil
avcodec avcodec
+14 -7
View File
@@ -140,7 +140,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
break; break;
} }
AVFrame *hw_in = f.get(); AVFramePtr hw_in = f;
VideoParams plane_params = vp; VideoParams plane_params = vp;
plane_params.set_channel_count(1); plane_params.set_channel_count(1);
@@ -148,7 +148,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr y_plane = p.renderer->CreateTexture( TexturePtr y_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size); plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
y_plane->handleFrame(hw_in);
switch (f->format) { switch (f->format) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV422P:
@@ -170,8 +170,11 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr u_plane = p.renderer->CreateTexture( TexturePtr u_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[1], hw_in->linesize[1] / px_size); plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
u_plane->handleFrame(hw_in);
TexturePtr v_plane = p.renderer->CreateTexture( TexturePtr v_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size); plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
v_plane->handleFrame(hw_in);
ShaderJob job; ShaderJob job;
job.Insert(QStringLiteral("y_channel"), job.Insert(QStringLiteral("y_channel"),
@@ -207,6 +210,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
case AV_PIX_FMT_RGBA: case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGBA64LE: case AV_PIX_FMT_RGBA64LE:
// RGBA can be uploaded directly to the texture // RGBA can be uploaded directly to the texture
tex->handleFrame(f);
tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel()); tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel());
break; break;
} }
@@ -282,14 +286,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
AVCOL_RANGE_MPEG; AVCOL_RANGE_MPEG;
// Perform any CPU processing required // Perform any CPU processing required
f = PreProcessFrame(f, p); AVFramePtr ptr = PreProcessFrame(f, p);
f=std::move(ptr);
if (!f) { if (!f) {
// Error occurred while software scaling // Error occurred while software scaling
return nullptr; return nullptr;
} }
// Finally, perform any GPU processing required // Finally, perform any GPU processing required
return ProcessFrameIntoTexture(f, p, original); TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
return texture;
} }
return nullptr; return nullptr;
@@ -799,7 +806,7 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
dest->format = f->format; dest->format = f->format;
dest->color_range = f->color_range; dest->color_range = f->color_range;
dest->colorspace = f->colorspace; dest->colorspace = f->colorspace;
dest->hw_frames_ctx = nullptr;
if (p.divider > 1) { if (p.divider > 1) {
dest->width = VideoParams::GetScaledDimension(dest->width, p.divider); dest->width = VideoParams::GetScaledDimension(dest->width, p.divider);
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider); dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
@@ -852,8 +859,8 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000); dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000);
} }
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data, r = sws_scale_frame(sws_ctx_, dest.get(), f.get());
dest->linesize);
if (r < 0) { if (r < 0) {
FFmpegError(r); FFmpegError(r);
return nullptr; return nullptr;
+1 -1
View File
@@ -87,7 +87,7 @@ public:
using AVFramePtr = std::shared_ptr<AVFrame>; using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f) inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{ {
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); }); return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
} }
inline AVFramePtr CreateAVFramePtr() inline AVFramePtr CreateAVFramePtr()
{ {
+1
View File
@@ -776,6 +776,7 @@ void Core::StartGUI(bool full_screen)
connect(qApp, &QApplication::focusChanged, PanelManager::instance(), connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
&PanelManager::FocusChanged); &PanelManager::FocusChanged);
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
// Set KDDockWidgets flags // Set KDDockWidgets flags
auto &config = KDDockWidgets::Config::self(); auto &config = KDDockWidgets::Config::self();
auto flags = config.flags(); auto flags = config.flags();
+1
View File
@@ -145,6 +145,7 @@ int decompress_project(const QString &project)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Set up debug handler // Set up debug handler
qInstallMessageHandler(olive::DebugHandler); qInstallMessageHandler(olive::DebugHandler);
+15 -3
View File
@@ -27,23 +27,26 @@
#include <QStyle> #include <QStyle>
#include <QStyleOption> #include <QStyleOption>
#include <QVariant> #include <QVariant>
#include "Window_p.h"
#include "panel/panelmanager.h" #include "panel/panelmanager.h"
#include <OpenImageIO/hash.h>
namespace olive namespace olive
{ {
#define super KDDockWidgets::DockWidget #define super KDDockWidgets::QtWidgets::DockWidget
PanelWidget::PanelWidget(const QString &object_name) PanelWidget::PanelWidget(const QString &object_name)
: super(object_name) : super(object_name)
, border_visible_(false) , border_visible_(false)
, signal_instead_of_close_(false) , signal_instead_of_close_(false)
{ {
setFocusPolicy(Qt::ClickFocus); View<QWidget>::setFocusPolicy(Qt::ClickFocus);
connect(this, &PanelWidget::shown, this, connect(this, &PanelWidget::shown, this,
static_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus)); reinterpret_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
PanelManager::instance()->RegisterPanel(this); PanelManager::instance()->RegisterPanel(this);
} }
@@ -125,6 +128,15 @@ void PanelWidget::changeEvent(QEvent *e)
if (e->type() == QEvent::LanguageChange) { if (e->type() == QEvent::LanguageChange) {
Retranslate(); Retranslate();
} }
if (e->type() == QEvent::WindowStateChange) {
if (isVisible() && !isMinimized()) {
emit shown(Qt::OtherFocusReason);
}
else {
emit hidden();
}
}
super::changeEvent(e); super::changeEvent(e);
} }
+13 -3
View File
@@ -21,18 +21,23 @@
#ifndef PANEL_WIDGET_H #ifndef PANEL_WIDGET_H
#define PANEL_WIDGET_H #define PANEL_WIDGET_H
#include "KDDockWidgets/src/core/Window_p.h"
#include "KDDockWidgets/src/qtwidgets/views/TabBar.h"
#include <kddockwidgets/DockWidget.h> #include <kddockwidgets/DockWidget.h>
#include <QEvent> #include <QEvent>
#include "common/define.h" #include "common/define.h"
#include <QTabWidget>
namespace olive namespace olive
{ {
/** /**
* @brief A widget that is always dockable within the MainWindow. * @brief A widget that is always dockable within the MainWindow.
*/ */
class PanelWidget : public KDDockWidgets::DockWidget { class PanelWidget : public KDDockWidgets::QtWidgets::DockWidget {
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -279,7 +284,8 @@ public:
signals: signals:
void CloseRequested(); void CloseRequested();
void shown(Qt::FocusReason reason);
void hidden();
protected: protected:
/** /**
* @brief paintEvent * @brief paintEvent
@@ -323,7 +329,7 @@ protected slots:
* String to set the subtitle to * String to set the subtitle to
*/ */
void SetSubtitle(const QString &t); void SetSubtitle(const QString &t);
protected slots:
private: private:
/** /**
* @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change. * @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change.
@@ -339,6 +345,10 @@ private:
bool border_visible_; bool border_visible_;
bool signal_instead_of_close_; bool signal_instead_of_close_;
QMetaObject::Connection m_tabBarConnection;
QMetaObject::Connection m_windowConnection;
bool m_lastVisibleState = false;
}; };
} }
+1 -1
View File
@@ -180,7 +180,7 @@ void ProjectPanel::ItemDoubleClickSlot(Node *item)
PanelManager::instance()->MostRecentlyFocused<FootageViewerPanel>(); PanelManager::instance()->MostRecentlyFocused<FootageViewerPanel>();
panel->ConnectViewerNode(static_cast<Footage *>(item)); panel->ConnectViewerNode(static_cast<Footage *>(item));
panel->raise(); panel->raise();
panel->setFocus(); panel->setFocus(Qt::FocusReason::MouseFocusReason);
} else if (dynamic_cast<Sequence *>(item)) { } else if (dynamic_cast<Sequence *>(item)) {
// Open this sequence in the Timeline // Open this sequence in the Timeline
Core::instance()->main_window()->OpenSequence( Core::instance()->main_window()->OpenSequence(
+8 -1
View File
@@ -21,6 +21,8 @@
#ifndef RENDERTEXTURE_H #ifndef RENDERTEXTURE_H
#define RENDERTEXTURE_H #define RENDERTEXTURE_H
#include "common/ffmpegutils.h"
#include <memory> #include <memory>
#include <QVariant> #include <QVariant>
@@ -150,7 +152,10 @@ public:
{ {
return job_; return job_;
} }
void handleFrame(AVFramePtr ptr)
{
frame_=ptr;
}
private: private:
Renderer *renderer_; Renderer *renderer_;
@@ -159,6 +164,8 @@ private:
QVariant id_; QVariant id_;
AcceleratedJob *job_; AcceleratedJob *job_;
AVFramePtr frame_;
}; };
} }
+5 -2
View File
@@ -1203,8 +1203,11 @@ void ViewerWidget::UpdateMinimumScale()
// Avoids divide by zero // Avoids divide by zero
SetMinimumScale(0); SetMinimumScale(0);
} else { } else {
SetMinimumScale(static_cast<double>(ruler()->width()) / double min_scale = static_cast<double>(ruler()->width()) /
GetConnectedNode()->GetLength().toDouble()); GetConnectedNode()->GetLength().toDouble();
// Ensure min_scale doesn't exceed max_scale to prevent crash
min_scale = qMin(min_scale, GetMaximumScale());
SetMinimumScale(min_scale);
} }
} }
+6 -4
View File
@@ -29,15 +29,17 @@
#include <QOffscreenSurface> #include <QOffscreenSurface>
#endif #endif
#include "KDDockWidgets/src/qtwidgets/Window_p.h"
#include "dialog/about/about.h" #include "dialog/about/about.h"
#include "mainmenu.h" #include "mainmenu.h"
#include "mainstatusbar.h" #include "mainstatusbar.h"
#include "KDDockWidgets/src/LayoutSaver.h"
#include "timeline/timelineundoworkarea.h" #include "timeline/timelineundoworkarea.h"
namespace olive namespace olive
{ {
#define super KDDockWidgets::MainWindow #define super KDDockWidgets::QtWidgets::MainWindow
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None, : super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None,
@@ -95,7 +97,7 @@ MainWindow::MainWindow(QWidget *parent)
// emit the "shown" signal before emitting the "hidden" signals, resulting // emit the "shown" signal before emitting the "hidden" signals, resulting
// in Core thinking there are -1 pixel samplers open. To mitigate that, // in Core thinking there are -1 pixel samplers open. To mitigate that,
// we force "shown" to emit ourselves here. // we force "shown" to emit ourselves here.
emit pixel_sampler_panel_->shown(); emit pixel_sampler_panel_->shown(Qt::OtherFocusReason);
// Make node-related connections // Make node-related connections
connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts, connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts,
@@ -382,7 +384,7 @@ void MainWindow::ToggleMaximizedPanel()
premaximized_state_.clear(); premaximized_state_.clear();
currently_focused_panel->raise(); currently_focused_panel->raise();
currently_focused_panel->setFocus(); currently_focused_panel->setFocus(Qt::ActiveWindowFocusReason);
PanelManager::instance()->SetSuppressChangedSignal(false); PanelManager::instance()->SetSuppressChangedSignal(false);
} }
@@ -430,7 +432,7 @@ void MainWindow::SetProject(Project *p)
project_panel_->set_project(p); project_panel_->set_project(p);
if (project_) { if (project_) {
project_panel_->setFocus(); project_panel_->setFocus(Qt::OtherFocusReason);
} }
} }
+1 -1
View File
@@ -53,7 +53,7 @@ namespace olive
/** /**
* @brief Olive's main window responsible for docking widgets and the main menu bar. * @brief Olive's main window responsible for docking widgets and the main menu bar.
*/ */
class MainWindow : public KDDockWidgets::MainWindow { class MainWindow : public KDDockWidgets::QtWidgets::MainWindow {
Q_OBJECT Q_OBJECT
public: public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);