修复bug

This commit is contained in:
2026-01-05 03:40:36 +08:00
parent 8263ccf814
commit 79f376b512
11 changed files with 431 additions and 24 deletions
+13 -3
View File
@@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-13, windows-2022]
os: [ubuntu-22.04, macos-latest, windows-2022]
env:
CMAKE_BUILD_TYPE: Release
steps:
@@ -53,12 +53,22 @@ jobs:
echo "VCPKG_ROOT=$env:VCPKG_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Configure (Linux/macOS)
if: runner.os != 'Windows'
- name: Configure (Linux)
if: runner.os == 'Linux'
run: |
cmake -S . -B build -G Ninja \
-DBUILD_TESTS=ON \
-DBUILD_QT6=ON \
-DOCIO_LOCATION=/usr \
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- name: Configure (macOS)
if: runner.os == 'macOS'
run: |
cmake -S . -B build -G Ninja \
-DBUILD_TESTS=ON \
-DBUILD_QT6=ON \
-DOCIO_LOCATION=$(brew --prefix opencolorio) \
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- name: Configure (Windows)
+33 -16
View File
@@ -39,21 +39,38 @@
extern "C" {
#include <libswscale/swscale.h>
}
namespace {
const std::string kBitDepthNoneStr(kOfxBitDepthNone);
const std::string kBitDepthByteStr(kOfxBitDepthByte);
const std::string kBitDepthShortStr(kOfxBitDepthShort);
const std::string kBitDepthHalfStr(kOfxBitDepthHalf);
const std::string kBitDepthFloatStr(kOfxBitDepthFloat);
const std::string kImageComponentNoneStr(kOfxImageComponentNone);
const std::string kImageComponentAlphaStr(kOfxImageComponentAlpha);
const std::string kImageComponentRGBStr(kOfxImageComponentRGB);
const std::string kImageComponentRGBAStr(kOfxImageComponentRGBA);
const std::string kImagePremultStr(kOfxImagePreMultiplied);
const std::string kImageUnPremultStr(kOfxImageUnPreMultiplied);
const std::string kImageFieldNoneStr(kOfxImageFieldNone);
const std::string kImageFieldUpperStr(kOfxImageFieldUpper);
const std::string kImageFieldLowerStr(kOfxImageFieldLower);
}
const std::string &olive::plugin::OliveClipInstance::getUnmappedBitDepth() const
{
switch (params_.format()) {
case PixelFormat::INVALID:
return kOfxBitDepthNone;
return kBitDepthNoneStr;
case PixelFormat::U8:
return kOfxBitDepthByte;
return kBitDepthByteStr;
case PixelFormat::U16:
return kOfxBitDepthShort;
return kBitDepthShortStr;
case PixelFormat::F16:
return kOfxBitDepthHalf;
return kBitDepthHalfStr;
case PixelFormat::F32:
return kOfxBitDepthFloat;
return kBitDepthFloatStr;
default:
return kOfxBitDepthNone;
return kBitDepthNoneStr;
}
}
const std::string &
@@ -61,21 +78,21 @@ olive::plugin::OliveClipInstance::getUnmappedComponents() const
{
switch (params_.channel_count()) {
case 1:
return kOfxImageComponentAlpha;
return kImageComponentAlphaStr;
case 3:
return kOfxImageComponentRGB;
return kImageComponentRGBStr;
case 4:
return kOfxImageComponentRGBA;
return kImageComponentRGBAStr;
default:
return kOfxImageComponentNone;
return kImageComponentNoneStr;
}
}
const std::string &olive::plugin::OliveClipInstance::getPremult() const
{
if (params_.premultiplied_alpha()) {
return kOfxImagePreMultiplied;
return kImagePremultStr;
} else {
return kOfxImageUnPreMultiplied;
return kImageUnPremultStr;
}
}
double olive::plugin::OliveClipInstance::getAspectRatio() const
@@ -97,13 +114,13 @@ const std::string &olive::plugin::OliveClipInstance::getFieldOrder() const
{
switch (params_.interlacing()) {
case VideoParams::kInterlaceNone:
return kOfxImageFieldNone;
return kImageFieldNoneStr;
case VideoParams::kInterlacedTopFirst:
return kOfxImageFieldUpper;
return kImageFieldUpperStr;
case VideoParams::kInterlacedBottomFirst:
return kOfxImageFieldLower;
return kImageFieldLowerStr;
}
return kOfxImageFieldNone;
return kImageFieldNoneStr;
}
bool olive::plugin::OliveClipInstance::getConnected() const
{
+8 -4
View File
@@ -43,17 +43,21 @@ namespace olive
namespace plugin
{
namespace {
const std::string kImageFieldNoneStr(kOfxImageFieldNone);
const std::string kImageFieldUpperStr(kOfxImageFieldUpper);
const std::string kImageFieldLowerStr(kOfxImageFieldLower);
const std::string &FieldOrderForParams(const VideoParams &params)
{
switch (params.interlacing()) {
case VideoParams::kInterlaceNone:
return kOfxImageFieldNone;
return kImageFieldNoneStr;
case VideoParams::kInterlacedTopFirst:
return kOfxImageFieldUpper;
return kImageFieldUpperStr;
case VideoParams::kInterlacedBottomFirst:
return kOfxImageFieldLower;
return kImageFieldLowerStr;
}
return kOfxImageFieldNone;
return kImageFieldNoneStr;
}
class DeferredRedoCommand : public UndoCommand {
+6
View File
@@ -7,11 +7,17 @@ add_executable(olive-gtest
node_keyframe_test.cpp
node_serialization_test.cpp
render_videoparams_test.cpp
render_videoparams_branch_test.cpp
render_audioparams_test.cpp
render_audioparams_branch_test.cpp
render_sampleformat_test.cpp
render_pixelformat_test.cpp
project_serializer_test.cpp
timeline_marker_test.cpp
undo_stack_test.cpp
plugin_support_test.cpp
plugin_support_image_test.cpp
plugin_support_clip_test.cpp
codec_frame_test.cpp
task_taskmanager_test.cpp
module_smoke_test.cpp
+96
View File
@@ -0,0 +1,96 @@
#include <gtest/gtest.h>
#include "ofxImageEffect.h"
#include "ofxhClip.h"
#include "pluginSupport/OliveClip.h"
namespace {
olive::VideoParams MakeParams(int width, int height,
olive::core::PixelFormat format,
int channels,
bool premultiplied)
{
olive::VideoParams params;
params.set_width(width);
params.set_height(height);
params.set_format(format);
params.set_channel_count(channels);
params.set_premultiplied_alpha(premultiplied);
return params;
}
}
TEST(PluginSupportClip, PropertyGetters)
{
OFX::Host::ImageEffect::ClipDescriptor desc(kOfxImageEffectOutputClipName);
olive::VideoParams params =
MakeParams(1920, 1080, olive::core::PixelFormat::U16, 3, false);
params.set_pixel_aspect_ratio(olive::core::rational(2, 1));
params.set_frame_rate(olive::core::rational(30, 1));
params.set_start_time(2);
params.set_duration(4);
params.set_interlacing(olive::VideoParams::kInterlacedTopFirst);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
EXPECT_EQ(clip.getUnmappedBitDepth(), kOfxBitDepthShort);
EXPECT_EQ(clip.getUnmappedComponents(), kOfxImageComponentRGB);
EXPECT_EQ(clip.getPremult(), kOfxImageUnPreMultiplied);
EXPECT_DOUBLE_EQ(clip.getAspectRatio(), 2.0);
EXPECT_DOUBLE_EQ(clip.getFrameRate(), 30.0);
double start_frame = 0.0;
double end_frame = 0.0;
clip.getFrameRange(start_frame, end_frame);
EXPECT_DOUBLE_EQ(start_frame, 60.0);
EXPECT_DOUBLE_EQ(end_frame, 180.0);
EXPECT_EQ(clip.getFieldOrder(), kOfxImageFieldUpper);
EXPECT_DOUBLE_EQ(clip.getUnmappedFrameRate(), 30.0);
clip.getUnmappedFrameRange(start_frame, end_frame);
EXPECT_DOUBLE_EQ(start_frame, 60.0);
EXPECT_DOUBLE_EQ(end_frame, 180.0);
EXPECT_FALSE(clip.getContinuousSamples());
EXPECT_FALSE(clip.getConnected());
}
TEST(PluginSupportClip, GetImageClampsBoundsAndCachesOutput)
{
OFX::Host::ImageEffect::ClipDescriptor desc(kOfxImageEffectOutputClipName);
olive::VideoParams params =
MakeParams(100, 80, olive::core::PixelFormat::U8, 4, true);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
OfxRectD optional_bounds = { -10.0, -10.0, 200.0, 200.0 };
OFX::Host::ImageEffect::Image *image =
clip.getImage(0.0, &optional_bounds);
ASSERT_NE(image, nullptr);
auto *olive_image = static_cast<olive::plugin::Image *>(image);
EXPECT_EQ(olive_image->width(), 100);
EXPECT_EQ(olive_image->height(), 80);
OFX::Host::ImageEffect::Image *image_again =
clip.getImage(0.0, nullptr);
EXPECT_EQ(image, image_again);
}
TEST(PluginSupportClip, GetImageReturnsNewImageForNonOutput)
{
OFX::Host::ImageEffect::ClipDescriptor desc("Source");
olive::VideoParams params =
MakeParams(64, 64, olive::core::PixelFormat::U8, 4, false);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
OFX::Host::ImageEffect::Image *first = clip.getImage(0.0, nullptr);
OFX::Host::ImageEffect::Image *second = clip.getImage(0.0, nullptr);
EXPECT_NE(first, nullptr);
EXPECT_NE(second, nullptr);
EXPECT_NE(first, second);
first->releaseReference();
second->releaseReference();
}
+91
View File
@@ -0,0 +1,91 @@
#include <gtest/gtest.h>
#include "ofxImageEffect.h"
#include "ofxhClip.h"
#include "pluginSupport/OliveClip.h"
#include "pluginSupport/image.h"
namespace {
olive::VideoParams MakeParams(int width, int height,
olive::core::PixelFormat format,
int channels,
bool premultiplied)
{
olive::VideoParams params;
params.set_width(width);
params.set_height(height);
params.set_format(format);
params.set_channel_count(channels);
params.set_premultiplied_alpha(premultiplied);
return params;
}
}
TEST(PluginSupportImage, AllocateFromParamsSetsProperties)
{
OFX::Host::ImageEffect::ClipDescriptor desc(kOfxImageEffectOutputClipName);
olive::VideoParams params =
MakeParams(640, 480, olive::core::PixelFormat::U8, 4, true);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
olive::plugin::Image image(clip);
OfxRectI bounds = { 0, 0, 640, 480 };
OfxRectI rod = bounds;
image.AllocateFromParams(params, bounds, rod, true);
EXPECT_NE(image.data(), nullptr);
EXPECT_EQ(image.width(), 640);
EXPECT_EQ(image.height(), 480);
EXPECT_EQ(image.row_bytes(), 640 * 4);
EXPECT_EQ(image.pixel_format(), olive::core::PixelFormat::U8);
EXPECT_EQ(image.channel_count(), 4);
EXPECT_TRUE(image.premultiplied_alpha());
}
TEST(PluginSupportImage, EnsureAllocatedFromParamsClearsAndResizes)
{
OFX::Host::ImageEffect::ClipDescriptor desc(kOfxImageEffectOutputClipName);
olive::VideoParams params =
MakeParams(64, 32, olive::core::PixelFormat::U8, 3, false);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
olive::plugin::Image image(clip);
OfxRectI bounds = { 0, 0, 64, 32 };
OfxRectI rod = bounds;
image.AllocateFromParams(params, bounds, rod, true);
ASSERT_NE(image.data(), nullptr);
image.data()[0] = 0xAB;
image.EnsureAllocatedFromParams(params, bounds, rod, true);
EXPECT_EQ(image.data()[0], 0);
OfxRectI new_bounds = { 0, 0, 16, 16 };
image.EnsureAllocatedFromParams(params, new_bounds, rod, false);
EXPECT_EQ(image.width(), 16);
EXPECT_EQ(image.height(), 16);
}
TEST(PluginSupportImage, PropertyFallbacks)
{
OFX::Host::ImageEffect::ClipDescriptor desc(kOfxImageEffectOutputClipName);
olive::VideoParams params =
MakeParams(1, 1, olive::core::PixelFormat::INVALID, 0, false);
olive::plugin::OliveClipInstance clip(nullptr, desc, params);
olive::plugin::Image image(clip);
image.setStringProperty(kOfxImageEffectPropPixelDepth, kOfxBitDepthHalf);
image.setStringProperty(kOfxImageEffectPropComponents,
kOfxImageComponentRGB);
image.setStringProperty(kOfxImageEffectPropPreMultiplication,
kOfxImagePreMultiplied);
image.setIntProperty(kOfxImagePropBounds, 10, 0);
image.setIntProperty(kOfxImagePropBounds, 20, 1);
image.setIntProperty(kOfxImagePropBounds, 42, 2);
image.setIntProperty(kOfxImagePropBounds, 70, 3);
EXPECT_EQ(image.pixel_format(), olive::core::PixelFormat::F16);
EXPECT_EQ(image.channel_count(), 3);
EXPECT_TRUE(image.premultiplied_alpha());
EXPECT_EQ(image.width(), 32);
EXPECT_EQ(image.height(), 50);
}
@@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include "olive/core/render/audioparams.h"
TEST(RenderAudioParams, ValidityAndEquality)
{
olive::core::AudioParams invalid;
EXPECT_FALSE(invalid.is_valid());
olive::core::AudioParams params(
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16);
EXPECT_TRUE(params.is_valid());
olive::core::AudioParams other(
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16);
EXPECT_TRUE(params == other);
other.set_sample_rate(44100);
EXPECT_TRUE(params != other);
}
TEST(RenderAudioParams, TimeAndSampleConversions)
{
olive::core::AudioParams params(
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16);
EXPECT_EQ(params.channel_count(), 2);
EXPECT_EQ(params.bytes_per_sample_per_channel(), 2);
EXPECT_EQ(params.bits_per_sample(), 16);
EXPECT_EQ(params.time_to_samples(1.0), 48000);
EXPECT_EQ(params.time_to_bytes_per_channel(1.0), 96000);
EXPECT_EQ(params.time_to_bytes(1.0), 192000);
EXPECT_EQ(params.samples_to_bytes(48000), 192000);
EXPECT_EQ(params.samples_to_bytes_per_channel(48000), 96000);
EXPECT_EQ(params.bytes_to_samples(192000), 48000);
EXPECT_EQ(params.bytes_to_time(192000), olive::core::rational(1, 1));
EXPECT_EQ(params.bytes_per_channel_to_time(96000),
olive::core::rational(1, 1));
}
+28
View File
@@ -0,0 +1,28 @@
#include <gtest/gtest.h>
#include "olive/core/render/pixelformat.h"
TEST(RenderPixelFormat, ByteCountAndString)
{
using olive::core::PixelFormat;
EXPECT_EQ(PixelFormat::byte_count(PixelFormat::INVALID), 0);
EXPECT_EQ(PixelFormat::byte_count(PixelFormat::U8), 1);
EXPECT_EQ(PixelFormat::byte_count(PixelFormat::U16), 2);
EXPECT_EQ(PixelFormat::byte_count(PixelFormat::F16), 2);
EXPECT_EQ(PixelFormat::byte_count(PixelFormat::F32), 4);
EXPECT_EQ(PixelFormat(PixelFormat::U8).to_string(), std::string("u8"));
EXPECT_EQ(PixelFormat(PixelFormat::INVALID).to_string(), std::string(""));
}
TEST(RenderPixelFormat, FloatChecks)
{
using olive::core::PixelFormat;
EXPECT_FALSE(PixelFormat::is_float(PixelFormat::U8));
EXPECT_FALSE(PixelFormat::is_float(PixelFormat::U16));
EXPECT_TRUE(PixelFormat::is_float(PixelFormat::F16));
EXPECT_TRUE(PixelFormat::is_float(PixelFormat::F32));
EXPECT_FALSE(PixelFormat::is_float(PixelFormat::INVALID));
}
+29
View File
@@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include "olive/core/render/sampleformat.h"
TEST(RenderSampleFormat, ByteCountAndStringRoundTrip)
{
using olive::core::SampleFormat;
EXPECT_EQ(SampleFormat::byte_count(SampleFormat::INVALID), 0);
EXPECT_EQ(SampleFormat::byte_count(SampleFormat::U8), 1);
EXPECT_EQ(SampleFormat::byte_count(SampleFormat::S16), 2);
EXPECT_EQ(SampleFormat::byte_count(SampleFormat::F32), 4);
EXPECT_EQ(SampleFormat::byte_count(SampleFormat::F64), 8);
EXPECT_EQ(SampleFormat::to_string(SampleFormat::S16), "s16");
EXPECT_EQ(SampleFormat::from_string("s16"), SampleFormat::S16);
EXPECT_EQ(SampleFormat::from_string(""), SampleFormat::INVALID);
EXPECT_EQ(SampleFormat::from_string("unknown"), SampleFormat::INVALID);
}
TEST(RenderSampleFormat, PackedAndPlanarChecks)
{
using olive::core::SampleFormat;
EXPECT_TRUE(SampleFormat::is_packed(SampleFormat::S16));
EXPECT_FALSE(SampleFormat::is_packed(SampleFormat::S16P));
EXPECT_TRUE(SampleFormat::is_planar(SampleFormat::S16P));
EXPECT_FALSE(SampleFormat::is_planar(SampleFormat::S16));
}
@@ -0,0 +1,84 @@
#include <gtest/gtest.h>
extern "C" {
#include <libavutil/avutil.h>
}
#include "render/videoparams.h"
TEST(RenderVideoParams, BytesPerChannelAndPixel)
{
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::INVALID),
0);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::U8),
1);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::U16),
2);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::F16),
2);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::F32),
4);
EXPECT_EQ(olive::VideoParams::GetBytesPerPixel(
olive::core::PixelFormat::U8, 4),
4);
}
TEST(RenderVideoParams, DividerAndFormatNames)
{
EXPECT_EQ(olive::VideoParams::GetNameForDivider(1),
QStringLiteral("Full"));
EXPECT_EQ(olive::VideoParams::GetNameForDivider(3),
QStringLiteral("1/3"));
const QString unknown =
olive::VideoParams::GetFormatName(olive::core::PixelFormat::INVALID);
EXPECT_TRUE(unknown.contains(QStringLiteral("Unknown")));
}
TEST(RenderVideoParams, ScalingAndDividerForTarget)
{
EXPECT_EQ(olive::VideoParams::GetScaledDimension(100, 3), 33);
EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution(
1920, 1080, 960, 540),
2);
EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution(
1920, 1080, 480, 270),
4);
}
TEST(RenderVideoParams, FrameRateStringsAndPixelAspect)
{
const QString fps =
olive::VideoParams::FrameRateToString(olive::core::rational(24, 1));
EXPECT_TRUE(fps.contains(QStringLiteral("24")));
EXPECT_TRUE(fps.contains(QStringLiteral("FPS")));
const QStringList names =
olive::VideoParams::GetStandardPixelAspectRatioNames();
ASSERT_EQ(names.size(), 6);
EXPECT_TRUE(names.at(0).contains(QStringLiteral("1.0000")));
}
TEST(RenderVideoParams, ValidityAndTimebase)
{
olive::VideoParams params;
EXPECT_FALSE(params.is_valid());
EXPECT_EQ(params.get_time_in_timebase_units(olive::core::rational(1, 1)),
AV_NOPTS_VALUE);
params.set_width(1920);
params.set_height(1080);
params.set_format(olive::core::PixelFormat::U8);
params.set_channel_count(4);
params.set_pixel_aspect_ratio(olive::core::rational(1, 1));
params.set_time_base(olive::core::rational(1, 1));
params.set_start_time(10);
EXPECT_TRUE(params.is_valid());
EXPECT_EQ(params.get_time_in_timebase_units(olive::core::rational(2, 1)),
12);
}