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"]
path = ext/KDDockWidgets
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)
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_DOXYGEN "Build Doxygen documentation" OFF)
option(BUILD_TESTS "Build unit tests" OFF)
@@ -168,7 +172,7 @@ list(APPEND OLIVE_LIBRARIES
)
# Link FFmpeg
find_package(FFMPEG 3.0 REQUIRED
find_package(FFMPEG REQUIRED
COMPONENTS
avutil
avcodec
+14 -7
View File
@@ -140,7 +140,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
break;
}
AVFrame *hw_in = f.get();
AVFramePtr hw_in = f;
VideoParams plane_params = vp;
plane_params.set_channel_count(1);
@@ -148,7 +148,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr y_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
y_plane->handleFrame(hw_in);
switch (f->format) {
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUV422P:
@@ -170,8 +170,11 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr u_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
u_plane->handleFrame(hw_in);
TexturePtr v_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
v_plane->handleFrame(hw_in);
ShaderJob job;
job.Insert(QStringLiteral("y_channel"),
@@ -207,6 +210,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGBA64LE:
// RGBA can be uploaded directly to the texture
tex->handleFrame(f);
tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel());
break;
}
@@ -282,14 +286,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
AVCOL_RANGE_MPEG;
// Perform any CPU processing required
f = PreProcessFrame(f, p);
AVFramePtr ptr = PreProcessFrame(f, p);
f=std::move(ptr);
if (!f) {
// Error occurred while software scaling
return nullptr;
}
// Finally, perform any GPU processing required
return ProcessFrameIntoTexture(f, p, original);
TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
return texture;
}
return nullptr;
@@ -799,7 +806,7 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
dest->format = f->format;
dest->color_range = f->color_range;
dest->colorspace = f->colorspace;
dest->hw_frames_ctx = nullptr;
if (p.divider > 1) {
dest->width = VideoParams::GetScaledDimension(dest->width, 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);
}
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data,
dest->linesize);
r = sws_scale_frame(sws_ctx_, dest.get(), f.get());
if (r < 0) {
FFmpegError(r);
return nullptr;
+1 -1
View File
@@ -87,7 +87,7 @@ public:
using AVFramePtr = std::shared_ptr<AVFrame>;
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()
{
+1
View File
@@ -776,6 +776,7 @@ void Core::StartGUI(bool full_screen)
connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
&PanelManager::FocusChanged);
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
// Set KDDockWidgets flags
auto &config = KDDockWidgets::Config::self();
auto flags = config.flags();
+1
View File
@@ -145,6 +145,7 @@ int decompress_project(const QString &project)
int main(int argc, char *argv[])
{
// Set up debug handler
qInstallMessageHandler(olive::DebugHandler);
+15 -3
View File
@@ -27,23 +27,26 @@
#include <QStyle>
#include <QStyleOption>
#include <QVariant>
#include "Window_p.h"
#include "panel/panelmanager.h"
#include <OpenImageIO/hash.h>
namespace olive
{
#define super KDDockWidgets::DockWidget
#define super KDDockWidgets::QtWidgets::DockWidget
PanelWidget::PanelWidget(const QString &object_name)
: super(object_name)
, border_visible_(false)
, signal_instead_of_close_(false)
{
setFocusPolicy(Qt::ClickFocus);
View<QWidget>::setFocusPolicy(Qt::ClickFocus);
connect(this, &PanelWidget::shown, this,
static_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
reinterpret_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
PanelManager::instance()->RegisterPanel(this);
}
@@ -125,6 +128,15 @@ void PanelWidget::changeEvent(QEvent *e)
if (e->type() == QEvent::LanguageChange) {
Retranslate();
}
if (e->type() == QEvent::WindowStateChange) {
if (isVisible() && !isMinimized()) {
emit shown(Qt::OtherFocusReason);
}
else {
emit hidden();
}
}
super::changeEvent(e);
}
+13 -3
View File
@@ -21,18 +21,23 @@
#ifndef 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 <QEvent>
#include "common/define.h"
#include <QTabWidget>
namespace olive
{
/**
* @brief A widget that is always dockable within the MainWindow.
*/
class PanelWidget : public KDDockWidgets::DockWidget {
class PanelWidget : public KDDockWidgets::QtWidgets::DockWidget {
Q_OBJECT
public:
/**
@@ -279,7 +284,8 @@ public:
signals:
void CloseRequested();
void shown(Qt::FocusReason reason);
void hidden();
protected:
/**
* @brief paintEvent
@@ -323,7 +329,7 @@ protected slots:
* String to set the subtitle to
*/
void SetSubtitle(const QString &t);
protected slots:
private:
/**
* @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change.
@@ -339,6 +345,10 @@ private:
bool border_visible_;
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>();
panel->ConnectViewerNode(static_cast<Footage *>(item));
panel->raise();
panel->setFocus();
panel->setFocus(Qt::FocusReason::MouseFocusReason);
} else if (dynamic_cast<Sequence *>(item)) {
// Open this sequence in the Timeline
Core::instance()->main_window()->OpenSequence(
+8 -1
View File
@@ -21,6 +21,8 @@
#ifndef RENDERTEXTURE_H
#define RENDERTEXTURE_H
#include "common/ffmpegutils.h"
#include <memory>
#include <QVariant>
@@ -150,7 +152,10 @@ public:
{
return job_;
}
void handleFrame(AVFramePtr ptr)
{
frame_=ptr;
}
private:
Renderer *renderer_;
@@ -159,6 +164,8 @@ private:
QVariant id_;
AcceleratedJob *job_;
AVFramePtr frame_;
};
}
+5 -2
View File
@@ -1203,8 +1203,11 @@ void ViewerWidget::UpdateMinimumScale()
// Avoids divide by zero
SetMinimumScale(0);
} else {
SetMinimumScale(static_cast<double>(ruler()->width()) /
GetConnectedNode()->GetLength().toDouble());
double min_scale = static_cast<double>(ruler()->width()) /
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>
#endif
#include "KDDockWidgets/src/qtwidgets/Window_p.h"
#include "dialog/about/about.h"
#include "mainmenu.h"
#include "mainstatusbar.h"
#include "KDDockWidgets/src/LayoutSaver.h"
#include "timeline/timelineundoworkarea.h"
namespace olive
{
#define super KDDockWidgets::MainWindow
#define super KDDockWidgets::QtWidgets::MainWindow
MainWindow::MainWindow(QWidget *parent)
: super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None,
@@ -95,7 +97,7 @@ MainWindow::MainWindow(QWidget *parent)
// emit the "shown" signal before emitting the "hidden" signals, resulting
// in Core thinking there are -1 pixel samplers open. To mitigate that,
// we force "shown" to emit ourselves here.
emit pixel_sampler_panel_->shown();
emit pixel_sampler_panel_->shown(Qt::OtherFocusReason);
// Make node-related connections
connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts,
@@ -382,7 +384,7 @@ void MainWindow::ToggleMaximizedPanel()
premaximized_state_.clear();
currently_focused_panel->raise();
currently_focused_panel->setFocus();
currently_focused_panel->setFocus(Qt::ActiveWindowFocusReason);
PanelManager::instance()->SetSuppressChangedSignal(false);
}
@@ -430,7 +432,7 @@ void MainWindow::SetProject(Project *p)
project_panel_->set_project(p);
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.
*/
class MainWindow : public KDDockWidgets::MainWindow {
class MainWindow : public KDDockWidgets::QtWidgets::MainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);