From 62a69e49275ce012fc52f0cd7a0c61d03c3a34a4 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 12 Mar 2021 14:42:05 -0600 Subject: [PATCH 01/10] Fixing logic when applying chromakey logic with pre-multiplied alpha. Each channel must be updated, vs just the alpha value. --- src/effects/ChromaKey.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp index 721c6a0ec..f686e1abf 100644 --- a/src/effects/ChromaKey.cpp +++ b/src/effects/ChromaKey.cpp @@ -83,18 +83,24 @@ std::shared_ptr ChromaKey::GetFrame(std::shared_ptrwidth() * image->height(); pixel++, byte_index+=4) { // Get the RGB values from the pixel - unsigned char R = pixels[byte_index]; - unsigned char G = pixels[byte_index + 1]; - unsigned char B = pixels[byte_index + 2]; - unsigned char A = pixels[byte_index + 3]; - - // Get distance between mask color and pixel color - long distance = Color::GetDistance((long)R, (long)G, (long)B, mask_R, mask_G, mask_B); - - // Alpha out the pixel (if color similar) - if (distance <= threshold) - // MATCHED - Make pixel transparent - pixels[byte_index + 3] = 0; + // Remove the premultiplied alpha values from R,G,B + float A = float(pixels[byte_index + 3]); + unsigned char R = (pixels[byte_index] / A) * 255.0; + unsigned char G = (pixels[byte_index + 1] / A) * 255.0; + unsigned char B = (pixels[byte_index + 2] / A) * 255.0; + + // Get distance between mask color and pixel color + long distance = Color::GetDistance((long)R, (long)G, (long)B, mask_R, mask_G, mask_B); + + if (distance <= threshold) { + // MATCHED - Make pixel transparent + // Due to premultiplied alpha, we must also zero out + // the individual color channels (or else artifacts are left behind) + pixels[byte_index] = 0; + pixels[byte_index + 1] = 0; + pixels[byte_index + 2] = 0; + pixels[byte_index + 3] = 0; + } } // return the modified frame From 6b845c0acc7507ed4690cda2b7a2d6e2a1ea5101 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 12 Mar 2021 14:48:06 -0600 Subject: [PATCH 02/10] Increasing max fuzz for Chromakey --- src/effects/ChromaKey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp index f686e1abf..bb214c723 100644 --- a/src/effects/ChromaKey.cpp +++ b/src/effects/ChromaKey.cpp @@ -174,7 +174,7 @@ std::string ChromaKey::PropertiesJSON(int64_t requested_frame) const { root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame); root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame); root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame); - root["fuzz"] = add_property_json("Fuzz", fuzz.GetValue(requested_frame), "float", "", &fuzz, 0, 25, false, requested_frame); + root["fuzz"] = add_property_json("Fuzz", fuzz.GetValue(requested_frame), "float", "", &fuzz, 0, 125, false, requested_frame); // Return formatted string return root.toStyledString(); From 917ddb09021c86d4c4f118a216eea995cbc86cf8 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 15 Mar 2021 16:45:17 -0400 Subject: [PATCH 03/10] Set checkout fetch depth for codecov See https://community.codecov.io/t/issue-detecting-commit-sha-please-run-actions-checkout-with-fetch-depth-1-or-set-to-0/2571 --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3ac30332..0bc414103 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,10 @@ jobs: steps: - uses: actions/checkout@v2 + # Work around a codecov issue detecting commit SHAs + # see: https://community.codecov.io/t/issue-detecting-commit-sha-please-run-actions-checkout-with-fetch-depth-1-or-set-to-0/2571 + with: + fetch-depth: 0 - uses: haya14busa/action-cond@v1 id: coverage From 5b8474fc9b45c28c1130d62a286b34bc8a069cbb Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 18:30:50 -0400 Subject: [PATCH 04/10] README: Remove Travis build shields --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index a03ce11ec..bb9ff0870 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,6 @@ OpenShot Video Library (libopenshot) is a free, open-source C++ library dedicated to delivering high quality video editing, animation, and playback solutions to the world. -## Build Status - -[![Build Status](https://img.shields.io/travis/OpenShot/libopenshot/develop.svg?label=libopenshot)](https://travis-ci.org/OpenShot/libopenshot) [![Build Status](https://img.shields.io/travis/OpenShot/libopenshot-audio/develop.svg?label=libopenshot-audio)](https://travis-ci.org/OpenShot/libopenshot-audio) - ## Features * Cross-Platform (Linux, Mac, and Windows) From c49ffd18b7cba3b88ccfc0ccffc7a1647c32f761 Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 18:35:59 -0400 Subject: [PATCH 05/10] README: Add Github Actions status badges --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bb9ff0870..40c76c48c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ OpenShot Video Library (libopenshot) is a free, open-source C++ library dedicated to delivering high quality video editing, animation, and playback solutions to the world. +## Build Status + +[![CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) [![CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) + ## Features * Cross-Platform (Linux, Mac, and Windows) From f234fb5355c68860bc3b06c2a099f8d755914a20 Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 18:36:45 -0400 Subject: [PATCH 06/10] README: label badges by repo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40c76c48c..f255e8984 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ solutions to the world. ## Build Status -[![CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) [![CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) +[![libopenshot CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) [![libopenshot-audio CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) ## Features From 23bea37ccf21e9e806770a367c43a6ee65536bff Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 18:38:12 -0400 Subject: [PATCH 07/10] README: Label badges visibly --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f255e8984..1d67f565f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ solutions to the world. ## Build Status -[![libopenshot CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) [![libopenshot-audio CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) +**libopenshot** [![CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) **libopenshot-audio** [![CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) ## Features From 367ab512f0f4712c72f24c2fc41355e85a0417f5 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 31 Mar 2021 18:46:01 -0400 Subject: [PATCH 08/10] Add repo name to CI workflow title --- .github/workflows/ci.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bc414103..9c3c672fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI Build +name: libopenshot CI Build on: [push, pull_request] jobs: build: diff --git a/README.md b/README.md index 1d67f565f..f255e8984 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ solutions to the world. ## Build Status -**libopenshot** [![CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) **libopenshot-audio** [![CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) +[![libopenshot CI Build](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot/actions/workflows/ci.yml) [![libopenshot-audio CI Build](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenShot/libopenshot-audio/actions/workflows/ci.yml) ## Features From fa087d6847b97af153fe9b8477d3812d874909e3 Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 19:44:05 -0400 Subject: [PATCH 09/10] tests/DummyReader: Goose coverage (#650) --- tests/DummyReader_Tests.cpp | 101 ++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/tests/DummyReader_Tests.cpp b/tests/DummyReader_Tests.cpp index b8a86d02e..806bd281e 100644 --- a/tests/DummyReader_Tests.cpp +++ b/tests/DummyReader_Tests.cpp @@ -40,43 +40,48 @@ #include "Fraction.h" #include "Frame.h" -using namespace std; -using namespace openshot; +SUITE (DummyReader) { -TEST (DummyReader_Basic_Constructor) { - // Create a default fraction (should be 1/1) +TEST (Default_Constructor) { openshot::DummyReader r; - r.Open(); // Open the reader - // Check values - CHECK_EQUAL(1280, r.info.width); - CHECK_EQUAL(768, r.info.height); - CHECK_EQUAL(24, r.info.fps.num); - CHECK_EQUAL(1, r.info.fps.den); - CHECK_EQUAL(44100, r.info.sample_rate); - CHECK_EQUAL(2, r.info.channels); - CHECK_EQUAL(30.0, r.info.duration); + CHECK_EQUAL(false, r.IsOpen()); + CHECK_THROW(r.GetFrame(1), openshot::ReaderClosed); + + r.Open(); + + // Default values + CHECK_EQUAL(1280, r.info.width); + CHECK_EQUAL(768, r.info.height); + CHECK_EQUAL(24, r.info.fps.num); + CHECK_EQUAL(1, r.info.fps.den); + CHECK_EQUAL(44100, r.info.sample_rate); + CHECK_EQUAL(2, r.info.channels); + CHECK_EQUAL(30.0, r.info.duration); + + CHECK_EQUAL("DummyReader", r.Name()); + + auto cache = r.GetCache(); + CHECK_EQUAL(true, cache == nullptr); } -TEST (DummyReader_Constructor) { - // Create a default fraction (should be 1/1) - openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 60.0); - r.Open(); // Open the reader +TEST (Constructor) { + openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 48000, 2, 60.0); + r.Open(); // Check values CHECK_EQUAL(1920, r.info.width); CHECK_EQUAL(1080, r.info.height); CHECK_EQUAL(30, r.info.fps.num); CHECK_EQUAL(1, r.info.fps.den); - CHECK_EQUAL(44100, r.info.sample_rate); + CHECK_EQUAL(48000, r.info.sample_rate); CHECK_EQUAL(2, r.info.channels); CHECK_EQUAL(60.0, r.info.duration); } -TEST (DummyReader_Blank_Frame) { - // Create a default fraction (should be 1/1) +TEST (Blank_Frame) { openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0); - r.Open(); // Open the reader + r.Open(); // Get a blank frame (because we have not passed a Cache object (full of Frame objects) to the constructor // Check values @@ -85,17 +90,17 @@ TEST (DummyReader_Blank_Frame) { CHECK_EQUAL(1, r.GetFrame(1)->GetPixels(701)[701] == 0); // black pixel } -TEST (DummyReader_Fake_Frame) { +TEST (Fake_Frame) { // Create cache object to hold test frames - CacheMemory cache; + openshot::CacheMemory cache; // Let's create some test frames for (int64_t frame_number = 1; frame_number <= 30; frame_number++) { // Create blank frame (with specific frame #, samples, and channels) // Sample count should be 44100 / 30 fps = 1470 samples per frame int sample_count = 1470; - std::shared_ptr f(new openshot::Frame(frame_number, sample_count, 2)); + auto f = std::make_shared(frame_number, sample_count, 2); // Create test samples with incrementing value float *audio_buffer = new float[sample_count]; @@ -112,9 +117,8 @@ TEST (DummyReader_Fake_Frame) { cache.Add(f); } - // Create a default fraction (should be 1/1) openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0, &cache); - r.Open(); // Open the reader + r.Open(); // Verify our artificial audio sample data is correct CHECK_EQUAL(1, r.GetFrame(1)->number); @@ -130,26 +134,59 @@ TEST (DummyReader_Fake_Frame) { r.Close(); } -TEST (DummyReader_Invalid_Fake_Frame) { +TEST (Invalid_Fake_Frame) { // Create fake frames (with specific frame #, samples, and channels) - std::shared_ptr f1(new openshot::Frame(1, 1470, 2)); - std::shared_ptr f2(new openshot::Frame(2, 1470, 2)); + auto f1 = std::make_shared(1, 1470, 2); + auto f2 = std::make_shared(2, 1470, 2); // Add test frames to cache object - CacheMemory cache; + openshot::CacheMemory cache; cache.Add(f1); cache.Add(f2); - // Create a default fraction (should be 1/1) openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0, &cache); r.Open(); // Verify exception CHECK_EQUAL(1, r.GetFrame(1)->number); CHECK_EQUAL(2, r.GetFrame(2)->number); - CHECK_THROW(r.GetFrame(3)->number, InvalidFile); + CHECK_THROW(r.GetFrame(3)->number, openshot::InvalidFile); // Clean up cache.Clear(); r.Close(); } + + +TEST(Json) +{ + openshot::DummyReader r1; + openshot::DummyReader r2(openshot::Fraction(24, 1), 1280, 768, 44100, 2, 30.0); + auto json1 = r1.Json(); + auto json2 = r2.JsonValue(); + auto json_string2 = json2.toStyledString(); + CHECK_EQUAL(json1, json_string2); +} + +TEST(SetJson) +{ + openshot::DummyReader r1; + std::stringstream json_stream; + json_stream << R"json( + { + "width": 1920, + "height": 1080, + "fps": { "num": 15, "den": 1 }, + "duration": 15.0 + } + )json"; + + r1.SetJson(json_stream.str()); + CHECK_EQUAL(1920, r1.info.width); + CHECK_EQUAL(1080, r1.info.height); + CHECK_EQUAL(15, r1.info.fps.num); + CHECK_EQUAL(1, r1.info.fps.den); + CHECK_EQUAL(15.0, r1.info.duration); +} + +} // SUITE From 3f9b402ea48b4c5de978a9c6d75c7e1c858e8249 Mon Sep 17 00:00:00 2001 From: Frank Dana Date: Wed, 31 Mar 2021 19:44:48 -0400 Subject: [PATCH 10/10] FFmpegReader: Throw correct exception (#647) The only reason an `AV_ALLOCATE_FRAME()` could fail is because there wasn't enough memory to allocate the necessary buffers, so if it returns a `nullptr` then throwing `OutOfMemory` makes far more sense than throwing `OutOfBoundsFrame`. --- src/FFmpegReader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index a64e52303..e4eaab2c8 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1243,13 +1243,13 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { processing_video_frames[current_frame] = current_frame; // Create variables for a RGB Frame (since most videos are not in RGB, we must convert it) - AVFrame *pFrameRGB = NULL; - uint8_t *buffer = NULL; + AVFrame *pFrameRGB = nullptr; + uint8_t *buffer = nullptr; // Allocate an AVFrame structure pFrameRGB = AV_ALLOCATE_FRAME(); - if (pFrameRGB == NULL) - throw OutOfBoundsFrame("Convert Image Broke!", current_frame, video_length); + if (pFrameRGB == nullptr) + throw OutOfMemory("Failed to allocate frame buffer", path); // Determine the max size of this source image (based on the timeline's size, the scaling mode, // and the scaling keyframes). This is a performance improvement, to keep the images as small as possible,