Merge branch 'refs/heads/dev' into plugin
# Conflicts: # .gitmodules # CMakeLists.txt # ext/core
This commit is contained in:
+6
-4
@@ -1,6 +1,8 @@
|
|||||||
[submodule "ext/KDDockWidgets"]
|
|
||||||
path = ext/KDDockWidgets
|
|
||||||
url = https://github.com/olive-editor/KDDockWidgets.git
|
|
||||||
[submodule "ext/core"]
|
[submodule "ext/core"]
|
||||||
path = ext/core
|
path = ext/core
|
||||||
url = https://github.com/olive-editor/core
|
url = https://github.com/OliveCommunity/core.git
|
||||||
|
branch = dev
|
||||||
|
[submodule "ext/KDDockWidgets"]
|
||||||
|
path = ext/KDDockWidgets
|
||||||
|
url = https://github.com/OliveCommunity/KDDockWidgets.git
|
||||||
|
branch = main
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
From 6b0ef44eb189411d36c739ccde8a081a3e62034a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mike Solar <iam@mikesolar.com>
|
||||||
|
Date: Mon, 24 Nov 2025 20:57:12 +0800
|
||||||
|
Subject: [PATCH] Fix include path for Window_p.h in qtcommon directory
|
||||||
|
|
||||||
|
---
|
||||||
|
src/qtcommon/Window_p.h | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/src/qtcommon/Window_p.h b/src/qtcommon/Window_p.h
|
||||||
|
index f29b209a5..d1d6b47ad 100644
|
||||||
|
--- a/src/qtcommon/Window_p.h
|
||||||
|
+++ b/src/qtcommon/Window_p.h
|
||||||
|
@@ -11,7 +11,7 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
-#include "core/Window_p.h"
|
||||||
|
+#include "../core/Window_p.h"
|
||||||
|
#include "Screen_p.h"
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
|
--
|
||||||
|
2.52.0
|
||||||
|
|
||||||
+9
-11
@@ -14,16 +14,14 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.30 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_CXX_STANDARD 23)
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g")
|
||||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory)
|
||||||
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD ON)
|
endif ()
|
||||||
find_package(expat REQUIRED)
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -37,11 +35,10 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
|
||||||
# Generates a compile_commands.json in the build dir, link that to the repo
|
# Generates a compile_commands.json in the build dir, link that to the repo
|
||||||
# root to enrich your IDE with clangd language server protocol functionalities
|
# root to enrich your IDE with clangd language server protocol functionalities
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
add_subdirectory(third_party/openfx/HostSupport)
|
|
||||||
# Sanitizers
|
# Sanitizers
|
||||||
add_library(olive-sanitizers INTERFACE)
|
add_library(olive-sanitizers INTERFACE)
|
||||||
include(cmake/Sanitizers.cmake)
|
include(cmake/Sanitizers.cmake)
|
||||||
@@ -110,6 +107,7 @@ list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES})
|
|||||||
list(APPEND OLIVE_LIBRARIES olivecore)
|
list(APPEND OLIVE_LIBRARIES olivecore)
|
||||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include)
|
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include)
|
||||||
|
|
||||||
|
|
||||||
# Link Qt
|
# Link Qt
|
||||||
set(QT_LIBRARIES
|
set(QT_LIBRARIES
|
||||||
Core
|
Core
|
||||||
@@ -175,7 +173,7 @@ list(APPEND OLIVE_LIBRARIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Link FFmpeg
|
# Link FFmpeg
|
||||||
find_package(FFMPEG 7.0 REQUIRED
|
find_package(FFMPEG REQUIRED
|
||||||
COMPONENTS
|
COMPONENTS
|
||||||
avutil
|
avutil
|
||||||
avcodec
|
avcodec
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ extern "C" {
|
|||||||
#include <libavutil/imgutils.h>
|
#include <libavutil/imgutils.h>
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
|
#include <libavutil/frame.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <OpenImageIO/imagebuf.h>
|
#include <OpenImageIO/imagebuf.h>
|
||||||
@@ -137,7 +138,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);
|
||||||
@@ -145,7 +146,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:
|
||||||
@@ -167,8 +168,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"),
|
||||||
@@ -204,6 +208,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;
|
||||||
}
|
}
|
||||||
@@ -279,14 +284,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;
|
||||||
@@ -380,116 +388,77 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
|
|||||||
VideoParams::kInterlaceNone;
|
VideoParams::kInterlaceNone;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Read at least two frames to get more information about this video stream
|
// Read at least two frames to get more information about this video stream
|
||||||
AVPacket *pkt = av_packet_alloc();
|
AVPacket *pkt = av_packet_alloc();
|
||||||
AVFrame *frame = av_frame_alloc();
|
AVFrame *frame = av_frame_alloc();
|
||||||
|
|
||||||
{
|
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
|
||||||
Instance instance;
|
AVRational pixel_aspect_ratio = {1, 1};
|
||||||
instance.Open(filename_c, avstream->index);
|
AVRational frame_rate = avstream->avg_frame_rate;
|
||||||
|
AVPixelFormat compatible_pix_fmt =
|
||||||
|
FFmpegUtils::GetCompatiblePixelFormat(
|
||||||
|
static_cast<AVPixelFormat>(avstream->codecpar->format));
|
||||||
|
bool image_is_still = false;
|
||||||
|
|
||||||
// Read first frame and retrieve some metadata
|
{
|
||||||
if (instance.GetFrame(pkt, frame) >= 0) {
|
Instance instance;
|
||||||
// Check if video is interlaced and what field dominance it has if so
|
if (instance.Open(filename_c, avstream->index) != 0)
|
||||||
if (frame->interlaced_frame) {
|
goto cleanup;
|
||||||
if (frame->top_field_first) {
|
|
||||||
interlacing =
|
|
||||||
VideoParams::kInterlacedTopFirst;
|
|
||||||
} else {
|
|
||||||
interlacing =
|
|
||||||
VideoParams::kInterlacedBottomFirst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pixel_aspect_ratio =
|
AVCodecContext *avctx = instance.codec_ctx();
|
||||||
av_guess_sample_aspect_ratio(
|
interlacing = FFmpegFieldOrderToOlive(avctx->field_order);
|
||||||
instance.fmt_ctx(), instance.avstream(),
|
|
||||||
frame);
|
|
||||||
|
|
||||||
frame_rate = av_guess_frame_rate(
|
if (instance.GetFrame(pkt, frame) >= 0) {
|
||||||
instance.fmt_ctx(), instance.avstream(),
|
pixel_aspect_ratio =
|
||||||
frame);
|
av_guess_sample_aspect_ratio(instance.fmt_ctx(),
|
||||||
|
instance.avstream(), frame);
|
||||||
|
frame_rate =
|
||||||
|
av_guess_frame_rate(instance.fmt_ctx(),
|
||||||
|
instance.avstream(), frame);
|
||||||
|
}
|
||||||
|
|
||||||
compatible_pix_fmt =
|
int ret = instance.GetFrame(pkt, frame);
|
||||||
FFmpegUtils::GetCompatiblePixelFormat(
|
if (ret == AVERROR_EOF) {
|
||||||
static_cast<AVPixelFormat>(
|
image_is_still = true;
|
||||||
avstream->codecpar->format));
|
} else if (avstream->duration == AV_NOPTS_VALUE ||
|
||||||
}
|
duration_guessed_from_bitrate) {
|
||||||
|
int64_t last_ts = frame->best_effort_timestamp;
|
||||||
|
while (instance.GetFrame(pkt, frame) >= 0 &&
|
||||||
|
(!cancelled || !cancelled->IsCancelled()))
|
||||||
|
last_ts = frame->best_effort_timestamp;
|
||||||
|
avstream->duration = last_ts;
|
||||||
|
}
|
||||||
|
|
||||||
// Read second frame
|
instance.Close();
|
||||||
int ret = instance.GetFrame(pkt, frame);
|
}
|
||||||
|
|
||||||
if (ret >= 0) {
|
cleanup:
|
||||||
// Check if we need a manual duration
|
av_frame_free(&frame);
|
||||||
if (avstream->duration == AV_NOPTS_VALUE ||
|
av_packet_free(&pkt);
|
||||||
duration_guessed_from_bitrate) {
|
|
||||||
if (footage_duration == AV_NOPTS_VALUE ||
|
|
||||||
duration_guessed_from_bitrate) {
|
|
||||||
// Manually read through file for duration
|
|
||||||
int64_t new_dur;
|
|
||||||
|
|
||||||
do {
|
VideoParams stream;
|
||||||
new_dur =
|
stream.set_stream_index(i);
|
||||||
frame->best_effort_timestamp;
|
stream.set_width(avstream->codecpar->width);
|
||||||
} while (instance.GetFrame(
|
stream.set_height(avstream->codecpar->height);
|
||||||
pkt, frame) >= 0 &&
|
stream.set_video_type(image_is_still ? VideoParams::kVideoTypeStill
|
||||||
(!cancelled ||
|
: VideoParams::kVideoTypeVideo);
|
||||||
!cancelled->IsCancelled()));
|
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
||||||
|
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
|
||||||
|
stream.set_interlacing(interlacing); // <-- 已正确填充
|
||||||
|
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
||||||
|
stream.set_frame_rate(frame_rate);
|
||||||
|
stream.set_start_time(avstream->start_time);
|
||||||
|
stream.set_time_base(avstream->time_base);
|
||||||
|
stream.set_duration(avstream->duration);
|
||||||
|
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG
|
||||||
|
? VideoParams::kColorRangeFull
|
||||||
|
: VideoParams::kColorRangeLimited);
|
||||||
|
stream.set_premultiplied_alpha(false);
|
||||||
|
|
||||||
avstream->duration = new_dur;
|
desc.AddVideoStream(stream);
|
||||||
|
image_is_still ? still_streams++ : video_streams++;
|
||||||
} else {
|
|
||||||
// Fallback to footage duration
|
|
||||||
avstream->duration =
|
|
||||||
Timecode::rescale_timestamp_ceil(
|
|
||||||
footage_duration,
|
|
||||||
rational(1, AV_TIME_BASE),
|
|
||||||
avstream->time_base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ret == AVERROR_EOF) {
|
|
||||||
// Video has only one frame in it, treat it like a still image
|
|
||||||
image_is_still = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
av_frame_free(&frame);
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams stream;
|
|
||||||
stream.set_stream_index(i);
|
|
||||||
stream.set_width(avstream->codecpar->width);
|
|
||||||
stream.set_height(avstream->codecpar->height);
|
|
||||||
stream.set_video_type((image_is_still) ?
|
|
||||||
VideoParams::kVideoTypeStill :
|
|
||||||
VideoParams::kVideoTypeVideo);
|
|
||||||
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
|
||||||
stream.set_channel_count(
|
|
||||||
GetNativeChannelCount(compatible_pix_fmt));
|
|
||||||
stream.set_interlacing(interlacing);
|
|
||||||
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
|
||||||
stream.set_frame_rate(frame_rate);
|
|
||||||
stream.set_start_time(avstream->start_time);
|
|
||||||
stream.set_time_base(avstream->time_base);
|
|
||||||
stream.set_duration(avstream->duration);
|
|
||||||
stream.set_color_range(avstream->codecpar->color_range ==
|
|
||||||
AVCOL_RANGE_JPEG ?
|
|
||||||
VideoParams::kColorRangeFull :
|
|
||||||
VideoParams::kColorRangeLimited);
|
|
||||||
|
|
||||||
// Defaults to false, requires user intervention if incorrect
|
|
||||||
stream.set_premultiplied_alpha(false);
|
|
||||||
|
|
||||||
desc.AddVideoStream(stream);
|
|
||||||
|
|
||||||
if (image_is_still) {
|
|
||||||
still_streams++;
|
|
||||||
} else {
|
|
||||||
video_streams++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (avstream->codecpar->codec_type ==
|
} else if (avstream->codecpar->codec_type ==
|
||||||
AVMEDIA_TYPE_AUDIO) {
|
AVMEDIA_TYPE_AUDIO) {
|
||||||
@@ -627,7 +596,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
|
|||||||
}
|
}
|
||||||
// Create resampling context
|
// Create resampling context
|
||||||
AVChannelLayout layout = params.channel_layout();
|
AVChannelLayout layout = params.channel_layout();
|
||||||
SwrContext *resampler;
|
SwrContext *resampler=NULL;
|
||||||
swr_alloc_set_opts2(
|
swr_alloc_set_opts2(
|
||||||
&resampler, &layout,
|
&resampler, &layout,
|
||||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||||
@@ -835,7 +804,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);
|
||||||
@@ -888,8 +857,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;
|
||||||
|
|||||||
@@ -40,6 +40,20 @@ extern "C" {
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// 头文件里先备好枚举映射
|
||||||
|
static VideoParams::Interlacing FFmpegFieldOrderToOlive(AVFieldOrder fo)
|
||||||
|
{
|
||||||
|
switch (fo) {
|
||||||
|
case AV_FIELD_TT: // 隔行,顶场在前
|
||||||
|
return VideoParams::kInterlacedTopFirst;
|
||||||
|
case AV_FIELD_BB: // 隔行,底场在前
|
||||||
|
return VideoParams::kInterlacedBottomFirst;
|
||||||
|
case AV_FIELD_PROGRESSIVE:
|
||||||
|
default:
|
||||||
|
return VideoParams::kInterlaceNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||||
*/
|
*/
|
||||||
@@ -122,6 +136,10 @@ private:
|
|||||||
{
|
{
|
||||||
return avstream_;
|
return avstream_;
|
||||||
}
|
}
|
||||||
|
AVCodecContext *codec_ctx()
|
||||||
|
{
|
||||||
|
return codec_ctx_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AVFormatContext *fmt_ctx_;
|
AVFormatContext *fmt_ctx_;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ extern "C" {
|
|||||||
#include <libavfilter/buffersink.h>
|
#include <libavfilter/buffersink.h>
|
||||||
#include <libavfilter/buffersrc.h>
|
#include <libavfilter/buffersrc.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -866,7 +867,7 @@ void FFmpegEncoder::FlushEncoders()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fmt_ctx_) {
|
if (fmt_ctx_) {
|
||||||
if (fmt_ctx_->oformat->flags & AVFMT_ALLOW_FLUSH) {
|
if (fmt_ctx_->oformat->flags) {
|
||||||
int r = av_interleaved_write_frame(fmt_ctx_, nullptr);
|
int r = av_interleaved_write_frame(fmt_ctx_, nullptr);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
FFmpegError(tr("Failed to write interleaved packet"), r);
|
FFmpegError(tr("Failed to write interleaved packet"), r);
|
||||||
|
|||||||
@@ -85,7 +85,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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -774,6 +774,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();
|
||||||
|
|||||||
@@ -143,6 +143,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
@@ -25,23 +25,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);
|
||||||
}
|
}
|
||||||
@@ -123,6 +126,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
@@ -19,18 +19,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:
|
||||||
/**
|
/**
|
||||||
@@ -277,7 +282,8 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void CloseRequested();
|
void CloseRequested();
|
||||||
|
void shown(Qt::FocusReason reason);
|
||||||
|
void hidden();
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief paintEvent
|
* @brief paintEvent
|
||||||
@@ -321,7 +327,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.
|
||||||
@@ -337,6 +343,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,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(
|
||||||
|
|||||||
@@ -19,6 +19,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>
|
||||||
|
|
||||||
@@ -148,7 +150,10 @@ public:
|
|||||||
{
|
{
|
||||||
return job_;
|
return job_;
|
||||||
}
|
}
|
||||||
|
void handleFrame(AVFramePtr ptr)
|
||||||
|
{
|
||||||
|
frame_=ptr;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Renderer *renderer_;
|
Renderer *renderer_;
|
||||||
|
|
||||||
@@ -157,6 +162,8 @@ private:
|
|||||||
QVariant id_;
|
QVariant id_;
|
||||||
|
|
||||||
AcceleratedJob *job_;
|
AcceleratedJob *job_;
|
||||||
|
|
||||||
|
AVFramePtr frame_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ const double &TimeScaledObject::timebase_dbl() const
|
|||||||
rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale,
|
rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale,
|
||||||
const rational &timebase, bool round)
|
const rational &timebase, bool round)
|
||||||
{
|
{
|
||||||
|
if (timebase.isNull()) {
|
||||||
|
return rational();
|
||||||
|
}
|
||||||
double unscaled_time = x / x_scale / timebase.toDouble();
|
double unscaled_time = x / x_scale / timebase.toDouble();
|
||||||
|
|
||||||
// Adjust screen point by scale and timebase
|
// Adjust screen point by scale and timebase
|
||||||
@@ -85,16 +88,25 @@ rational TimeScaledObject::SceneToTimeNoGrid(const double &x,
|
|||||||
|
|
||||||
double TimeScaledObject::TimeToScene(const rational &time) const
|
double TimeScaledObject::TimeToScene(const rational &time) const
|
||||||
{
|
{
|
||||||
|
if (timebase_.isNull()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
return time.toDouble() * scale_;
|
return time.toDouble() * scale_;
|
||||||
}
|
}
|
||||||
|
|
||||||
rational TimeScaledObject::SceneToTime(const double &x, bool round) const
|
rational TimeScaledObject::SceneToTime(const double &x, bool round) const
|
||||||
{
|
{
|
||||||
|
if (timebase_.isNull()) {
|
||||||
|
return rational();
|
||||||
|
}
|
||||||
return SceneToTime(x, scale_, timebase_, round);
|
return SceneToTime(x, scale_, timebase_, round);
|
||||||
}
|
}
|
||||||
|
|
||||||
rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const
|
rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const
|
||||||
{
|
{
|
||||||
|
if (timebase_.isNull()) {
|
||||||
|
return rational::fromDouble(x / scale_);
|
||||||
|
}
|
||||||
return SceneToTimeNoGrid(x, scale_);
|
return SceneToTimeNoGrid(x, scale_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1201,8 +1201,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ bool ViewerDisplayWidget::eventFilter(QObject *o, QEvent *e)
|
|||||||
void ViewerDisplayWidget::OnPaint()
|
void ViewerDisplayWidget::OnPaint()
|
||||||
{
|
{
|
||||||
// Clear background to empty
|
// Clear background to empty
|
||||||
QColor bg_color = show_widget_background_ ? palette().window().color() :
|
QColor bg_color = show_widget_background_ ? palette().window().color() :
|
||||||
Qt::black;
|
Qt::black;
|
||||||
renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(),
|
renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(),
|
||||||
bg_color.blueF());
|
bg_color.blueF());
|
||||||
|
|||||||
@@ -27,15 +27,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,
|
||||||
@@ -93,7 +95,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,
|
||||||
@@ -380,7 +382,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);
|
||||||
}
|
}
|
||||||
@@ -428,7 +430,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,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);
|
||||||
|
|||||||
+1
-1
Submodule ext/KDDockWidgets updated: 8d2d0a5764...0e9723b96b
+1
-1
Submodule ext/core updated: 08eeee5fa9...50caae473d
Reference in New Issue
Block a user