From adeb452e5d47176de82c304c6aab2675bd465d3f Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Mon, 4 Nov 2019 19:56:33 +0100 Subject: [PATCH 01/86] Color-separated Saturation Signed-off-by: Markus KARG --- include/effects/Saturation.h | 5 ++- src/effects/Saturation.cpp | 75 +++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index de3cc7714..08695fd90 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -65,6 +65,9 @@ namespace openshot public: Keyframe saturation; ///< The color saturation: 0.0 = black and white, 1.0 = normal, 2.0 = double saturation + Keyframe saturation_R; ///< Red color saturation + Keyframe saturation_G; ///< Green color saturation + Keyframe saturation_B; ///< Blue color saturation /// Blank constructor, useful when using Json to load the effect properties Saturation(); @@ -72,7 +75,7 @@ namespace openshot /// Default constructor, which takes 1 curve, to adjust the color saturation over time. /// /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = black and white, 1.0 = normal, 2.0 = double saturation) - Saturation(Keyframe new_saturation); + Saturation(Keyframe new_saturation, Keyframe new_saturation_R, Keyframe new_saturation_G, Keyframe new_saturation_B); /// @brief This method is required for all derived classes of EffectBase, and returns a /// modified openshot::Frame object diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 06bcb02c2..6762b4e77 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -33,13 +33,13 @@ using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -Saturation::Saturation() : saturation(1.0) { +Saturation::Saturation() : saturation(1.0), saturation_R(1.0), saturation_G(1.0), saturation_B(1.0) { // Init effect properties init_effect_details(); } // Default constructor -Saturation::Saturation(Keyframe new_saturation) : saturation(new_saturation) +Saturation::Saturation(Keyframe new_saturation, Keyframe new_saturation_R, Keyframe new_saturation_G, Keyframe new_saturation_B) : saturation(new_saturation), saturation_R(new_saturation_R), saturation_G(new_saturation_G), saturation_B(new_saturation_B) { // Init effect properties init_effect_details(); @@ -71,6 +71,9 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ // Get keyframe values for this frame float saturation_value = saturation.GetValue(frame_number); + float saturation_value_R = saturation_R.GetValue(frame_number); + float saturation_value_G = saturation_G.GetValue(frame_number); + float saturation_value_B = saturation_B.GetValue(frame_number); // Constants used for color saturation formula double pR = .299; @@ -87,6 +90,10 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ int B = pixels[byte_index + 2]; int A = pixels[byte_index + 3]; + /* + * Common saturation adjustment + */ + // Calculate the saturation multiplier double p = sqrt( (R * R * pR) + (G * G * pG) + @@ -102,6 +109,58 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ G = constrain(G); B = constrain(B); + /* + * Color-separated saturation adjustment + * + * Splitting each of the three subpixels (R, G and B) into three distincs sub-subpixels (R, G and B in turn) + * which in their optical sum reproduce the original subpixel's color OR produce white light in the brightness + * of the original subpixel (dependening on the color channel's slider value). + */ + + // Three subpixels producing either R or white with brightness of R + int Rr = R; + int Gr = 0; + int Br = 0; + + // Three subpixels producing either G or white with brightness of G + int Rg = 0; + int Gg = G; + int Bg = 0; + + // Three subpixels producing either B or white with brightness of B + int Rb = 0; + int Gb = 0; + int Bb = B; + + // Compute the brightness ("saturation multiplier") of the replaced subpixels + // Actually mathematical no-ops mostly, verbosity is kept just for clarification + const double p_r = sqrt( (Rr * Rr * pR) + (Gr * Gr * pG) + (Br * Br * pB) ); + const double p_g = sqrt( (Rg * Rg * pR) + (Gg * Gg * pG) + (Bg * Bg * pB) ); + const double p_b = sqrt( (Rb * Rb * pR) + (Gb * Gb * pG) + (Bb * Bb * pB) ); + + // Adjust the saturation + Rr = p_r + (Rr - p_r) * saturation_value_R; + Gr = p_r + (Gr - p_r) * saturation_value_R; + Br = p_r + (Br - p_r) * saturation_value_R; + + Rg = p_g + (Rg - p_g) * saturation_value_G; + Gg = p_g + (Gg - p_g) * saturation_value_G; + Bg = p_g + (Bg - p_g) * saturation_value_G; + + Rb = p_b + (Rb - p_b) * saturation_value_B; + Gb = p_b + (Gb - p_b) * saturation_value_B; + Bb = p_b + (Bb - p_b) * saturation_value_B; + + // Recombine brightness of sub-subpixels (Rx, Gx and Bx) into sub-pixels (R, G and B) again + R = Rr + Rg + Rb; + G = Gr + Gg + Gb; + B = Br + Bg + Bb; + + // Constrain the value from 0 to 255 + R = constrain(R); + G = constrain(G); + B = constrain(B); + // Set all pixels to new value pixels[byte_index] = R; pixels[byte_index + 1] = G; @@ -127,6 +186,9 @@ Json::Value Saturation::JsonValue() { Json::Value root = EffectBase::JsonValue(); // get parent properties root["type"] = info.class_name; root["saturation"] = saturation.JsonValue(); + root["saturation_R"] = saturation_R.JsonValue(); + root["saturation_G"] = saturation_G.JsonValue(); + root["saturation_B"] = saturation_B.JsonValue(); // return JsonValue return root; @@ -170,6 +232,12 @@ void Saturation::SetJsonValue(Json::Value root) { // Set data from Json (if key is found) if (!root["saturation"].isNull()) saturation.SetJsonValue(root["saturation"]); + if (!root["saturation_R"].isNull()) + saturation_R.SetJsonValue(root["saturation_R"]); + if (!root["saturation_G"].isNull()) + saturation_G.SetJsonValue(root["saturation_G"]); + if (!root["saturation_B"].isNull()) + saturation_B.SetJsonValue(root["saturation_B"]); } // Get all properties for a specific frame @@ -186,6 +254,9 @@ std::string Saturation::PropertiesJSON(int64_t requested_frame) { // Keyframes root["saturation"] = add_property_json("Saturation", saturation.GetValue(requested_frame), "float", "", &saturation, 0.0, 4.0, false, requested_frame); + root["saturation_R"] = add_property_json("Saturation (Red)", saturation_R.GetValue(requested_frame), "float", "", &saturation_R, 0.0, 4.0, false, requested_frame); + root["saturation_G"] = add_property_json("Saturation (Green)", saturation_G.GetValue(requested_frame), "float", "", &saturation_G, 0.0, 4.0, false, requested_frame); + root["saturation_B"] = add_property_json("Saturation (Blue)", saturation_B.GetValue(requested_frame), "float", "", &saturation_B, 0.0, 4.0, false, requested_frame); // Return formatted string return root.toStyledString(); From ffab5705543b3739e1f4b8bcdef0460eb8947952 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sat, 23 Nov 2019 19:10:11 +0100 Subject: [PATCH 02/86] Color-separated Saturation: Enhanced Comment See https://github.com/OpenShot/libopenshot/pull/368#discussion_r349868322. Signed-off-by: Markus KARG --- include/effects/Saturation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 08695fd90..83ee75b2c 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -64,7 +64,7 @@ namespace openshot void init_effect_details(); public: - Keyframe saturation; ///< The color saturation: 0.0 = black and white, 1.0 = normal, 2.0 = double saturation + Keyframe saturation; ///< Overall color saturation: 0.0 = black and white, 1.0 = normal, 2.0 = double saturation Keyframe saturation_R; ///< Red color saturation Keyframe saturation_G; ///< Green color saturation Keyframe saturation_B; ///< Blue color saturation From 09435d3e0c0dc4cb92f231ae584e16939eb81c65 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sat, 23 Nov 2019 19:21:14 +0100 Subject: [PATCH 03/86] Saturation: Enhanced Comment ("greyscale" vs. "black and white") Signed-off-by: Markus KARG --- include/effects/Saturation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 83ee75b2c..495816f67 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -64,7 +64,7 @@ namespace openshot void init_effect_details(); public: - Keyframe saturation; ///< Overall color saturation: 0.0 = black and white, 1.0 = normal, 2.0 = double saturation + Keyframe saturation; ///< Overall color saturation: 0.0 = greyscale, 1.0 = normal, 2.0 = double saturation Keyframe saturation_R; ///< Red color saturation Keyframe saturation_G; ///< Green color saturation Keyframe saturation_B; ///< Blue color saturation @@ -74,7 +74,7 @@ namespace openshot /// Default constructor, which takes 1 curve, to adjust the color saturation over time. /// - /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = black and white, 1.0 = normal, 2.0 = double saturation) + /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) Saturation(Keyframe new_saturation, Keyframe new_saturation_R, Keyframe new_saturation_G, Keyframe new_saturation_B); /// @brief This method is required for all derived classes of EffectBase, and returns a From 9474973c15fb3691eefee2e74a1e7b472342818a Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sat, 23 Nov 2019 19:33:54 +0100 Subject: [PATCH 04/86] Color-separated Saturation: Enhanced constructor parameter names See https://github.com/OpenShot/libopenshot/pull/368#discussion_r349868437. Signed-off-by: Markus KARG --- include/effects/Saturation.h | 4 ++-- src/effects/Saturation.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 495816f67..7badef0ef 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -74,8 +74,8 @@ namespace openshot /// Default constructor, which takes 1 curve, to adjust the color saturation over time. /// - /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) - Saturation(Keyframe new_saturation, Keyframe new_saturation_R, Keyframe new_saturation_G, Keyframe new_saturation_B); + /// @param saturation The curve to adjust the saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) + Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B); /// @brief This method is required for all derived classes of EffectBase, and returns a /// modified openshot::Frame object diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 6762b4e77..567db2d18 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -39,7 +39,7 @@ Saturation::Saturation() : saturation(1.0), saturation_R(1.0), saturation_G(1.0) } // Default constructor -Saturation::Saturation(Keyframe new_saturation, Keyframe new_saturation_R, Keyframe new_saturation_G, Keyframe new_saturation_B) : saturation(new_saturation), saturation_R(new_saturation_R), saturation_G(new_saturation_G), saturation_B(new_saturation_B) +Saturation::Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B) : saturation(saturation), saturation_R(saturation_R), saturation_G(saturation_G), saturation_B(saturation_B) { // Init effect properties init_effect_details(); From 0d7468b0704df1dc985817c32c47ea9ce0a59e4d Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sun, 24 Nov 2019 17:12:17 +0100 Subject: [PATCH 05/86] Color-separated saturation: Documented new constructor parameters Signed-off-by: Markus KARG --- include/effects/Saturation.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 7badef0ef..afd1c91f3 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -75,6 +75,9 @@ namespace openshot /// Default constructor, which takes 1 curve, to adjust the color saturation over time. /// /// @param saturation The curve to adjust the saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) + /// @param saturation_R The curve to adjust red saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) + /// @param saturation_G The curve to adjust green saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) + /// @param saturation_B The curve to adjust blue saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B); /// @brief This method is required for all derived classes of EffectBase, and returns a From 88c04944b1d073b3ece17aca3920763cff6edae0 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sun, 24 Nov 2019 17:15:49 +0100 Subject: [PATCH 06/86] Color-separated Saturation: Enhanced description of constructor Signed-off-by: Markus KARG --- include/effects/Saturation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index afd1c91f3..aba762b4f 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -72,7 +72,7 @@ namespace openshot /// Blank constructor, useful when using Json to load the effect properties Saturation(); - /// Default constructor, which takes 1 curve, to adjust the color saturation over time. + /// Default constructor, which takes four curves (one common curve and one curve per color), to adjust the color saturation over time. /// /// @param saturation The curve to adjust the saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) /// @param saturation_R The curve to adjust red saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) From 3a987eb044e0d755b4a1ae321fd816572e246e27 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sun, 24 Nov 2019 17:21:43 +0100 Subject: [PATCH 07/86] Color-separated Saturation: Line Wrap Signed-off-by: Markus KARG --- src/effects/Saturation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 567db2d18..407f97af0 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -39,7 +39,8 @@ Saturation::Saturation() : saturation(1.0), saturation_R(1.0), saturation_G(1.0) } // Default constructor -Saturation::Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B) : saturation(saturation), saturation_R(saturation_R), saturation_G(saturation_G), saturation_B(saturation_B) +Saturation::Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B) : + saturation(saturation), saturation_R(saturation_R), saturation_G(saturation_G), saturation_B(saturation_B) { // Init effect properties init_effect_details(); From c979f5b0d6655add38016ab058d26569cc49361f Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sun, 24 Nov 2019 18:16:21 +0100 Subject: [PATCH 08/86] Color-separated Saturation: Optimized math (removed no-ops) Signed-off-by: Markus KARG --- src/effects/Saturation.cpp | 39 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 407f97af0..560c4102b 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -118,39 +118,24 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ * of the original subpixel (dependening on the color channel's slider value). */ - // Three subpixels producing either R or white with brightness of R - int Rr = R; - int Gr = 0; - int Br = 0; - - // Three subpixels producing either G or white with brightness of G - int Rg = 0; - int Gg = G; - int Bg = 0; - - // Three subpixels producing either B or white with brightness of B - int Rb = 0; - int Gb = 0; - int Bb = B; - // Compute the brightness ("saturation multiplier") of the replaced subpixels // Actually mathematical no-ops mostly, verbosity is kept just for clarification - const double p_r = sqrt( (Rr * Rr * pR) + (Gr * Gr * pG) + (Br * Br * pB) ); - const double p_g = sqrt( (Rg * Rg * pR) + (Gg * Gg * pG) + (Bg * Bg * pB) ); - const double p_b = sqrt( (Rb * Rb * pR) + (Gb * Gb * pG) + (Bb * Bb * pB) ); + const double p_r = sqrt(R * R * pR); + const double p_g = sqrt(G * G * pG); + const double p_b = sqrt(B * B * pB); // Adjust the saturation - Rr = p_r + (Rr - p_r) * saturation_value_R; - Gr = p_r + (Gr - p_r) * saturation_value_R; - Br = p_r + (Br - p_r) * saturation_value_R; + const int Rr = p_r + (R - p_r) * saturation_value_R; + const int Gr = p_r + (0 - p_r) * saturation_value_R; + const int Br = p_r + (0 - p_r) * saturation_value_R; - Rg = p_g + (Rg - p_g) * saturation_value_G; - Gg = p_g + (Gg - p_g) * saturation_value_G; - Bg = p_g + (Bg - p_g) * saturation_value_G; + const int Rg = p_g + (0 - p_g) * saturation_value_G; + const int Gg = p_g + (G - p_g) * saturation_value_G; + const int Bg = p_g + (0 - p_g) * saturation_value_G; - Rb = p_b + (Rb - p_b) * saturation_value_B; - Gb = p_b + (Gb - p_b) * saturation_value_B; - Bb = p_b + (Bb - p_b) * saturation_value_B; + const int Rb = p_b + (0 - p_b) * saturation_value_B; + const int Gb = p_b + (0 - p_b) * saturation_value_B; + const int Bb = p_b + (B - p_b) * saturation_value_B; // Recombine brightness of sub-subpixels (Rx, Gx and Bx) into sub-pixels (R, G and B) again R = Rr + Rg + Rb; From 5764b03544b8a14f9f3669e14db4740c23c9d06e Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 4 Mar 2020 23:52:42 -0500 Subject: [PATCH 09/86] FFmpegWriter: Use std::clog for output - Eliminate fprintf(stderr...) and non-threadsafe std::cerr - Replace with thread-safe std:clog or ZeroMQ calls - Clean up some formatting & ZeroMQ logging --- include/FFmpegWriter.h | 2 -- src/FFmpegWriter.cpp | 53 +++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index 1dfb21a93..252c1c43c 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -47,8 +47,6 @@ #include #include -#include -#include #include #include "CacheMemory.h" #include "Exceptions.h" diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index e6a1d180a..dd36bed08 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -33,6 +33,8 @@ #include "../include/FFmpegWriter.h" +#include + using namespace openshot; #if HAVE_HW_ACCEL @@ -59,7 +61,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 int err = 0; if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { - fprintf(stderr, "Failed to create HW frame context.\n"); + std::clog << "Failed to create HW frame context.\n"; return -1; } frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); @@ -69,8 +71,8 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 frames_ctx->height = height; frames_ctx->initial_pool_size = 20; if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) { - fprintf(stderr, "Failed to initialize HW frame context." - "Error code: %s\n",av_err2str(err)); + std::clog << "Failed to initialize HW frame context. " << + "Error code: " << av_err2str(err) << "\n"; av_buffer_unref(&hw_frames_ref); return err; } @@ -1365,7 +1367,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { int adapter_num; // Use the hw device given in the environment variable HW_EN_DEVICE_SET or the default if not set adapter_num = openshot::Settings::Instance()->HW_EN_DEVICE_SET; - fprintf(stderr, "\n\nEncodiing Device Nr: %d\n", adapter_num); + std::clog << "Encoding Device Nr: " << adapter_num << "\n"; if (adapter_num < 3 && adapter_num >=0) { #if defined(__linux__) snprintf(adapter,sizeof(adapter),"/dev/dri/renderD%d", adapter_num+128); @@ -1392,11 +1394,11 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { } else { adapter_ptr = NULL; // use default - ZmqLogger::Instance()->AppendDebugMethod("Encode Device not present using default"); + ZmqLogger::Instance()->AppendDebugMethod("Encode Device not present, using default"); } if (av_hwdevice_ctx_create(&hw_device_ctx, hw_en_av_device_type, adapter_ptr, NULL, 0) < 0) { - ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video : Codec name: ", info.vcodec.c_str(), -1, " ERROR creating\n", -1); + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video ERROR creating hwdevice, Codec name:", info.vcodec.c_str(), -1); throw InvalidCodec("Could not create hwdevice", path); } } @@ -1817,10 +1819,7 @@ void FFmpegWriter::write_audio_packets(bool is_final) { pkt.flags |= AV_PKT_FLAG_KEY; /* write the compressed frame in the media file */ - int error_code = av_interleaved_write_frame(oc, &pkt); - if (error_code < 0) { - ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code); - } + error_code = av_interleaved_write_frame(oc, &pkt); } if (error_code < 0) { @@ -1998,17 +1997,17 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { if (!(hw_frame = av_frame_alloc())) { - fprintf(stderr, "Error code: av_hwframe_alloc\n"); + std::clog << "Error code: av_hwframe_alloc\n"; } if (av_hwframe_get_buffer(video_codec->hw_frames_ctx, hw_frame, 0) < 0) { - fprintf(stderr, "Error code: av_hwframe_get_buffer\n"); + std::clog << "Error code: av_hwframe_get_buffer\n"; } if (!hw_frame->hw_frames_ctx) { - fprintf(stderr, "Error hw_frames_ctx.\n"); + std::clog << "Error hw_frames_ctx.\n"; } hw_frame->format = AV_PIX_FMT_NV12; if ( av_hwframe_transfer_data(hw_frame, frame_final, 0) < 0) { - fprintf(stderr, "Error while transferring frame data to surface.\n"); + std::clog << "Error while transferring frame data to surface.\n"; } av_frame_copy_props(hw_frame, frame_final); } @@ -2031,13 +2030,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra } error_code = ret; if (ret < 0 ) { - ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame not sent)"); - if (ret == AVERROR(EAGAIN) ) { - std::cerr << "Frame EAGAIN" << "\n"; - } - if (ret == AVERROR_EOF ) { - std::cerr << "Frame AVERROR_EOF" << "\n"; - } + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame not sent) ERROR [" + (std::string) av_err2str(ret) + "]", "ret", ret); avcodec_send_frame(video_codec, NULL); } else { @@ -2060,10 +2053,10 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // Write video packet (older than FFmpeg 3.2) error_code = avcodec_encode_video2(video_codec, &pkt, frame_final, &got_packet_ptr); if (error_code != 0) { - std::cerr << "Frame AVERROR_EOF" << "\n"; + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code); } if (got_packet_ptr == 0) { - std::cerr << "Frame gotpacket error" << "\n"; + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame gotpacket error)"); } #else // Write video packet (even older versions of FFmpeg) @@ -2103,9 +2096,9 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra pkt.stream_index = video_st->index; /* write the compressed frame in the media file */ - int error_code = av_interleaved_write_frame(oc, &pkt); - if (error_code < 0) { - ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code); + int result = av_interleaved_write_frame(oc, &pkt); + if (result < 0) { + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(result) + "]", "result", result); return false; } } @@ -2148,12 +2141,14 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) { // Init the software scaler from FFMpeg #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { - img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_PIX_FMT_NV12, scale_mode, NULL, NULL, NULL); + img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, + info.width, info.height, AV_PIX_FMT_NV12, scale_mode, NULL, NULL, NULL); } else #endif // HAVE_HW_ACCEL { - img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(video_st, video_st->codec), scale_mode, - NULL, NULL, NULL); + img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, + info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(video_st, video_st->codec), + scale_mode, NULL, NULL, NULL); } // Add rescaler to vector From 3a88d218662e4487ca3ce5dff036600a02bfc596 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 4 Mar 2020 23:58:16 -0500 Subject: [PATCH 10/86] FFmpegWriter: Drop ancient FFmpeg code - Drop any FFmpeg version checks for versions older than 2.6 - Remove code that was only used in FFmpeg 2.4 or lower - Add human-readable version comments to checks for LIBFOO_VERSION_MAJOR. etc. --- src/FFmpegWriter.cpp | 268 ++++++++++++++++--------------------------- 1 file changed, 97 insertions(+), 171 deletions(-) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index dd36bed08..245b01086 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -222,10 +222,10 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f hw_en_on = 0; hw_en_supported = 0; } - #else // is FFmpeg 3 but not linux +#else // unknown OS new_codec = avcodec_find_encoder_by_name(codec.c_str()); - #endif //__linux__ -#else // not ffmpeg 3 +#endif //__linux__/_WIN32/__APPLE__ +#else // HAVE_HW_ACCEL new_codec = avcodec_find_encoder_by_name(codec.c_str()); #endif // HAVE_HW_ACCEL if (new_codec == NULL) @@ -410,57 +410,54 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va // encode quality and special settings like lossless // This might be better in an extra methods as more options // and way to set quality are possible - #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) - #if HAVE_HW_ACCEL - if (hw_en_on) { - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 - } else - #endif // HAVE_HW_ACCEL - { + #if HAVE_HW_ACCEL + if (hw_en_on) { + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 + } else + #endif // HAVE_HW_ACCEL + { switch (c->codec_id) { - #if (LIBAVCODEC_VERSION_MAJOR >= 58) - case AV_CODEC_ID_AV1 : - c->bit_rate = 0; - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 - break; - #endif - case AV_CODEC_ID_VP8 : - c->bit_rate = 10000000; - av_opt_set_int(c->priv_data, "qp", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63 - break; - case AV_CODEC_ID_VP9 : - c->bit_rate = 0; // Must be zero! - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - av_opt_set_int(c->priv_data, "lossless", 1, 0); - } - break; - case AV_CODEC_ID_H264 : - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 51), 0); // 0-51 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - } - break; - case AV_CODEC_ID_HEVC : - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 51), 0); // 0-51 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - av_opt_set_int(c->priv_data, "lossless", 1, 0); - } - break; - default: - // For all other codecs assume a range of 0-63 - av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63 - c->bit_rate = 0; + #if (LIBAVCODEC_VERSION_MAJOR >= 58) + case AV_CODEC_ID_AV1 : + c->bit_rate = 0; + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 + break; + #endif + case AV_CODEC_ID_VP8 : + c->bit_rate = 10000000; + av_opt_set_int(c->priv_data, "qp", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63 + break; + case AV_CODEC_ID_VP9 : + c->bit_rate = 0; // Must be zero! + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + av_opt_set_int(c->priv_data, "lossless", 1, 0); + } + break; + case AV_CODEC_ID_H264 : + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 51), 0); // 0-51 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + } + break; + case AV_CODEC_ID_HEVC : + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 51), 0); // 0-51 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + av_opt_set_int(c->priv_data, "lossless", 1, 0); + } + break; + default: + // For all other codecs assume a range of 0-63 + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63 + c->bit_rate = 0; } } - #endif } else if (name == "crf") { // encode quality and special settings like lossless // This might be better in an extra methods as more options // and way to set quality are possible -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) #if HAVE_HW_ACCEL if (hw_en_on) { double mbs = 15000000.0; @@ -478,51 +475,50 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) - case AV_CODEC_ID_AV1 : - c->bit_rate = 0; - av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0); - break; + case AV_CODEC_ID_AV1 : + c->bit_rate = 0; + av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0); + break; #endif - case AV_CODEC_ID_VP8 : - c->bit_rate = 10000000; - av_opt_set_int(c->priv_data, "crf", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63 - break; - case AV_CODEC_ID_VP9 : - c->bit_rate = 0; // Must be zero! - av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 63), 0); // 0-63 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - av_opt_set_int(c->priv_data, "lossless", 1, 0); - } - break; - case AV_CODEC_ID_H264 : - av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - } - break; - case AV_CODEC_ID_HEVC : - av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51 - if (std::stoi(value) == 0) { - av_opt_set(c->priv_data, "preset", "veryslow", 0); - av_opt_set_int(c->priv_data, "lossless", 1, 0); - } - break; - default: - // If this codec doesn't support crf calculate a bitrate - // TODO: find better formula - double mbs = 15000000.0; - if (info.video_bit_rate > 0) { - if (info.video_bit_rate > 42) { - mbs = 380000.0; - } else { - mbs *= std::pow(0.912, info.video_bit_rate); - } + case AV_CODEC_ID_VP8 : + c->bit_rate = 10000000; + av_opt_set_int(c->priv_data, "crf", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63 + break; + case AV_CODEC_ID_VP9 : + c->bit_rate = 0; // Must be zero! + av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 63), 0); // 0-63 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + av_opt_set_int(c->priv_data, "lossless", 1, 0); + } + break; + case AV_CODEC_ID_H264 : + av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + } + break; + case AV_CODEC_ID_HEVC : + av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51 + if (std::stoi(value) == 0) { + av_opt_set(c->priv_data, "preset", "veryslow", 0); + av_opt_set_int(c->priv_data, "lossless", 1, 0); + } + break; + default: + // If this codec doesn't support crf calculate a bitrate + // TODO: find better formula + double mbs = 15000000.0; + if (info.video_bit_rate > 0) { + if (info.video_bit_rate > 42) { + mbs = 380000.0; + } else { + mbs *= std::pow(0.912, info.video_bit_rate); } - c->bit_rate = (int) (mbs); + } + c->bit_rate = (int) (mbs); } } -#endif } else { // Set AVOption AV_OPTION_SET(st, c->priv_data, name.c_str(), value.c_str(), c); @@ -635,15 +631,8 @@ void FFmpegWriter::WriteFrame(std::shared_ptr frame) { // Write the frames once it reaches the correct cache size if ((int)spooled_video_frames.size() == cache_size || (int)spooled_audio_frames.size() == cache_size) { - // Is writer currently writing? - if (!is_writing) - // Write frames to video file - write_queued_frames(); - - else { - // Write frames to video file - write_queued_frames(); - } + // Write frames to video file + write_queued_frames(); } // Keep track of the last frame added @@ -801,6 +790,7 @@ void FFmpegWriter::flush_encoders() { if (info.has_audio && audio_codec && AV_GET_CODEC_TYPE(audio_st) == AVMEDIA_TYPE_AUDIO && AV_GET_CODEC_ATTRIBUTES(audio_st, audio_codec)->frame_size <= 1) return; #if (LIBAVFORMAT_VERSION_MAJOR < 58) + // FFmpeg < 4.0 if (info.has_video && video_codec && AV_GET_CODEC_TYPE(video_st) == AVMEDIA_TYPE_VIDEO && (oc->oformat->flags & AVFMT_RAWPICTURE) && AV_FIND_DECODER_CODEC_ID(video_st) == AV_CODEC_ID_RAWVIDEO) return; #endif @@ -820,9 +810,6 @@ void FFmpegWriter::flush_encoders() { pkt.data = NULL; pkt.size = 0; - // Pointer for video buffer (if using old FFmpeg version) - uint8_t *video_outbuf = NULL; - /* encode the image */ int got_packet = 0; int error_code = 0; @@ -853,28 +840,9 @@ void FFmpegWriter::flush_encoders() { } #else // IS_FFMPEG_3_2 -#if LIBAVFORMAT_VERSION_MAJOR >= 54 // Encode video packet (older than FFmpeg 3.2) error_code = avcodec_encode_video2(video_codec, &pkt, NULL, &got_packet); -#else - // Encode video packet (even older version of FFmpeg) - int video_outbuf_size = 0; - - /* encode the image */ - int out_size = avcodec_encode_video(video_codec, NULL, video_outbuf_size, NULL); - - /* if zero size, it means the image was buffered */ - if (out_size > 0) { - if(video_codec->coded_frame->key_frame) - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.data= video_outbuf; - pkt.size= out_size; - - // got data back (so encode this frame) - got_packet = 1; - } -#endif // LIBAVFORMAT_VERSION_MAJOR >= 54 #endif // IS_FFMPEG_3_2 if (error_code < 0) { @@ -903,9 +871,6 @@ void FFmpegWriter::flush_encoders() { ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::flush_encoders ERROR [" + (std::string)av_err2str(error_code) + "]", "error_code", error_code); } - // Deallocate memory (if needed) - if (video_outbuf) - av_freep(&video_outbuf); } // FLUSH AUDIO ENCODER @@ -913,12 +878,8 @@ void FFmpegWriter::flush_encoders() { for (;;) { // Increment PTS (in samples and scaled to the codec's timebase) -#if LIBAVFORMAT_VERSION_MAJOR >= 54 // for some reason, it requires me to multiply channels X 2 write_audio_count += av_rescale_q(audio_input_position / (audio_codec->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)), (AVRational){1, info.sample_rate}, audio_codec->time_base); -#else - write_audio_count += av_rescale_q(audio_input_position / audio_codec->channels, (AVRational){1, info.sample_rate}, audio_codec->time_base); -#endif AVPacket pkt; av_init_packet(&pkt); @@ -1073,11 +1034,7 @@ AVStream *FFmpegWriter::add_audio_stream() { AV_FORMAT_NEW_STREAM(oc, audio_codec, codec, st) c->codec_id = codec->id; -#if LIBAVFORMAT_VERSION_MAJOR >= 53 c->codec_type = AVMEDIA_TYPE_AUDIO; -#else - c->codec_type = CODEC_TYPE_AUDIO; -#endif // Set the sample parameters c->bit_rate = info.audio_bit_rate; @@ -1131,6 +1088,7 @@ AVStream *FFmpegWriter::add_audio_stream() { // some formats want stream headers to be separate if (oc->oformat->flags & AVFMT_GLOBALHEADER) #if (LIBAVCODEC_VERSION_MAJOR >= 57) + // FFmpeg 3.0+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else c->flags |= CODEC_FLAG_GLOBAL_HEADER; @@ -1156,11 +1114,7 @@ AVStream *FFmpegWriter::add_video_stream() { AV_FORMAT_NEW_STREAM(oc, video_codec, codec, st) c->codec_id = codec->id; -#if LIBAVFORMAT_VERSION_MAJOR >= 53 c->codec_type = AVMEDIA_TYPE_VIDEO; -#else - c->codec_type = CODEC_TYPE_VIDEO; -#endif /* Init video encoder options */ if (info.video_bit_rate >= 1000) { @@ -1174,13 +1128,12 @@ AVStream *FFmpegWriter::add_video_stream() { } else { // Check if codec supports crf switch (c->codec_id) { -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) #if (LIBAVCODEC_VERSION_MAJOR >= 58) + // FFmpeg 4.0+ case AV_CODEC_ID_AV1 : #endif case AV_CODEC_ID_VP9 : case AV_CODEC_ID_HEVC : -#endif case AV_CODEC_ID_VP8 : case AV_CODEC_ID_H264 : if (info.video_bit_rate < 40) { @@ -1217,7 +1170,7 @@ AVStream *FFmpegWriter::add_video_stream() { identically 1. */ c->time_base.num = info.video_timebase.num; c->time_base.den = info.video_timebase.den; -// AVCodecContext->framerate was added in FFmpeg 2.2 +// AVCodecContext->framerate was added in FFmpeg 2.6 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 26, 0) c->framerate = av_inv_q(c->time_base); #endif @@ -1238,6 +1191,7 @@ AVStream *FFmpegWriter::add_video_stream() { // some formats want stream headers to be separate if (oc->oformat->flags & AVFMT_GLOBALHEADER) #if (LIBAVCODEC_VERSION_MAJOR >= 57) + // FFmpeg 3.0+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else c->flags |= CODEC_FLAG_GLOBAL_HEADER; @@ -1259,6 +1213,7 @@ AVStream *FFmpegWriter::add_video_stream() { c->pix_fmt = PIX_FMT_RGB24; #if (LIBAVFORMAT_VERSION_MAJOR < 58) + // FFmpeg < 4.0 if (strcmp(fmt->name, "gif") != 0) // If not GIF format, skip the encoding process // Set raw picture flag (so we don't encode this video) @@ -1272,6 +1227,7 @@ AVStream *FFmpegWriter::add_video_stream() { AV_COPY_PARAMS_FROM_CONTEXT(st, c); #if (LIBAVFORMAT_VERSION_MAJOR < 58) + // FFmpeg < 4.0 ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::add_video_stream (" + (std::string)fmt->name + " : " + (std::string)av_get_pix_fmt_name(c->pix_fmt) + ")", "c->codec_id", c->codec_id, "c->bit_rate", c->bit_rate, "c->pix_fmt", c->pix_fmt, "oc->oformat->flags", oc->oformat->flags, "AVFMT_RAWPICTURE", AVFMT_RAWPICTURE); #else ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::add_video_stream (" + (std::string)fmt->name + " : " + (std::string)av_get_pix_fmt_name(c->pix_fmt) + ")", "c->codec_id", c->codec_id, "c->bit_rate", c->bit_rate, "c->pix_fmt", c->pix_fmt, "oc->oformat->flags", oc->oformat->flags); @@ -1373,9 +1329,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { snprintf(adapter,sizeof(adapter),"/dev/dri/renderD%d", adapter_num+128); // Maybe 127 is better because the first card would be 1?! adapter_ptr = adapter; -#elif defined(_WIN32) - adapter_ptr = NULL; -#elif defined(__APPLE__) +#elif defined(_WIN32) || defined(__APPLE__) adapter_ptr = NULL; #endif } @@ -1385,9 +1339,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { // Check if it is there and writable #if defined(__linux__) if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) { -#elif defined(_WIN32) - if( adapter_ptr != NULL ) { -#elif defined(__APPLE__) +#elif defined(_WIN32) || defined(__APPLE__) if( adapter_ptr != NULL ) { #endif ZmqLogger::Instance()->AppendDebugMethod("Encode Device present using device", "adapter", adapter_num); @@ -1948,6 +1900,7 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) { // write video frame bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *frame_final) { #if (LIBAVFORMAT_VERSION_MAJOR >= 58) + // FFmpeg 4.0+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet", "frame->number", frame->number, "oc->oformat->flags", oc->oformat->flags); #else ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet", "frame->number", frame->number, "oc->oformat->flags & AVFMT_RAWPICTURE", oc->oformat->flags & AVFMT_RAWPICTURE); @@ -1977,7 +1930,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra AV_FREE_PACKET(&pkt); } else -#endif +#endif // LIBAVFORMAT_VERSION_MAJOR >= 58 { AVPacket pkt; @@ -1986,9 +1939,6 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra pkt.size = 0; pkt.pts = pkt.dts = AV_NOPTS_VALUE; - // Pointer for video buffer (if using old FFmpeg version) - uint8_t *video_outbuf = NULL; - // Increment PTS (in frames and scaled to the codec's timebase) write_video_count += av_rescale_q(1, (AVRational) {info.fps.den, info.fps.num}, video_codec->time_base); @@ -2049,7 +1999,6 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra } } #else -#if LIBAVFORMAT_VERSION_MAJOR >= 54 // Write video packet (older than FFmpeg 3.2) error_code = avcodec_encode_video2(video_codec, &pkt, frame_final, &got_packet_ptr); if (error_code != 0) { @@ -2058,25 +2007,6 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra if (got_packet_ptr == 0) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame gotpacket error)"); } -#else - // Write video packet (even older versions of FFmpeg) - int video_outbuf_size = 200000; - video_outbuf = (uint8_t*) av_malloc(200000); - - /* encode the image */ - int out_size = avcodec_encode_video(video_codec, video_outbuf, video_outbuf_size, frame_final); - - /* if zero size, it means the image was buffered */ - if (out_size > 0) { - if(video_codec->coded_frame->key_frame) - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.data= video_outbuf; - pkt.size= out_size; - - // got data back (so encode this frame) - got_packet_ptr = 1; - } -#endif // LIBAVFORMAT_VERSION_MAJOR >= 54 #endif // IS_FFMPEG_3_2 /* if zero size, it means the image was buffered */ @@ -2103,10 +2033,6 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra } } - // Deallocate memory (if needed) - if (video_outbuf) - delete[] video_outbuf; - // Deallocate packet AV_FREE_PACKET(&pkt); #if HAVE_HW_ACCEL From 92ebd15545c52622dd060d8f693209ac058cd5b6 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Thu, 5 Mar 2020 01:38:25 -0500 Subject: [PATCH 11/86] FFmpegWriter: Code reformatting --- src/FFmpegWriter.cpp | 109 +++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 34 deletions(-) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 245b01086..e8e9542c1 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -255,10 +255,14 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f info.pixel_ratio.num = pixel_ratio.num; info.pixel_ratio.den = pixel_ratio.den; } - if (bit_rate >= 1000) // bit_rate is the bitrate in b/s + if (bit_rate >= 1000) { + // bit_rate is the bitrate in b/s info.video_bit_rate = bit_rate; - if ((bit_rate >= 0) && (bit_rate < 64)) // bit_rate is the bitrate in crf + } + if ((bit_rate >= 0) && (bit_rate < 64)) { + // bit_rate is the bitrate in crf info.video_bit_rate = bit_rate; + } info.interlaced_frame = interlaced; info.top_field_first = top_field_first; @@ -282,8 +286,10 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f // Set video export options (overloaded function) void FFmpegWriter::SetVideoOptions(std::string codec, int width, int height, Fraction fps, int bit_rate) { // Call full signature with some default parameters - FFmpegWriter::SetVideoOptions(true, codec, fps, width, height, - openshot::Fraction(1, 1), false, true, bit_rate); + FFmpegWriter::SetVideoOptions( + true, codec, fps, width, height, + openshot::Fraction(1, 1), false, true, bit_rate + ); } @@ -326,8 +332,10 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample // Set audio export options (overloaded function) void FFmpegWriter::SetAudioOptions(std::string codec, int sample_rate, int bit_rate) { // Call full signature with some default parameters - FFmpegWriter::SetAudioOptions(true, codec, sample_rate, 2, - openshot::LAYOUT_STEREO, bit_rate); + FFmpegWriter::SetAudioOptions( + true, codec, sample_rate, 2, + openshot::LAYOUT_STEREO, bit_rate + ); } @@ -418,6 +426,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) + // FFmpeg 4.0+ case AV_CODEC_ID_AV1 : c->bit_rate = 0; av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 @@ -475,6 +484,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) + // FFmpeg 4.0+ case AV_CODEC_ID_AV1 : c->bit_rate = 0; av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0); @@ -1570,20 +1580,25 @@ void FFmpegWriter::write_audio_packets(bool is_final) { int nb_samples = 0; // Convert audio samples - nb_samples = SWR_CONVERT(avr, // audio resample context - audio_converted->data, // output data pointers - audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown) - audio_converted->nb_samples, // maximum number of samples that the output buffer can hold - audio_frame->data, // input data pointers - audio_frame->linesize[0], // input plane size, in bytes (0 if unknown) - audio_frame->nb_samples); // number of input samples to convert + nb_samples = SWR_CONVERT( + avr, // audio resample context + audio_converted->data, // output data pointers + audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown) + audio_converted->nb_samples, // maximum number of samples that the output buffer can hold + audio_frame->data, // input data pointers + audio_frame->linesize[0], // input plane size, in bytes (0 if unknown) + audio_frame->nb_samples // number of input samples to convert + ); // Set remaining samples remaining_frame_samples = nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); // Create a new array (to hold all resampled S16 audio samples) all_resampled_samples = (int16_t *) av_malloc( - sizeof(int16_t) * nb_samples * info.channels * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))); + sizeof(int16_t) * nb_samples * info.channels + * (av_get_bytes_per_sample(output_sample_fmt) / + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) ) + ); // Copy audio samples over original samples memcpy(all_resampled_samples, audio_converted->data[0], nb_samples * info.channels * av_get_bytes_per_sample(output_sample_fmt)); @@ -1613,8 +1628,14 @@ void FFmpegWriter::write_audio_packets(bool is_final) { // Copy frame samples into the packet samples array if (!is_final) //TODO: Make this more sane - memcpy(samples + (audio_input_position * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))), - all_resampled_samples + samples_position, diff * av_get_bytes_per_sample(output_sample_fmt)); + memcpy( + samples + (audio_input_position + * (av_get_bytes_per_sample(output_sample_fmt) / + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) ) + ), + all_resampled_samples + samples_position, + diff * av_get_bytes_per_sample(output_sample_fmt) + ); // Increment counters audio_input_position += diff; @@ -1631,7 +1652,14 @@ void FFmpegWriter::write_audio_packets(bool is_final) { AVFrame *frame_final = AV_ALLOCATE_FRAME(); AV_RESET_FRAME(frame_final); if (av_sample_fmt_is_planar(audio_codec->sample_fmt)) { - ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets (2nd resampling for Planar formats)", "in_sample_fmt", output_sample_fmt, "out_sample_fmt", audio_codec->sample_fmt, "in_sample_rate", info.sample_rate, "out_sample_rate", info.sample_rate, "in_channels", info.channels, "out_channels", info.channels); + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets (2nd resampling for Planar formats)", + "in_sample_fmt", output_sample_fmt, + "out_sample_fmt", audio_codec->sample_fmt, + "in_sample_rate", info.sample_rate, + "out_sample_rate", info.sample_rate, + "in_channels", info.channels, + "out_channels", info.channels + ); // setup resample context if (!avr_planar) { @@ -1654,31 +1682,38 @@ void FFmpegWriter::write_audio_packets(bool is_final) { // Create a new array final_samples_planar = (int16_t *) av_malloc( - sizeof(int16_t) * audio_frame->nb_samples * info.channels * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))); + sizeof(int16_t) * audio_frame->nb_samples * info.channels + * (av_get_bytes_per_sample(output_sample_fmt) / + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) ) + ); // Copy audio into buffer for frame memcpy(final_samples_planar, samples, audio_frame->nb_samples * info.channels * av_get_bytes_per_sample(output_sample_fmt)); // Fill input frame with sample data - avcodec_fill_audio_frame(audio_frame, info.channels, output_sample_fmt, (uint8_t *) final_samples_planar, - audio_encoder_buffer_size, 0); + avcodec_fill_audio_frame(audio_frame, info.channels, output_sample_fmt, + (uint8_t *) final_samples_planar, audio_encoder_buffer_size, 0); // Create output frame (and allocate arrays) frame_final->nb_samples = audio_input_frame_size; - av_samples_alloc(frame_final->data, frame_final->linesize, info.channels, frame_final->nb_samples, audio_codec->sample_fmt, 0); + av_samples_alloc(frame_final->data, frame_final->linesize, info.channels, + frame_final->nb_samples, audio_codec->sample_fmt, 0); // Convert audio samples - int nb_samples = SWR_CONVERT(avr_planar, // audio resample context - frame_final->data, // output data pointers - frame_final->linesize[0], // output plane size, in bytes. (0 if unknown) - frame_final->nb_samples, // maximum number of samples that the output buffer can hold - audio_frame->data, // input data pointers - audio_frame->linesize[0], // input plane size, in bytes (0 if unknown) - audio_frame->nb_samples); // number of input samples to convert + int nb_samples = SWR_CONVERT( + avr_planar, // audio resample context + frame_final->data, // output data pointers + frame_final->linesize[0], // output plane size, in bytes. (0 if unknown) + frame_final->nb_samples, // maximum number of samples that the output buffer can hold + audio_frame->data, // input data pointers + audio_frame->linesize[0], // input plane size, in bytes (0 if unknown) + audio_frame->nb_samples // number of input samples to convert + ); // Copy audio samples over original samples if (nb_samples > 0) - memcpy(samples, frame_final->data[0], nb_samples * av_get_bytes_per_sample(audio_codec->sample_fmt) * info.channels); + memcpy(samples, frame_final->data[0], + nb_samples * av_get_bytes_per_sample(audio_codec->sample_fmt) * info.channels); // deallocate AVFrame av_freep(&(audio_frame->data[0])); @@ -1690,7 +1725,10 @@ void FFmpegWriter::write_audio_packets(bool is_final) { } else { // Create a new array final_samples = (int16_t *) av_malloc( - sizeof(int16_t) * audio_input_position * (av_get_bytes_per_sample(audio_codec->sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))); + sizeof(int16_t) * audio_input_position + * (av_get_bytes_per_sample(audio_codec->sample_fmt) / + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) ) + ); // Copy audio into buffer for frame memcpy(final_samples, samples, audio_input_position * av_get_bytes_per_sample(audio_codec->sample_fmt)); @@ -1699,8 +1737,8 @@ void FFmpegWriter::write_audio_packets(bool is_final) { frame_final->nb_samples = audio_input_frame_size; // Fill the final_frame AVFrame with audio (non planar) - avcodec_fill_audio_frame(frame_final, audio_codec->channels, audio_codec->sample_fmt, (uint8_t *) final_samples, - audio_encoder_buffer_size, 0); + avcodec_fill_audio_frame(frame_final, audio_codec->channels, audio_codec->sample_fmt, + (uint8_t *) final_samples, audio_encoder_buffer_size, 0); } // Increment PTS (in samples) @@ -1872,7 +1910,10 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) { } else #endif // HAVE_HW_ACCEL { - frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL); + frame_final = allocate_avframe( + (AVPixelFormat)(video_st->codecpar->format), + info.width, info.height, &bytes_final, NULL + ); } #else AVFrame *frame_final = allocate_avframe(video_codec->pix_fmt, info.width, info.height, &bytes_final, NULL); @@ -1884,7 +1925,7 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) { // Resize & convert pixel format sws_scale(scaler, frame_source->data, frame_source->linesize, 0, - source_image_height, frame_final->data, frame_final->linesize); + source_image_height, frame_final->data, frame_final->linesize); // Add resized AVFrame to av_frames map #pragma omp critical (av_frames_section) From 8c8b535cc9e83e873c5da1092cc4d6cd31d03c19 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 27 Mar 2020 16:28:43 -0400 Subject: [PATCH 12/86] Fix Win64 Gitlab builds --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6b2645d81..925bf0201 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,7 +72,6 @@ windows-builder-x64: - cd build - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR\build\install-x64" -D"PYTHON_MODULE_PATH=python" -D"RUBY_MODULE_PATH=ruby" -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make -D"CMAKE_BUILD_TYPE:STRING=Release" ../ - mingw32-make install - - Move-Item -Force -path "install-x64\lib\python3.7\site-packages\*openshot*" -destination "install-x64\python\" - New-Item -path "install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" From ff5095c64d061df1041d2ae5242a2f7dd007c956 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 27 Mar 2020 17:30:42 -0400 Subject: [PATCH 13/86] Update release-log formatting on builders --- .gitlab-ci.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 925bf0201..87de2e165 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,9 @@ stages: - build-libopenshot - trigger-openshot-qt +variables: + GIT_LOG_FORMAT: "- %h %ad %s [%aN]" + linux-builder: stage: build-libopenshot artifacts: @@ -22,7 +25,7 @@ linux-builder: - make doc - ~/auto-update-docs "$CI_PROJECT_DIR/build" "$CI_COMMIT_REF_NAME" - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -47,7 +50,7 @@ mac-builder: - make - make install - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -74,7 +77,7 @@ windows-builder-x64: - mingw32-make install - New-Item -path "install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -101,7 +104,7 @@ windows-builder-x86: - mingw32-make install - New-Item -path "install-x86/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x86/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x86/share/$CI_PROJECT_NAME.log" when: always except: - tags From 7a91ca1d866216e0086d46a430d5666c5525c620 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 8 Jun 2020 15:58:17 -0400 Subject: [PATCH 14/86] FFmpegWriter: Remove unreachable branch --- src/FFmpegWriter.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index e6a7e5ef3..3366ac5f1 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -552,7 +552,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va } else if (strstr(info.vcodec.c_str(), "rav1e") != NULL) { // Set number of tiles to a fixed value - // TODO Let user choose number of tiles + // TODO Let user choose number of tiles av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),255), 0); av_opt_set_int(c->priv_data, "speed", 7, 0); av_opt_set_int(c->priv_data, "tile-rows", 2, 0); // number of rows @@ -560,7 +560,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va } else if (strstr(info.vcodec.c_str(), "aom") != NULL) { // Set number of tiles to a fixed value - // TODO Let user choose number of tiles + // TODO Let user choose number of tiles // libaom doesn't have qp only crf av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0); av_opt_set_int(c->priv_data, "tile-rows", 1, 0); // log2 of number of rows @@ -691,15 +691,8 @@ void FFmpegWriter::WriteFrame(std::shared_ptr frame) { // Write the frames once it reaches the correct cache size if ((int)spooled_video_frames.size() == cache_size || (int)spooled_audio_frames.size() == cache_size) { - // Is writer currently writing? - if (!is_writing) - // Write frames to video file - write_queued_frames(); - - else { - // Write frames to video file - write_queued_frames(); - } + // Write frames to video file + write_queued_frames(); } // Keep track of the last frame added From c57f8ede3b54776b399b6671f284f7946df757fc Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 8 Jun 2020 16:02:02 -0400 Subject: [PATCH 15/86] FFmpeg: Combine 2 constructors via a default arg, fix docs, const refs --- include/FFmpegReader.h | 15 +++++++-------- include/FFmpegWriter.h | 6 ++++-- src/FFmpegReader.cpp | 23 +---------------------- src/FFmpegWriter.cpp | 2 +- 4 files changed, 13 insertions(+), 33 deletions(-) diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index ec0829652..35e11a426 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -233,14 +233,13 @@ namespace openshot { /// codecs have trouble seeking, and can introduce artifacts or blank images into the video. bool enable_seek; - /// Constructor for FFmpegReader. This automatically opens the media file and loads - /// frame 1, or it throws one of the following exceptions. - FFmpegReader(std::string path); - - /// Constructor for FFmpegReader. This only opens the media file to inspect its properties - /// if inspect_reader=true. When not inspecting the media file, it's much faster, and useful - /// when you are inflating the object using JSON after instantiating it. - FFmpegReader(std::string path, bool inspect_reader); + /// @brief Constructor for FFmpegReader. + /// + /// Sets (and possibly opens) the media file path, + /// or throws an exception. + /// @param path The filesystem location to load + /// @param inspect_reader if true (the default), automatically open the media file and loads frame 1. + FFmpegReader(const std::string& path, bool inspect_reader=true); /// Destructor virtual ~FFmpegReader(); diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index 37fa22dce..f2e28d009 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -251,9 +251,11 @@ namespace openshot { public: - /// @brief Constructor for FFmpegWriter. Throws one of the following exceptions. + /// @brief Constructor for FFmpegWriter. + /// Throws an exception on failure to open path. + /// /// @param path The file path of the video file you want to open and read - FFmpegWriter(std::string path); + FFmpegWriter(const std::string& path); /// Close the writer void Close(); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index c8ce141fe..12b9776ac 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -83,7 +83,7 @@ int hw_de_on = 0; AVHWDeviceType hw_de_av_device_type_global = AV_HWDEVICE_TYPE_NONE; #endif -FFmpegReader::FFmpegReader(std::string path) +FFmpegReader::FFmpegReader(const std::string& path, bool inspect_reader) : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), is_open(false), seek_audio_frame_found(0), seek_video_frame_found(0), @@ -91,27 +91,6 @@ FFmpegReader::FFmpegReader(std::string path) current_video_frame(0), has_missing_frames(false), num_packets_since_video_frame(0), num_checks_since_final(0), packet(NULL) { - // Initialize FFMpeg, and register all formats and codecs - AV_REGISTER_ALL - AVCODEC_REGISTER_ALL - - // Init cache - working_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * info.fps.ToDouble() * 2, info.width, info.height, info.sample_rate, info.channels); - missing_frames.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); - final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); - - // Open and Close the reader, to populate its attributes (such as height, width, etc...) - Open(); - Close(); -} - -FFmpegReader::FFmpegReader(std::string path, bool inspect_reader) - : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), - audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false), - check_fps(false), enable_seek(true), is_open(false), seek_audio_frame_found(0), seek_video_frame_found(0), - prev_samples(0), prev_pts(0), pts_total(0), pts_counter(0), is_duration_known(false), largest_frame_processed(0), - current_video_frame(0), has_missing_frames(false), num_packets_since_video_frame(0), num_checks_since_final(0), - packet(NULL) { // Initialize FFMpeg, and register all formats and codecs AV_REGISTER_ALL diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 3366ac5f1..eff2507e9 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -83,7 +83,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 } #endif // HAVE_HW_ACCEL -FFmpegWriter::FFmpegWriter(std::string path) : +FFmpegWriter::FFmpegWriter(const std::string& path) : path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), samples(NULL), audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0), initial_audio_input_frame_size(0), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), From 96d84311c68a3fee3e982ca51fd2951bf5bc4c2a Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 8 Jun 2020 16:07:04 -0400 Subject: [PATCH 16/86] OpenMP: Move off deprecated allow_nested boolean - Replacement openmp_set_max_active_levels() takes an int argument - In OpenMP 5.0 it can be set to openmp_get_supported_active_levels() - Below 5.0, we just set it to our processor count - Move configuration of OpenMP from ad-hoc locations to constructors for FFmpegWriter/Reader and Timeline --- include/OpenMPUtilities.h | 7 +++++++ src/FFmpegReader.cpp | 10 +++++----- src/FFmpegWriter.cpp | 11 ++++++----- src/Timeline.cpp | 18 +++++++++++++----- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/include/OpenMPUtilities.h b/include/OpenMPUtilities.h index 9810d636e..5b5107c1b 100644 --- a/include/OpenMPUtilities.h +++ b/include/OpenMPUtilities.h @@ -41,5 +41,12 @@ #define OPEN_MP_NUM_PROCESSORS (std::min(omp_get_num_procs(), std::max(2, openshot::Settings::Instance()->OMP_THREADS) )) #define FF_NUM_PROCESSORS (std::min(omp_get_num_procs(), std::max(2, openshot::Settings::Instance()->FF_THREADS) )) +// Set max-active-levels to the max supported, if possible +// (supported_active_levels is OpenMP 5.0 (November 2018) or later, only.) +#if (_OPENMP >= 201811) + #define OPEN_MP_MAX_ACTIVE openmp_get_supported_active_levels() +#else + #define OPEN_MP_MAX_ACTIVE OPEN_MP_NUM_PROCESSORS +#endif #endif diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 12b9776ac..fea4f60b1 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -91,6 +91,11 @@ FFmpegReader::FFmpegReader(const std::string& path, bool inspect_reader) current_video_frame(0), has_missing_frames(false), num_packets_since_video_frame(0), num_checks_since_final(0), packet(NULL) { + // Configure OpenMP parallelism + // Default number of threads per section + omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); + // Allow nested parallel sections as deeply as supported + omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE); // Initialize FFMpeg, and register all formats and codecs AV_REGISTER_ALL @@ -877,11 +882,6 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) { int minimum_packets = OPEN_MP_NUM_PROCESSORS; int max_packets = 4096; - // Set the number of threads in OpenMP - omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); - // Allow nested OpenMP sections - omp_set_nested(true); - // Debug output ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream", "requested_frame", requested_frame, "OPEN_MP_NUM_PROCESSORS", OPEN_MP_NUM_PROCESSORS); diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index eff2507e9..39a3768f1 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -95,6 +95,12 @@ FFmpegWriter::FFmpegWriter(const std::string& path) : info.has_audio = false; info.has_video = false; + // Configure OpenMP parallelism + // Default number of threads per block + omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); + // Allow nested parallel sections as deeply as supported + omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE); + // Initialize FFMpeg, and register all formats and codecs AV_REGISTER_ALL @@ -714,11 +720,6 @@ void FFmpegWriter::write_queued_frames() { spooled_video_frames.clear(); spooled_audio_frames.clear(); - // Set the number of threads in OpenMP - omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); - // Allow nested OpenMP sections - omp_set_nested(true); - // Create blank exception bool has_error_encoding_video = false; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 124058ac2..89f0992ca 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -67,7 +67,13 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha info.acodec = "openshot::timeline"; info.vcodec = "openshot::timeline"; - // Init max image size + // Configure OpenMP parallelism + // Default number of threads per block + omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); + // Allow nested parallel sections as deeply as supported + omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE); + + // Init max image size SetMaxSize(info.width, info.height); // Init cache @@ -194,6 +200,12 @@ Timeline::Timeline(std::string projectPath, bool convert_absolute_paths) : info.has_video = true; info.has_audio = true; + // Configure OpenMP parallelism + // Default number of threads per section + omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); + // Allow nested parallel sections as deeply as supported + omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE); + // Init max image size SetMaxSize(info.width, info.height); @@ -892,10 +904,6 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) #pragma omp critical (T_GetFrame) nearby_clips = find_intersecting_clips(requested_frame, minimum_frames, true); - omp_set_num_threads(OPEN_MP_NUM_PROCESSORS); - // Allow nested OpenMP sections - omp_set_nested(true); - // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame", "requested_frame", requested_frame, "minimum_frames", minimum_frames, "OPEN_MP_NUM_PROCESSORS", OPEN_MP_NUM_PROCESSORS); From 3f13ed87f0e18e636376be67847f9993670c0302 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 8 Jun 2020 16:07:55 -0400 Subject: [PATCH 17/86] Timeline: const-ref path arg in constructor, docs --- include/Timeline.h | 22 +++++++++++++--------- src/Timeline.cpp | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/Timeline.h b/include/Timeline.h index 932b04acf..32eac6be1 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -202,19 +202,23 @@ namespace openshot { public: - /// @brief Default Constructor for the timeline (which sets the canvas width and height and FPS) - /// @param width The width of the timeline (and thus, the generated openshot::Frame objects) - /// @param height The height of the timeline (and thus, the generated openshot::Frame objects) - /// @param fps The frames rate of the timeline - /// @param sample_rate The sample rate of the timeline's audio - /// @param channels The number of audio channels of the timeline + /// @brief Default Constructor for the timeline (which configures the default frame properties) + /// @param width The image width of generated openshot::Frame objects + /// @param height The image height of generated openshot::Frame objects + /// @param fps The frame rate of the generated video + /// @param sample_rate The audio sample rate + /// @param channels The number of audio channels /// @param channel_layout The channel layout (i.e. mono, stereo, 3 point surround, etc...) Timeline(int width, int height, Fraction fps, int sample_rate, int channels, ChannelLayout channel_layout); - /// @brief Constructor for the timeline (which loads a JSON structure from a file path, and initializes a timeline) + /// @brief Project-file constructor for the timeline + /// + /// Loads a JSON structure from a file path, and + /// initializes the timeline described within. + /// /// @param projectPath The path of the UTF-8 *.osp project file (JSON contents). Contents will be loaded automatically. - /// @param convert_absolute_paths Should all paths be converted to absolute paths (based on the folder of the path provided) - Timeline(std::string projectPath, bool convert_absolute_paths); + /// @param convert_absolute_paths Should all paths be converted to absolute paths (relative to the location of projectPath) + Timeline(const std::string& projectPath, bool convert_absolute_paths); virtual ~Timeline(); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 89f0992ca..bbbc6858c 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -82,7 +82,7 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha } // Constructor for the timeline (which loads a JSON structure from a file path, and initializes a timeline) -Timeline::Timeline(std::string projectPath, bool convert_absolute_paths) : +Timeline::Timeline(const std::string& projectPath, bool convert_absolute_paths) : is_open(false), auto_map_clips(true), managed_cache(true), path(projectPath) { // Create CrashHandler and Attach (incase of errors) From 56736e9505530a4ed5f88da92f803703ddd7aed8 Mon Sep 17 00:00:00 2001 From: eisneinechse <42617957+eisneinechse@users.noreply.github.com> Date: Tue, 16 Jun 2020 10:22:52 -0700 Subject: [PATCH 18/86] Set the field order when interlace is on --- src/FFmpegWriter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index d960d6771..643eb8542 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -340,6 +340,12 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va st = video_st; // Get codec context c = AV_GET_CODEC_PAR_CONTEXT(st, video_codec); + // Was a codec / stream found? + if (c) { + if (info.interlaced_frame) { + c->field_order = info.top_field_first ? AV_FIELD_TT : AV_FIELD_BB; + } + } } else if (info.has_audio && stream == AUDIO_STREAM && audio_st) { st = audio_st; // Get codec context From f69aa41127fa03b3804007825b86328eb85b1134 Mon Sep 17 00:00:00 2001 From: eisneinechse <42617957+eisneinechse@users.noreply.github.com> Date: Wed, 8 Jul 2020 17:11:47 -0700 Subject: [PATCH 19/86] Add some comment --- src/FFmpegWriter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 643eb8542..6e300bf10 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -344,6 +344,8 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va if (c) { if (info.interlaced_frame) { c->field_order = info.top_field_first ? AV_FIELD_TT : AV_FIELD_BB; + // We only use these two version and ignore AV_FIELD_TB and AV_FIELD_BT + // Otherwise we would need to change the whole export window } } } else if (info.has_audio && stream == AUDIO_STREAM && audio_st) { From 8e6c73449e3450cb6c70d5dd22c9f16c6109bc16 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 7 Aug 2020 07:46:26 -0400 Subject: [PATCH 20/86] "Ensure full 40-character hashes in changelog" --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87de2e165..25fcb43a7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,7 +25,7 @@ linux-builder: - make doc - ~/auto-update-docs "$CI_PROJECT_DIR/build" "$CI_COMMIT_REF_NAME" - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -50,7 +50,7 @@ mac-builder: - make - make install - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 '@^')..@ --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -77,7 +77,7 @@ windows-builder-x64: - mingw32-make install - New-Item -path "install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -104,7 +104,7 @@ windows-builder-x86: - mingw32-make install - New-Item -path "install-x86/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x86/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x86/share/$CI_PROJECT_NAME.log" when: always except: - tags From 3c2532b4deb51fee22338f70c094a5f058bf9c7f Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Thu, 20 Aug 2020 16:50:12 -0400 Subject: [PATCH 21/86] Use std::make_shared to allocate shared ptrs std::make_shared does in a single allocation what the constructors for std::shared_ptr usually take at least 2 allocations to do. May give us an infinitesimal performance/memory improvement. https://www.modernescpp.com/index.php/memory-and-performance-overhead-of-smart-pointer --- include/DummyReader.h | 2 +- include/Frame.h | 2 +- src/CacheDisk.cpp | 6 +-- src/ChunkWriter.cpp | 4 +- src/Clip.cpp | 7 +++- src/DecklinkInput.cpp | 8 ++-- src/DecklinkWriter.cpp | 2 +- src/FFmpegReader.cpp | 6 +-- src/Frame.cpp | 84 +++++++++++++++++++++++-------------- src/FrameMapper.cpp | 8 ++-- src/ImageReader.cpp | 6 ++- src/QtHtmlReader.cpp | 9 ++-- src/QtImageReader.cpp | 23 ++++++---- src/QtTextReader.cpp | 8 ++-- src/TextReader.cpp | 11 +++-- src/effects/Bars.cpp | 3 +- src/effects/Crop.cpp | 3 +- src/effects/Deinterlace.cpp | 4 +- src/effects/Mask.cpp | 11 ++--- 19 files changed, 129 insertions(+), 78 deletions(-) diff --git a/include/DummyReader.h b/include/DummyReader.h index 9a75751d0..af06656d8 100644 --- a/include/DummyReader.h +++ b/include/DummyReader.h @@ -69,7 +69,7 @@ namespace openshot * // 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]; diff --git a/include/Frame.h b/include/Frame.h index f4ff54d44..7e7866c07 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -99,7 +99,7 @@ namespace openshot * ); * * // Some methods require a shared pointer to an openshot::Frame object. - * std::shared_ptr f(new Frame(1, 720, 480, "#000000", 44100, 2)); + * auto f = std::make_shared(1, 720, 480, "#000000", 44100, 2); * * @endcode */ diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index bb2e12c42..409d8c4e1 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -227,14 +227,14 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number) if (path.exists(frame_path)) { // Load image file - std::shared_ptr image = std::shared_ptr(new QImage()); + auto image = std::make_shared(); image->load(frame_path); // Set pixel formatimage-> - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); + image = std::make_shared(image->convertToFormat(QImage::Format_RGBA8888)); // Create frame object - std::shared_ptr frame(new Frame()); + auto frame = std::make_shared(); frame->number = frame_number; frame->AddImage(image); diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp index f9d653b0c..c7752cd57 100644 --- a/src/ChunkWriter.cpp +++ b/src/ChunkWriter.cpp @@ -134,7 +134,9 @@ void ChunkWriter::WriteFrame(std::shared_ptr frame) writer_thumb->WriteFrame(last_frame); } else { // Write the 1st frame (of the 1st chunk)... since no previous chunk is available - std::shared_ptr blank_frame(new Frame(1, info.width, info.height, "#000000", info.sample_rate, info.channels)); + auto blank_frame = std::make_shared( + 1, info.width, info.height, "#000000", + info.sample_rate, info.channels); blank_frame->AddColor(info.width, info.height, "#000000"); writer_final->WriteFrame(blank_frame); writer_preview->WriteFrame(blank_frame); diff --git a/src/Clip.cpp b/src/Clip.cpp index d9f694408..f20f32fbf 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -339,7 +339,10 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) original_frame = GetOrCreateFrame(new_frame_number); // Create a new frame - std::shared_ptr frame(new Frame(new_frame_number, 1, 1, "#000000", original_frame->GetAudioSamplesCount(), original_frame->GetAudioChannelsCount())); + auto frame = std::make_shared( + new_frame_number, 1, 1, "#000000", + original_frame->GetAudioSamplesCount(), + original_frame->GetAudioChannelsCount()); { frame->SampleRate(original_frame->SampleRate()); frame->ChannelsLayout(original_frame->ChannelsLayout()); @@ -347,7 +350,7 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Copy the image from the odd field if (enabled_video) - frame->AddImage(std::shared_ptr(new QImage(*original_frame->GetImage()))); + frame->AddImage(std::make_shared(*original_frame->GetImage())); // Loop through each channel, add audio if (enabled_audio && reader->info.has_audio) diff --git a/src/DecklinkInput.cpp b/src/DecklinkInput.cpp index da5a8d00e..b03ad8e4a 100644 --- a/src/DecklinkInput.cpp +++ b/src/DecklinkInput.cpp @@ -139,7 +139,7 @@ HRESULT DeckLinkInputDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* { // Handle Video Frame if(videoFrame) - { + { if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) { @@ -245,7 +245,8 @@ omp_set_nested(true); m_rgbFrame->GetBytes(&frameBytes); // *********** CREATE OPENSHOT FRAME ********** - std::shared_ptr f(new openshot::Frame(copy_frameCount, width, height, "#000000", 2048, 2)); + auto f = std::make_shared( + copy_frameCount, width, height, "#000000", 2048, 2); // Add Image data to openshot frame // TODO: Fix Decklink support with QImage Upgrade @@ -289,6 +290,3 @@ HRESULT DeckLinkInputDelegate::VideoInputFormatChanged(BMDVideoInputFormatChange { return S_OK; } - - - diff --git a/src/DecklinkWriter.cpp b/src/DecklinkWriter.cpp index 4ebbd1f02..3eafda1cf 100644 --- a/src/DecklinkWriter.cpp +++ b/src/DecklinkWriter.cpp @@ -142,7 +142,7 @@ void DecklinkWriter::Open() // throw DecklinkError("Failed to enable audio output. Is another application using the card?"); // Begin video preroll by scheduling a second of frames in hardware - //std::shared_ptr f(new Frame(1, displayMode->GetWidth(), displayMode->GetHeight(), "Blue")); + //auto f = std::make_shared(1, displayMode->GetWidth(), displayMode->GetHeight(), "Blue"); //f->AddColor(displayMode->GetWidth(), displayMode->GetHeight(), "Blue"); // Preroll 1 second of video diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index c8ce141fe..1952e8d61 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -2134,7 +2134,7 @@ bool FFmpegReader::CheckMissingFrame(int64_t requested_frame) { // Add this frame to the processed map (since it's already done) std::shared_ptr parent_image = parent_frame->GetImage(); if (parent_image) { - missing_frame->AddImage(std::shared_ptr(new QImage(*parent_image))); + missing_frame->AddImage(std::make_shared(*parent_image)); processed_video_frames[missing_frame->number] = missing_frame->number; } } @@ -2224,7 +2224,7 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram if (info.has_video && !is_video_ready && last_video_frame) { // Copy image from last frame - f->AddImage(std::shared_ptr(new QImage(*last_video_frame->GetImage()))); + f->AddImage(std::make_shared(*last_video_frame->GetImage())); is_video_ready = true; } @@ -2246,7 +2246,7 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram // Add missing image (if needed - sometimes end_of_stream causes frames with only audio) if (info.has_video && !is_video_ready && last_video_frame) // Copy image from last frame - f->AddImage(std::shared_ptr(new QImage(*last_video_frame->GetImage()))); + f->AddImage(std::make_shared(*last_video_frame->GetImage())); // Reset counter since last 'final' frame num_checks_since_final = 0; diff --git a/src/Frame.cpp b/src/Frame.cpp index cf47556af..16f420bc8 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -39,7 +39,7 @@ Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), max_audio_sample(0) { // Init the image magic and audio buffer - audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); + audio = std::make_shared(channels, 0); // initialize the audio samples to zero (silence) audio->clear(); @@ -52,7 +52,7 @@ Frame::Frame(int64_t number, int width, int height, std::string color) max_audio_sample(0) { // Init the image magic and audio buffer - audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); + audio = std::make_shared(channels, 0); // initialize the audio samples to zero (silence) audio->clear(); @@ -65,7 +65,7 @@ Frame::Frame(int64_t number, int samples, int channels) : max_audio_sample(0) { // Init the image magic and audio buffer - audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, samples)); + audio = std::make_shared(channels, samples); // initialize the audio samples to zero (silence) audio->clear(); @@ -78,7 +78,7 @@ Frame::Frame(int64_t number, int width, int height, std::string color, int sampl max_audio_sample(0) { // Init the image magic and audio buffer - audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, samples)); + audio = std::make_shared(channels, samples); // initialize the audio samples to zero (silence) audio->clear(); @@ -117,11 +117,11 @@ void Frame::DeepCopy(const Frame& other) max_audio_sample = other.max_audio_sample; if (other.image) - image = std::shared_ptr(new QImage(*(other.image))); + image = std::make_shared(*(other.image)); if (other.audio) - audio = std::shared_ptr(new juce::AudioSampleBuffer(*(other.audio))); + audio = std::make_shared(*(other.audio)); if (other.wave_image) - wave_image = std::shared_ptr(new QImage(*(other.wave_image))); + wave_image = std::make_shared(*(other.wave_image)); } // Destructor @@ -138,7 +138,7 @@ void Frame::Display() // Only create the QApplication once static int argc = 1; static char* argv[1] = {NULL}; - previewApp = std::shared_ptr(new QApplication(argc, argv)); + previewApp = std::make_shared(argc, argv); } // Get preview image @@ -152,7 +152,8 @@ void Frame::Display() int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); // Resize to fix DAR - previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } // Create window @@ -228,7 +229,8 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G } // Create blank image - wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_RGBA8888)); + wave_image = std::make_shared( + total_width, total_height, QImage::Format_RGBA8888); wave_image->fill(QColor(0,0,0,0)); // Load QPainter with wave_image device @@ -253,13 +255,13 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G // Resize Image (if requested) if (width != total_width || height != total_height) { QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation); - wave_image = std::shared_ptr(new QImage(scaled_wave_image)); + wave_image = std::make_shared(scaled_wave_image); } } else { // No audio samples present - wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + wave_image = std::make_shared(width, height, QImage::Format_RGBA8888); wave_image->fill(QColor(QString::fromStdString("#000000"))); } @@ -294,7 +296,7 @@ void Frame::DisplayWaveform() // Only create the QApplication once static int argc = 1; static char* argv[1] = {NULL}; - previewApp = std::shared_ptr(new QApplication(argc, argv)); + previewApp = std::make_shared(argc, argv); } // Create window @@ -599,11 +601,15 @@ void Frame::Save(std::string path, float scale, std::string format, int quality) int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); // Resize to fix DAR - previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + new_width, new_height, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } // Resize image - previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + new_width * scale, new_height * scale, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); } // Save image @@ -615,7 +621,8 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate) { // Create blank thumbnail image & fill background color - std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888)); + auto thumbnail = std::make_shared( + new_width, new_height, QImage::Format_RGBA8888); thumbnail->fill(QColor(QString::fromStdString(background_color))); // Create painter @@ -633,16 +640,22 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); // Resize to fix DAR - previewImage = std::shared_ptr(new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + aspect_width, aspect_height, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } // Resize frame image if (ignore_aspect) // Ignore aspect ratio - previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + new_width, new_height, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); else // Maintain aspect ratio - previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + previewImage = std::make_shared(previewImage->scaled( + new_width, new_height, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); // Composite frame image onto background (centered) int x = (new_width - previewImage->size().width()) / 2.0; // center @@ -666,14 +679,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Overlay Image (if any) if (overlay_path != "") { // Open overlay - std::shared_ptr overlay = std::shared_ptr(new QImage()); + auto overlay = std::make_shared(); overlay->load(QString::fromStdString(overlay_path)); // Set pixel format - overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888))); + overlay = std::make_shared( + overlay->convertToFormat(QImage::Format_RGBA8888)); // Resize to fit - overlay = std::shared_ptr(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + overlay = std::make_shared(overlay->scaled( + new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Composite onto thumbnail painter.setCompositionMode(QPainter::CompositionMode_SourceOver); @@ -684,14 +699,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Mask Image (if any) if (mask_path != "") { // Open mask - std::shared_ptr mask = std::shared_ptr(new QImage()); + auto mask = std::make_shared(); mask->load(QString::fromStdString(mask_path)); // Set pixel format - mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_RGBA8888))); + mask = std::make_shared( + mask->convertToFormat(QImage::Format_RGBA8888)); // Resize to fit - mask = std::shared_ptr(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + mask = std::make_shared(mask->scaled( + new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Negate mask mask->invertPixels(); @@ -744,7 +761,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888)); + image = std::make_shared(new_width, new_height, QImage::Format_RGBA8888); // Fill with solid color image->fill(QColor(QString::fromStdString(color))); @@ -769,11 +786,12 @@ void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage: // Create new image object, and fill with pixel data #pragma omp critical (AddImage) { - image = std::shared_ptr(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer)); + image = std::make_shared( + qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer); // Always convert to RGBA8888 (if different) if (image->format() != QImage::Format_RGBA8888) - *image = image->convertToFormat(QImage::Format_RGBA8888); + *image = image->convertToFormat(QImage::Format_RGBA8888); // Update height and width width = image->width(); @@ -828,7 +846,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) ret = true; } else if (new_image->format() != image->format()) { - new_image = std::shared_ptr(new QImage(new_image->convertToFormat(image->format()))); + new_image = std::make_shared( + new_image->convertToFormat(image->format())); } } if (ret) { @@ -938,7 +957,8 @@ std::shared_ptr Frame::GetMagickImage() const QRgb *tmpBits = (const QRgb*)image->constBits(); // Create new image object, and fill with pixel data - std::shared_ptr magick_image = std::shared_ptr(new Magick::Image(image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits)); + auto magick_image = std::make_shared( + image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits); // Give image a transparent background color magick_image->backgroundColor(Magick::Color("none")); @@ -967,7 +987,9 @@ void Frame::AddMagickImage(std::shared_ptr new_image) MagickCore::ExportImagePixels(new_image->constImage(), 0, 0, new_image->columns(), new_image->rows(), "RGBA", Magick::CharPixel, buffer, &exception); // Create QImage of frame data - image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer)); + image = std::make_shared( + qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, + (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer); // Update height and width width = image->width(); diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index e1e5700c2..4946a5c1c 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -450,7 +450,8 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) } // Create a new frame - std::shared_ptr frame = std::make_shared(frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame); + auto frame = std::make_shared( + frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame); frame->SampleRate(mapped_frame->SampleRate()); frame->ChannelsLayout(mapped_frame->ChannelsLayout()); @@ -460,13 +461,14 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) odd_frame = GetOrCreateFrame(mapped.Odd.Frame); if (odd_frame) - frame->AddImage(std::shared_ptr(new QImage(*odd_frame->GetImage())), true); + frame->AddImage(std::make_shared(*odd_frame->GetImage()), true); if (mapped.Odd.Frame != mapped.Even.Frame) { // Add even lines (if different than the previous image) std::shared_ptr even_frame; even_frame = GetOrCreateFrame(mapped.Even.Frame); if (even_frame) - frame->AddImage(std::shared_ptr(new QImage(*even_frame->GetImage())), false); + frame->AddImage( + std::make_shared(*even_frame->GetImage()), false); } // Resample audio on frame (if needed) diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index 9ce3a70ff..9a001879f 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -61,7 +61,7 @@ void ImageReader::Open() try { // load image - image = std::shared_ptr(new Magick::Image(path)); + image = std::make_shared(path); // Give image a transparent background color image->backgroundColor(Magick::Color("none")); @@ -126,7 +126,9 @@ std::shared_ptr ImageReader::GetFrame(int64_t requested_frame) throw ReaderClosed("The FFmpegReader is closed. Call Open() before calling this method.", path); // Create or get frame object - std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2)); + auto image_frame = std::make_shared( + requested_frame, image->size().width(), image->size().height(), + "#000000", 0, 2); // Add Image data to frame image_frame->AddMagickImage(image); diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 6b502fbd4..4925d3a88 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -62,7 +62,7 @@ void QtHtmlReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image = std::make_shared(width, height, QImage::Format_RGBA8888); image->fill(QColor(background_color.c_str())); //start painting @@ -162,7 +162,9 @@ std::shared_ptr QtHtmlReader::GetFrame(int64_t requested_frame) if (image) { // Create or get frame object - std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), background_color, 0, 2)); + auto image_frame = std::make_shared( + requested_frame, image->size().width(), image->size().height(), + background_color, 0, 2); // Add Image data to frame image_frame->AddImage(image); @@ -171,7 +173,8 @@ std::shared_ptr QtHtmlReader::GetFrame(int64_t requested_frame) return image_frame; } else { // return empty frame - std::shared_ptr image_frame(new Frame(1, 640, 480, background_color, 0, 2)); + auto image_frame = std::make_shared( + 1, 640, 480, background_color, 0, 2); // return frame object return image_frame; diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index cf64ef930..b6dd7ee05 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -82,7 +82,8 @@ void QtImageReader::Open() ResvgRenderer renderer(path); if (renderer.isValid()) { - image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied)); + image = std::make_shared( + renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied); image->fill(Qt::transparent); QPainter p(image.get()); @@ -95,7 +96,7 @@ void QtImageReader::Open() if (!loaded) { // Attempt to open file using Qt's build in image processing capabilities - image = std::shared_ptr(new QImage()); + image = std::make_shared(); success = image->load(path); } @@ -105,7 +106,8 @@ void QtImageReader::Open() } // Convert to proper format - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); + image = std::make_shared( + image->convertToFormat(QImage::Format_RGBA8888)); // Update image properties info.has_audio = false; @@ -238,7 +240,9 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) svg_size.scale(max_width, max_height, Qt::KeepAspectRatio); // Create empty QImage - cached_image = std::shared_ptr(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_ARGB32_Premultiplied)); + cached_image = std::make_shared( + QSize(svg_size.width(), svg_size.height()), + QImage::Format_ARGB32_Premultiplied); cached_image->fill(Qt::transparent); // Render SVG into QImage @@ -253,10 +257,12 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) if (!rendered) { // We need to resize the original image to a smaller image (for performance reasons) // Only do this once, to prevent tons of unneeded scaling operations - cached_image = std::shared_ptr(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + cached_image = std::make_shared(image->scaled( + max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } - cached_image = std::shared_ptr(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888))); + cached_image = std::make_shared( + cached_image->convertToFormat(QImage::Format_RGBA8888)); // Set max size (to later determine if max_size is changed) max_size.setWidth(max_width); @@ -264,7 +270,10 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) } // Create or get frame object - std::shared_ptr image_frame(new Frame(requested_frame, cached_image->width(), cached_image->height(), "#000000", Frame::GetSamplesPerFrame(requested_frame, info.fps, info.sample_rate, info.channels), info.channels)); + auto image_frame = std::make_shared( + requested_frame, cached_image->width(), cached_image->height(), "#000000", + Frame::GetSamplesPerFrame(requested_frame, info.fps, info.sample_rate, info.channels), + info.channels); // Add Image data to frame image_frame->AddImage(cached_image); diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index d91d164e6..bd157ebd2 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -67,7 +67,7 @@ void QtTextReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image = std::make_shared(width, height, QImage::Format_RGBA8888); image->fill(QColor(background_color.c_str())); QPainter painter; @@ -179,7 +179,9 @@ std::shared_ptr QtTextReader::GetFrame(int64_t requested_frame) if (image) { // Create or get frame object - std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), background_color, 0, 2)); + auto image_frame = std::make_shared( + requested_frame, image->size().width(), image->size().height(), + background_color, 0, 2); // Add Image data to frame image_frame->AddImage(image); @@ -188,7 +190,7 @@ std::shared_ptr QtTextReader::GetFrame(int64_t requested_frame) return image_frame; } else { // return empty frame - std::shared_ptr image_frame(new Frame(1, 640, 480, background_color, 0, 2)); + auto image_frame = std::make_shared(1, 640, 480, background_color, 0, 2); // return frame object return image_frame; diff --git a/src/TextReader.cpp b/src/TextReader.cpp index e317700c9..be8c7375f 100644 --- a/src/TextReader.cpp +++ b/src/TextReader.cpp @@ -66,7 +66,8 @@ void TextReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new Magick::Image(Magick::Geometry(width,height), Magick::Color(background_color))); + image = std::make_shared( + Magick::Geometry(width,height), Magick::Color(background_color)); // Give image a transparent background color image->backgroundColor(Magick::Color("none")); @@ -166,10 +167,12 @@ std::shared_ptr TextReader::GetFrame(int64_t requested_frame) if (image) { // Create or get frame object - std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2)); + auto image_frame = std::make_shared( + requested_frame, image->size().width(), image->size().height(), + "#000000", 0, 2); // Add Image data to frame - std::shared_ptr copy_image(new Magick::Image(*image.get())); + auto copy_image = std::make_shared(*image.get()); copy_image->modifyImage(); // actually copy the image data to this object //TODO: Reimplement this with QImage image_frame->AddMagickImage(copy_image); @@ -178,7 +181,7 @@ std::shared_ptr TextReader::GetFrame(int64_t requested_frame) return image_frame; } else { // return empty frame - std::shared_ptr image_frame(new Frame(1, 640, 480, "#000000", 0, 2)); + auto image_frame = std::make_shared(1, 640, 480, "#000000", 0, 2); // return frame object return image_frame; diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index 3f9aac344..e653b7dda 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -68,7 +68,8 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get bar color (and create small color image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888)); + auto tempColor = std::make_shared( + frame_image->width(), 1, QImage::Format_RGBA8888); tempColor->fill(QColor(QString::fromStdString(color.GetColorHex(frame_number)))); // Get current keyframe values diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index b1c3d38d9..f0e0aa953 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -68,7 +68,8 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get transparent color (and create small transparent image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888)); + auto tempColor = std::make_shared( + frame_image->width(), 1, QImage::Format_RGBA8888); tempColor->fill(QColor(QString::fromStdString("transparent"))); // Get current keyframe values diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 39b3316a5..984336aa0 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -86,7 +86,9 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 } // Resize deinterlaced image back to original size, and update frame's image - image = std::shared_ptr(new QImage(deinterlaced_image.scaled(original_width, original_height, Qt::IgnoreAspectRatio, Qt::FastTransformation))); + image = std::make_shared(deinterlaced_image.scaled( + original_width, original_height, + Qt::IgnoreAspectRatio, Qt::FastTransformation)); // Update image on frame frame->AddImage(image); diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index 11c37f053..f270e4109 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -84,13 +84,14 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram (original_mask && original_mask->size() != frame_image->size())) { // Only get mask if needed - std::shared_ptr mask_without_sizing = std::shared_ptr( - new QImage(*reader->GetFrame(frame_number)->GetImage())); + auto mask_without_sizing = std::make_shared( + *reader->GetFrame(frame_number)->GetImage()); // Resize mask image to match frame size - original_mask = std::shared_ptr(new QImage( - mask_without_sizing->scaled(frame_image->width(), frame_image->height(), Qt::IgnoreAspectRatio, - Qt::SmoothTransformation))); + original_mask = std::make_shared( + mask_without_sizing->scaled( + frame_image->width(), frame_image->height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } } From c14922d57ea8226b2876a822dab9911ae2f1fbf9 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Thu, 20 Aug 2020 20:40:55 -0400 Subject: [PATCH 22/86] Frame.cpp/h: Fix a bunch of wrong comments Best reason not to narrate the code in the comments: The code gets changed, but the documentation doesn't. --- include/Frame.h | 12 ++++++------ src/Frame.cpp | 22 +++++++--------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/include/Frame.h b/include/Frame.h index 7e7866c07..8987dcb40 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -73,17 +73,17 @@ namespace openshot * There are many ways to create an instance of an openshot::Frame: * @code * - * // Most basic: a blank frame (300x200 blank image, 48kHz audio silence) + * // Most basic: a blank frame (all default values) * Frame(); * - * // Image only settings (48kHz audio silence) + * // Image only settings * Frame(1, // Frame number * 720, // Width of image * 480, // Height of image * "#000000" // HTML color code of background color * ); * - * // Audio only (300x200 blank image) + * // Audio only * Frame(number, // Frame number * 44100, // Sample rate of audio stream * 2 // Number of audio channels @@ -131,13 +131,13 @@ namespace openshot bool has_image_data; ///< This frame has been loaded with pixel data - /// Constructor - blank frame (300x200 blank image, 48kHz audio silence) + /// Constructor - blank frame Frame(); - /// Constructor - image only (48kHz audio silence) + /// Constructor - image only Frame(int64_t number, int width, int height, std::string color); - /// Constructor - audio only (300x200 blank image) + /// Constructor - audio only Frame(int64_t number, int samples, int channels); /// Constructor - image & audio diff --git a/src/Frame.cpp b/src/Frame.cpp index 16f420bc8..54901cc56 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -33,41 +33,35 @@ using namespace std; using namespace openshot; -// Constructor - blank frame (300x200 blank image, 48kHz audio silence) +// Constructor - blank frame Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), color("#000000"), channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), max_audio_sample(0) { - // Init the image magic and audio buffer + // Allocate and zero (fill with silence) the audio buffer audio = std::make_shared(channels, 0); - - // initialize the audio samples to zero (silence) audio->clear(); } -// Constructor - image only (48kHz audio silence) +// Constructor - image only Frame::Frame(int64_t number, int width, int height, std::string color) : number(number), pixel_ratio(1,1), channels(2), width(width), height(height), color(color), channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), max_audio_sample(0) { - // Init the image magic and audio buffer + // Allocate and zero (fill with silence) the audio buffer audio = std::make_shared(channels, 0); - - // initialize the audio samples to zero (silence) audio->clear(); } -// Constructor - audio only (300x200 blank image) +// Constructor - audio only Frame::Frame(int64_t number, int samples, int channels) : number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), color("#000000"), channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), max_audio_sample(0) { - // Init the image magic and audio buffer + // Allocate and zero (fill with silence) the audio buffer audio = std::make_shared(channels, samples); - - // initialize the audio samples to zero (silence) audio->clear(); } @@ -77,10 +71,8 @@ Frame::Frame(int64_t number, int width, int height, std::string color, int sampl channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), max_audio_sample(0) { - // Init the image magic and audio buffer + // Allocate and zero (fill with silence) the audio buffer audio = std::make_shared(channels, samples); - - // initialize the audio samples to zero (silence) audio->clear(); } From 5700b0ab7dcbf9dcb89d47174dd1fa427448df54 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 13:12:42 -0500 Subject: [PATCH 23/86] - Refactoring all Timeline drawing code into the Clip class - Making Clip a proper Reader (so it can be used directly, instead of a Timeline) --- include/Clip.h | 21 +++- src/Clip.cpp | 299 ++++++++++++++++++++++++++++++++++++++++++++++- src/Timeline.cpp | 261 ++--------------------------------------- 3 files changed, 323 insertions(+), 258 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index 0fbed1599..78ffa4abf 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -92,7 +92,7 @@ namespace openshot { * c2.alpha.AddPoint(384, 1.0); // Animate the alpha to visible (between frame #360 and frame #384) * @endcode */ - class Clip : public openshot::ClipBase { + class Clip : public openshot::ClipBase, public openshot::ReaderBase { protected: /// Section lock for multiple threads juce::CriticalSection getFrameCriticalSection; @@ -100,6 +100,7 @@ namespace openshot { private: bool waveform; ///< Should a waveform be used instead of the clip's image std::list effects; /// Is Reader opened // Audio resampler (if time mapping) openshot::AudioResampler *resampler; @@ -117,6 +118,9 @@ namespace openshot { /// Apply effects to the source frame (if any) std::shared_ptr apply_effects(std::shared_ptr frame); + /// Apply keyframes to the source frame (if any) + std::shared_ptr apply_keyframes(std::shared_ptr frame); + /// Get file extension std::string get_file_extension(std::string path); @@ -132,6 +136,9 @@ namespace openshot { /// Update default rotation from reader void init_reader_rotation(); + /// Compare 2 floating point numbers + bool isEqual(double a, double b); + /// Sort effects by order void sort_effects(); @@ -159,6 +166,18 @@ namespace openshot { /// Destructor virtual ~Clip(); + + /// Get the cache object used by this reader (always returns NULL for this object) + CacheMemory* GetCache() override { return NULL; }; + + /// Determine if reader is open or closed + bool IsOpen() override { return is_open; }; + + /// Return the type name of the class + std::string Name() override { return "Clip"; }; + + + /// @brief Add an effect to the clip /// @param effect Add an effect to the clip. An effect can modify the audio or video of an openshot::Frame. void AddEffect(openshot::EffectBase* effect); diff --git a/src/Clip.cpp b/src/Clip.cpp index d9f694408..c3460ff81 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -134,14 +134,14 @@ void Clip::init_reader_rotation() { } // Default Constructor for a clip -Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL) +Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); } // Constructor with reader -Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL) +Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); @@ -158,7 +158,7 @@ Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), alloca } // Constructor with filepath -Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL) +Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); @@ -262,6 +262,10 @@ void Clip::Open() { // Open the reader reader->Open(); + is_open = true; + + // Copy Reader info to Clip + info = reader->info; // Set some clip properties from the file reader if (end == 0.0) @@ -275,6 +279,7 @@ void Clip::Open() // Close the internal reader void Clip::Close() { + is_open = false; if (reader) { ZmqLogger::Instance()->AppendDebugMethod("Clip::Close"); @@ -311,6 +316,10 @@ float Clip::End() const // Get an openshot::Frame object for a specific frame number of this reader. std::shared_ptr Clip::GetFrame(int64_t requested_frame) { + // Check for open reader (or throw exception) + if (!is_open) + throw ReaderClosed("The Clip is closed. Call Open() before calling this method", "N/A"); + if (reader) { // Adjust out of bounds frame number @@ -360,6 +369,9 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Apply effects to the frame (if any) apply_effects(frame); + // Apply keyframe / transforms + apply_keyframes(frame); + // Return processed 'frame' return frame; } @@ -1042,3 +1054,284 @@ std::shared_ptr Clip::apply_effects(std::shared_ptr frame) // Return modified frame return frame; } + +// Compare 2 floating point numbers for equality +bool Clip::isEqual(double a, double b) +{ + return fabs(a - b) < 0.000001; +} + + +// Apply keyframes to the source frame (if any) +std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) +{ + // Get actual frame image data + std::shared_ptr source_image = frame->GetImage(); + + /* REPLACE IMAGE WITH WAVEFORM IMAGE (IF NEEDED) */ + if (Waveform()) + { + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Generate Waveform Image)", "frame->number", frame->number, "Waveform()", Waveform()); + + // Get the color of the waveform + int red = wave_color.red.GetInt(frame->number); + int green = wave_color.green.GetInt(frame->number); + int blue = wave_color.blue.GetInt(frame->number); + int alpha = wave_color.alpha.GetInt(frame->number); + + // Generate Waveform Dynamically (the size of the timeline) + source_image = frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); + frame->AddImage(std::shared_ptr(source_image)); + } + + /* ALPHA & OPACITY */ + if (alpha.GetValue(frame->number) != 1.0) + { + float alpha_value = alpha.GetValue(frame->number); + + // Get source image's pixels + unsigned char *pixels = (unsigned char *) source_image->bits(); + + // Loop through pixels + for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) + { + // Apply alpha to pixel + pixels[byte_index + 3] *= alpha_value; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); + } + + /* RESIZE SOURCE IMAGE - based on scale type */ + QSize source_size = source_image->size(); + switch (scale) + { + case (SCALE_FIT): { + // keep aspect ratio + source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_STRETCH): { + // ignore aspect ratio + source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_CROP): { + QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); + QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); + + // respect aspect ratio + if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) + source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); + else + source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_NONE): { + // Calculate ratio of source size to project size + // Even with no scaling, previews need to be adjusted correctly + // (otherwise NONE scaling draws the frame image outside of the preview) + float source_width_ratio = source_size.width() / float(Settings::Instance()->MAX_WIDTH); + float source_height_ratio = source_size.height() / float(Settings::Instance()->MAX_HEIGHT); + source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + } + + float crop_x_value = crop_x.GetValue(frame->number); + float crop_y_value = crop_y.GetValue(frame->number); + float crop_w_value = crop_width.GetValue(frame->number); + float crop_h_value = crop_height.GetValue(frame->number); + switch(crop_gravity) + { + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; + case (GRAVITY_TOP): + crop_x_value += 0.5; + break; + case (GRAVITY_TOP_RIGHT): + crop_x_value += 1.0; + break; + case (GRAVITY_LEFT): + crop_y_value += 0.5; + break; + case (GRAVITY_CENTER): + crop_x_value += 0.5; + crop_y_value += 0.5; + break; + case (GRAVITY_RIGHT): + crop_x_value += 1.0; + crop_y_value += 0.5; + break; + case (GRAVITY_BOTTOM_LEFT): + crop_y_value += 1.0; + break; + case (GRAVITY_BOTTOM): + crop_x_value += 0.5; + crop_y_value += 1.0; + break; + case (GRAVITY_BOTTOM_RIGHT): + crop_x_value += 1.0; + crop_y_value += 1.0; + break; + } + + /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ + float x = 0.0; // left + float y = 0.0; // top + + // Adjust size for scale x and scale y + float sx = scale_x.GetValue(frame->number); // percentage X scale + float sy = scale_y.GetValue(frame->number); // percentage Y scale + float scaled_source_width = source_size.width() * sx; + float scaled_source_height = source_size.height() * sy; + + switch (gravity) + { + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; + case (GRAVITY_TOP): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + break; + case (GRAVITY_TOP_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + break; + case (GRAVITY_LEFT): + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_CENTER): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_BOTTOM_LEFT): + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + case (GRAVITY_BOTTOM): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + case (GRAVITY_BOTTOM_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); + + /* LOCATION, ROTATION, AND SCALE */ + float r = rotation.GetValue(frame->number); // rotate in degrees + x += (Settings::Instance()->MAX_WIDTH * location_x.GetValue(frame->number)); // move in percentage of final width + y += (Settings::Instance()->MAX_HEIGHT * location_y.GetValue(frame->number)); // move in percentage of final height + float shear_x_value = shear_x.GetValue(frame->number); + float shear_y_value = shear_y.GetValue(frame->number); + float origin_x_value = origin_x.GetValue(frame->number); + float origin_y_value = origin_y.GetValue(frame->number); + + bool transformed = false; + QTransform transform; + + // Transform source image (if needed) + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); + + if (!isEqual(x, 0) || !isEqual(y, 0)) { + // TRANSLATE/MOVE CLIP + transform.translate(x, y); + transformed = true; + } + + if (!isEqual(r, 0) || !isEqual(shear_x_value, 0) || !isEqual(shear_y_value, 0)) { + // ROTATE CLIP (around origin_x, origin_y) + float origin_x_offset = (scaled_source_width * origin_x_value); + float origin_y_offset = (scaled_source_height * origin_y_value); + transform.translate(origin_x_offset, origin_y_offset); + transform.rotate(r); + transform.shear(shear_x_value, shear_y_value); + transform.translate(-origin_x_offset,-origin_y_offset); + transformed = true; + } + + // SCALE CLIP (if needed) + float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx; + float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy; + + if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) { + transform.scale(source_width_scale, source_height_scale); + transformed = true; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); + + /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ + std::shared_ptr new_image; + new_image = std::shared_ptr(new QImage(*source_image)); + new_image->fill(QColor(QString::fromStdString("#00000000"))); + + // Load timeline's new frame image into a QPainter + QPainter painter(new_image.get()); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); + + // Apply transform (translate, rotate, scale)... if any + if (transformed) + painter.setTransform(transform); + + // Composite a new layer onto the image + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(0, 0, *source_image, crop_x_value * source_image->width(), crop_y_value * source_image->height(), crop_w_value * source_image->width(), crop_h_value * source_image->height()); + + // Draw frame #'s on top of image (if needed) + if (display != FRAME_DISPLAY_NONE) { + std::stringstream frame_number_str; + switch (display) + { + case (FRAME_DISPLAY_NONE): + // This is only here to prevent unused-enum warnings + break; + + case (FRAME_DISPLAY_CLIP): + frame_number_str << frame->number; + break; + + case (FRAME_DISPLAY_TIMELINE): + frame_number_str << "N/A"; + break; + + case (FRAME_DISPLAY_BOTH): + frame_number_str << "N/A" << " (" << frame->number << ")"; + break; + } + + // Draw frame number on top of image + painter.setPen(QColor("#ffffff")); + painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + } + + painter.end(); + + // Add new QImage to frame + frame->AddImage(new_image); + + // Return modified frame + return frame; +} diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 124058ac2..5fad97fe7 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -409,25 +409,6 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer", "new_frame->number", new_frame->number, "clip_frame_number", clip_frame_number, "timeline_frame_number", timeline_frame_number); - /* REPLACE IMAGE WITH WAVEFORM IMAGE (IF NEEDED) */ - if (source_clip->Waveform()) - { - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Generate Waveform Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); - - // Get the color of the waveform - int red = source_clip->wave_color.red.GetInt(clip_frame_number); - int green = source_clip->wave_color.green.GetInt(clip_frame_number); - int blue = source_clip->wave_color.blue.GetInt(clip_frame_number); - int alpha = source_clip->wave_color.alpha.GetInt(clip_frame_number); - - // Generate Waveform Dynamically (the size of the timeline) - std::shared_ptr source_image; - #pragma omp critical (T_addLayer) - source_image = source_frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); - source_frame->AddImage(std::shared_ptr(source_image)); - } - /* Apply effects to the source frame (if any). If multiple clips are overlapping, only process the * effects on the top clip. */ if (is_top_clip && source_frame) { @@ -498,7 +479,6 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in else // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (No Audio Copied - Wrong # of Channels)", "source_clip->Reader()->info.has_audio", source_clip->Reader()->info.has_audio, "source_frame->GetAudioChannelsCount()", source_frame->GetAudioChannelsCount(), "info.channels", info.channels, "clip_frame_number", clip_frame_number, "timeline_frame_number", timeline_frame_number); - } // Skip out if video was disabled or only an audio frame (no visualisation in use) @@ -513,253 +493,26 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Get actual frame image data source_image = source_frame->GetImage(); - /* ALPHA & OPACITY */ - if (source_clip->alpha.GetValue(clip_frame_number) != 1.0) - { - float alpha = source_clip->alpha.GetValue(clip_frame_number); - - // Get source image's pixels - unsigned char *pixels = (unsigned char *) source_image->bits(); - - // Loop through pixels - for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) - { - // Apply alpha to pixel - pixels[byte_index + 3] *= alpha; - } - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Set Alpha & Opacity)", "alpha", alpha, "source_frame->number", source_frame->number, "clip_frame_number", clip_frame_number); - } - - /* RESIZE SOURCE IMAGE - based on scale type */ - QSize source_size = source_image->size(); - switch (source_clip->scale) - { - case (SCALE_FIT): { - // keep aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_FIT)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_STRETCH): { - // ignore aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_STRETCH)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_CROP): { - QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); - QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); - - // respect aspect ratio - if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) - source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); - else - source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_CROP)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_NONE): { - // Calculate ratio of source size to project size - // Even with no scaling, previews need to be adjusted correctly - // (otherwise NONE scaling draws the frame image outside of the preview) - float source_width_ratio = source_size.width() / float(info.width); - float source_height_ratio = source_size.height() / float(info.height); - source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_NONE)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - } - - float crop_x = source_clip->crop_x.GetValue(clip_frame_number); - float crop_y = source_clip->crop_y.GetValue(clip_frame_number); - float crop_w = source_clip->crop_width.GetValue(clip_frame_number); - float crop_h = source_clip->crop_height.GetValue(clip_frame_number); - switch(source_clip->crop_gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - crop_x += 0.5; - break; - case (GRAVITY_TOP_RIGHT): - crop_x += 1.0; - break; - case (GRAVITY_LEFT): - crop_y += 0.5; - break; - case (GRAVITY_CENTER): - crop_x += 0.5; - crop_y += 0.5; - break; - case (GRAVITY_RIGHT): - crop_x += 1.0; - crop_y += 0.5; - break; - case (GRAVITY_BOTTOM_LEFT): - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM): - crop_x += 0.5; - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM_RIGHT): - crop_x += 1.0; - crop_y += 1.0; - break; - } - - - /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ - float x = 0.0; // left - float y = 0.0; // top - - // Adjust size for scale x and scale y - float sx = source_clip->scale_x.GetValue(clip_frame_number); // percentage X scale - float sy = source_clip->scale_y.GetValue(clip_frame_number); // percentage Y scale - float scaled_source_width = source_size.width() * sx; - float scaled_source_height = source_size.height() * sy; - - switch (source_clip->gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - break; - case (GRAVITY_TOP_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - break; - case (GRAVITY_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_CENTER): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_BOTTOM_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - case (GRAVITY_BOTTOM): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - case (GRAVITY_BOTTOM_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - } - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Gravity)", "source_frame->number", source_frame->number, "source_clip->gravity", source_clip->gravity, "info.width", info.width, "scaled_source_width", scaled_source_width, "info.height", info.height, "scaled_source_height", scaled_source_height); - - /* LOCATION, ROTATION, AND SCALE */ - float r = source_clip->rotation.GetValue(clip_frame_number); // rotate in degrees - x += (Settings::Instance()->MAX_WIDTH * source_clip->location_x.GetValue(clip_frame_number)); // move in percentage of final width - y += (Settings::Instance()->MAX_HEIGHT * source_clip->location_y.GetValue(clip_frame_number)); // move in percentage of final height - float shear_x = source_clip->shear_x.GetValue(clip_frame_number); - float shear_y = source_clip->shear_y.GetValue(clip_frame_number); - float origin_x = source_clip->origin_x.GetValue(clip_frame_number); - float origin_y = source_clip->origin_y.GetValue(clip_frame_number); - - bool transformed = false; - QTransform transform; - - // Transform source image (if needed) - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Build QTransform - if needed)", "source_frame->number", source_frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); - - if (!isEqual(x, 0) || !isEqual(y, 0)) { - // TRANSLATE/MOVE CLIP - transform.translate(x, y); - transformed = true; - } - - if (!isEqual(r, 0) || !isEqual(shear_x, 0) || !isEqual(shear_y, 0)) { - // ROTATE CLIP (around origin_x, origin_y) - float origin_x_value = (scaled_source_width * origin_x); - float origin_y_value = (scaled_source_height * origin_y); - transform.translate(origin_x_value, origin_y_value); - transform.rotate(r); - transform.shear(shear_x, shear_y); - transform.translate(-origin_x_value,-origin_y_value); - transformed = true; - } - - // SCALE CLIP (if needed) - float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx; - float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy; - - if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) { - transform.scale(source_width_scale, source_height_scale); - transformed = true; - } - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; - #pragma omp critical (T_addLayer) - new_image = new_frame->GetImage(); + new_image = new_frame->GetImage(); // Load timeline's new frame image into a QPainter QPainter painter(new_image.get()); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); - - // Apply transform (translate, rotate, scale)... if any - if (transformed) - painter.setTransform(transform); // Composite a new layer onto the image painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, crop_x * source_image->width(), crop_y * source_image->height(), crop_w * source_image->width(), crop_h * source_image->height()); - - // Draw frame #'s on top of image (if needed) - if (source_clip->display != FRAME_DISPLAY_NONE) { - std::stringstream frame_number_str; - switch (source_clip->display) - { - case (FRAME_DISPLAY_NONE): - // This is only here to prevent unused-enum warnings - break; - - case (FRAME_DISPLAY_CLIP): - frame_number_str << clip_frame_number; - break; - - case (FRAME_DISPLAY_TIMELINE): - frame_number_str << timeline_frame_number; - break; - - case (FRAME_DISPLAY_BOTH): - frame_number_str << timeline_frame_number << " (" << clip_frame_number << ")"; - break; - } - - // Draw frame number on top of image - painter.setPen(QColor("#ffffff")); - painter.drawText(20, 20, QString(frame_number_str.str().c_str())); - } - + painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); painter.end(); + // Add new QImage to frame + new_frame->AddImage(new_image); + // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); } // Update the list of 'opened' clips From 5a9d47a03d71fec8dba37ddb99bde3dbdd0a38e4 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 17:05:50 -0500 Subject: [PATCH 24/86] Refactored the Settings::Instance()->MAX_WIDTH and Settings::Instance()->MAX_HEIGHT out of the Cilp class. GetFrame() now has an overload which specifies the width, height, and samples needed. Otherwise, it returns the Clip image based on the source reader (width, height, num samples). --- include/Clip.h | 16 +++++++- src/Clip.cpp | 96 +++++++++++++++++++++++++++++++----------------- src/Timeline.cpp | 6 ++- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index 78ffa4abf..71ab83c37 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -119,7 +119,7 @@ namespace openshot { std::shared_ptr apply_effects(std::shared_ptr frame); /// Apply keyframes to the source frame (if any) - std::shared_ptr apply_keyframes(std::shared_ptr frame); + std::shared_ptr apply_keyframes(std::shared_ptr frame, int width, int height); /// Get file extension std::string get_file_extension(std::string path); @@ -188,12 +188,24 @@ namespace openshot { /// Return the list of effects on the timeline std::list Effects() { return effects; }; - /// @brief Get an openshot::Frame object for a specific frame number of this timeline. + /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number + /// of samples match the source reader. /// /// @returns The requested frame (containing the image) /// @param requested_frame The frame number that is requested std::shared_ptr GetFrame(int64_t requested_frame); + /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number + /// of samples can be customized to match the Timeline, or any custom output. Extra samples will be moved to the + /// next Frame. Missing samples will be moved from the next Frame. + /// + /// @returns The requested frame (containing the image) + /// @param requested_frame The frame number that is requested + /// @param width The width of the image requested + /// @param height The height of the image requested + /// @param samples The number of samples requested + std::shared_ptr GetFrame(int64_t requested_frame, int width, int height, int samples); + /// Open the internal reader void Open(); diff --git a/src/Clip.cpp b/src/Clip.cpp index c3460ff81..0c664ebe0 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -325,6 +325,33 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Adjust out of bounds frame number requested_frame = adjust_frame_number_minimum(requested_frame); + // Is a time map detected + int64_t new_frame_number = requested_frame; + int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); + if (time.GetLength() > 1) + new_frame_number = time_mapped_number; + + // Get the # of audio samples from the time mapped Frame instance + std::shared_ptr time_mapped_original_frame = GetOrCreateFrame(new_frame_number); + return GetFrame(requested_frame, reader->info.width, reader->info.height, time_mapped_original_frame->GetAudioSamplesCount()); + } + else + // Throw error if reader not initialized + throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method."); +} + +// Get an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int height, int samples) +{ + // Check for open reader (or throw exception) + if (!is_open) + throw ReaderClosed("The Clip is closed. Call Open() before calling this method", "N/A"); + + if (reader) + { + // Adjust out of bounds frame number + requested_frame = adjust_frame_number_minimum(requested_frame); + // Adjust has_video and has_audio overrides int enabled_audio = has_audio.GetInt(requested_frame); if (enabled_audio == -1 && reader && reader->info.has_audio) @@ -364,13 +391,17 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) frame->AddAudio(true, channel, 0, original_frame->GetAudioSamples(channel), original_frame->GetAudioSamplesCount(), 1.0); // Get time mapped frame number (used to increase speed, change direction, etc...) + // TODO: Handle variable # of samples, since this resamples audio for different speeds (only when time curve is set) get_time_mapped_frame(frame, requested_frame); + // Adjust # of samples to match requested (the interaction with time curves will make this tricky) + // TODO: Implement move samples to/from next frame + // Apply effects to the frame (if any) apply_effects(frame); // Apply keyframe / transforms - apply_keyframes(frame); + apply_keyframes(frame, width, height); // Return processed 'frame' return frame; @@ -631,13 +662,9 @@ int64_t Clip::adjust_frame_number_minimum(int64_t frame_number) std::shared_ptr Clip::GetOrCreateFrame(int64_t number) { std::shared_ptr new_frame; - - // Init some basic properties about this frame - int samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels); - try { // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number, "samples_in_frame", samples_in_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number); // Attempt to get a frame (but this could fail if a reader has just been closed) new_frame = reader->GetFrame(number); @@ -654,14 +681,17 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number) // ... } + // Estimate # of samples needed for this frame + int estimated_samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels); + // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "samples_in_frame", samples_in_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "estimated_samples_in_frame", estimated_samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", samples_in_frame, reader->info.channels); + new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); new_frame->SampleRate(reader->info.sample_rate); new_frame->ChannelsLayout(reader->info.channel_layout); - new_frame->AddAudioSilence(samples_in_frame); + new_frame->AddAudioSilence(estimated_samples_in_frame); return new_frame; } @@ -1063,7 +1093,7 @@ bool Clip::isEqual(double a, double b) // Apply keyframes to the source frame (if any) -std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) +std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int width, int height) { // Get actual frame image data std::shared_ptr source_image = frame->GetImage(); @@ -1081,7 +1111,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) int alpha = wave_color.alpha.GetInt(frame->number); // Generate Waveform Dynamically (the size of the timeline) - source_image = frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); + source_image = frame->GetWaveform(width, height, red, green, blue, alpha); frame->AddImage(std::shared_ptr(source_image)); } @@ -1110,7 +1140,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) { case (SCALE_FIT): { // keep aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); + source_size.scale(width, height, Qt::KeepAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); @@ -1118,18 +1148,18 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) } case (SCALE_STRETCH): { // ignore aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); + source_size.scale(width, height, Qt::IgnoreAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_CROP): { - QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); - QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); + QSize width_size(width, round(width / (float(source_size.width()) / float(source_size.height())))); + QSize height_size(round(height / (float(source_size.height()) / float(source_size.width()))), height); // respect aspect ratio - if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) + if (width_size.width() >= width && width_size.height() >= height) source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); else source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); @@ -1142,9 +1172,9 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) // Calculate ratio of source size to project size // Even with no scaling, previews need to be adjusted correctly // (otherwise NONE scaling draws the frame image outside of the preview) - float source_width_ratio = source_size.width() / float(Settings::Instance()->MAX_WIDTH); - float source_height_ratio = source_size.height() / float(Settings::Instance()->MAX_HEIGHT); - source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); + float source_width_ratio = source_size.width() / float(width); + float source_height_ratio = source_size.height() / float(height); + source_size.scale(width * source_width_ratio, height * source_height_ratio, Qt::KeepAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); @@ -1207,32 +1237,32 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) // This is only here to prevent unused-enum warnings break; case (GRAVITY_TOP): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + x = (width - scaled_source_width) / 2.0; // center break; case (GRAVITY_TOP_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + x = width - scaled_source_width; // right break; case (GRAVITY_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_CENTER): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + x = (width - scaled_source_width) / 2.0; // center + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + x = width - scaled_source_width; // right + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_BOTTOM_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + y = (height - scaled_source_height); // bottom break; case (GRAVITY_BOTTOM): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + x = (width - scaled_source_width) / 2.0; // center + y = (height - scaled_source_height); // bottom break; case (GRAVITY_BOTTOM_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + x = width - scaled_source_width; // right + y = (height - scaled_source_height); // bottom break; } @@ -1241,8 +1271,8 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) /* LOCATION, ROTATION, AND SCALE */ float r = rotation.GetValue(frame->number); // rotate in degrees - x += (Settings::Instance()->MAX_WIDTH * location_x.GetValue(frame->number)); // move in percentage of final width - y += (Settings::Instance()->MAX_HEIGHT * location_y.GetValue(frame->number)); // move in percentage of final height + x += (width * location_x.GetValue(frame->number)); // move in percentage of final width + y += (height * location_y.GetValue(frame->number)); // move in percentage of final height float shear_x_value = shear_x.GetValue(frame->number); float shear_y_value = shear_y.GetValue(frame->number); float origin_x_value = origin_x.GetValue(frame->number); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 5fad97fe7..7f068d1a6 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -368,7 +368,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number)); + new_frame = std::shared_ptr(clip->GetFrame(number, info.width, info.height, samples_in_frame)); // Return real frame return new_frame; @@ -668,8 +668,10 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Get clip frame # long clip_start_frame = (clip->Start() * info.fps.ToDouble()) + 1; long clip_frame_number = frame_number - clip_start_position + clip_start_frame; + int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); + // Cache clip object - clip->GetFrame(clip_frame_number); + clip->GetFrame(clip_frame_number, info.width, info.height, samples_in_frame); } } } From 453d55f41a36f6e1db74771d1cc017b0e477c3df Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 22:47:31 -0500 Subject: [PATCH 25/86] Fixed a bug with cropping logic on Clip (disabled it temporarily). I need to replace the Crop functionality with a more robust cropping tool. Also, updated Timeline to use the MaxWidth/MaxHeight settings when calling the clip (since those are set when the screen is resized). --- include/Clip.h | 9 ++++++--- src/Clip.cpp | 39 +++++++++++++++++++++++++++++---------- src/Timeline.cpp | 4 ++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index 71ab83c37..51f9a859e 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -116,10 +116,10 @@ namespace openshot { int64_t adjust_frame_number_minimum(int64_t frame_number); /// Apply effects to the source frame (if any) - std::shared_ptr apply_effects(std::shared_ptr frame); + void apply_effects(std::shared_ptr frame); /// Apply keyframes to the source frame (if any) - std::shared_ptr apply_keyframes(std::shared_ptr frame, int width, int height); + void apply_keyframes(std::shared_ptr frame, int width, int height); /// Get file extension std::string get_file_extension(std::string path); @@ -146,6 +146,9 @@ namespace openshot { void reverse_buffer(juce::AudioSampleBuffer* buffer); public: + /// Final cache object used to hold final frames + CacheMemory final_cache; + openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent openshot::AnchorType anchor; ///< The anchor determines what parent a clip should snap to @@ -168,7 +171,7 @@ namespace openshot { /// Get the cache object used by this reader (always returns NULL for this object) - CacheMemory* GetCache() override { return NULL; }; + CacheMemory* GetCache() override { return &final_cache; }; /// Determine if reader is open or closed bool IsOpen() override { return is_open; }; diff --git a/src/Clip.cpp b/src/Clip.cpp index 0c664ebe0..7a504cc9d 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -107,6 +107,9 @@ void Clip::init_settings() // Init audio and video overrides has_audio = Keyframe(-1.0); has_video = Keyframe(-1.0); + + // Initialize Clip cache + final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); } // Init reader's rotation (if any) @@ -352,6 +355,16 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he // Adjust out of bounds frame number requested_frame = adjust_frame_number_minimum(requested_frame); + // Check the cache for this frame + std::shared_ptr cached_frame = final_cache.GetFrame(requested_frame); + if (cached_frame) { + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", requested_frame); + + // Return the cached frame + return cached_frame; + } + // Adjust has_video and has_audio overrides int enabled_audio = has_audio.GetInt(requested_frame); if (enabled_audio == -1 && reader && reader->info.has_audio) @@ -403,6 +416,9 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he // Apply keyframe / transforms apply_keyframes(frame, width, height); + // Cache frame + final_cache.Add(frame); + // Return processed 'frame' return frame; } @@ -874,6 +890,9 @@ void Clip::SetJsonValue(const Json::Value root) { // Set parent data ClipBase::SetJsonValue(root); + // Clear cache + final_cache.Clear(); + // Set data from Json (if key is found) if (!root["gravity"].isNull()) gravity = (GravityType) root["gravity"].asInt(); @@ -1062,16 +1081,22 @@ void Clip::AddEffect(EffectBase* effect) // Sort effects sort_effects(); + + // Clear cache + final_cache.Clear(); } // Remove an effect from the clip void Clip::RemoveEffect(EffectBase* effect) { effects.remove(effect); + + // Clear cache + final_cache.Clear(); } // Apply effects to the source frame (if any) -std::shared_ptr Clip::apply_effects(std::shared_ptr frame) +void Clip::apply_effects(std::shared_ptr frame) { // Find Effects at this position and layer for (auto effect : effects) @@ -1080,9 +1105,6 @@ std::shared_ptr Clip::apply_effects(std::shared_ptr frame) frame = effect->GetFrame(frame, frame->number); } // end effect loop - - // Return modified frame - return frame; } // Compare 2 floating point numbers for equality @@ -1093,7 +1115,7 @@ bool Clip::isEqual(double a, double b) // Apply keyframes to the source frame (if any) -std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int width, int height) +void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) { // Get actual frame image data std::shared_ptr source_image = frame->GetImage(); @@ -1315,7 +1337,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; - new_image = std::shared_ptr(new QImage(*source_image)); + new_image = std::shared_ptr(new QImage(QSize(width, height), source_image->format())); new_image->fill(QColor(QString::fromStdString("#00000000"))); // Load timeline's new frame image into a QPainter @@ -1328,7 +1350,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w // Composite a new layer onto the image painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, crop_x_value * source_image->width(), crop_y_value * source_image->height(), crop_w_value * source_image->width(), crop_h_value * source_image->height()); + painter.drawImage(0, 0, *source_image); // Draw frame #'s on top of image (if needed) if (display != FRAME_DISPLAY_NONE) { @@ -1361,7 +1383,4 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w // Add new QImage to frame frame->AddImage(new_image); - - // Return modified frame - return frame; } diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 7f068d1a6..d9b23ca5f 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -368,7 +368,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number, info.width, info.height, samples_in_frame)); + new_frame = std::shared_ptr(clip->GetFrame(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame)); // Return real frame return new_frame; @@ -671,7 +671,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Cache clip object - clip->GetFrame(clip_frame_number, info.width, info.height, samples_in_frame); + clip->GetFrame(clip_frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame); } } } From 47fca0254043f7d3fbf9d9577bac4ba570b10e2c Mon Sep 17 00:00:00 2001 From: eisneinechse <42617957+eisneinechse@users.noreply.github.com> Date: Sun, 13 Sep 2020 09:07:14 -0700 Subject: [PATCH 26/86] video_codec -> video_codec_ctx --- src/FFmpegWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 8b89de7fc..5dd142007 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -339,7 +339,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va if (info.has_video && stream == VIDEO_STREAM && video_st) { st = video_st; // Get codec context - c = AV_GET_CODEC_PAR_CONTEXT(st, video_codec); + c = AV_GET_CODEC_PAR_CONTEXT(st, video_codec_ctx); // Was a codec / stream found? if (c) { if (info.interlaced_frame) { From 1c8aea94d0a86985934fb07e28dfdd4ecdf850ee Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 13 Sep 2020 16:28:31 -0400 Subject: [PATCH 27/86] Frame: Put Qt includes where they're used --- include/Frame.h | 12 ++---------- src/Frame.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/Frame.h b/include/Frame.h index 8987dcb40..b3416d388 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -34,16 +34,8 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include #include "ZmqLogger.h" diff --git a/src/Frame.cpp b/src/Frame.cpp index 54901cc56..48af2a283 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -29,6 +29,21 @@ */ #include "../include/Frame.h" +#include "JuceHeader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; using namespace openshot; From 9c83429ab1042a992511a30ede220214c593c31b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 13 Sep 2020 16:33:36 -0400 Subject: [PATCH 28/86] Frame: Use delegating constructors --- src/Frame.cpp | 54 +++++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index 48af2a283..67371655e 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -48,49 +48,31 @@ using namespace std; using namespace openshot; -// Constructor - blank frame -Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), color("#000000"), - channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), - max_audio_sample(0) -{ - // Allocate and zero (fill with silence) the audio buffer - audio = std::make_shared(channels, 0); - audio->clear(); -} - -// Constructor - image only -Frame::Frame(int64_t number, int width, int height, std::string color) - : number(number), pixel_ratio(1,1), channels(2), width(width), height(height), color(color), - channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), - max_audio_sample(0) -{ - // Allocate and zero (fill with silence) the audio buffer - audio = std::make_shared(channels, 0); - audio->clear(); -} - -// Constructor - audio only -Frame::Frame(int64_t number, int samples, int channels) : - number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), color("#000000"), - channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), - max_audio_sample(0) -{ - // Allocate and zero (fill with silence) the audio buffer - audio = std::make_shared(channels, samples); - audio->clear(); -} - // Constructor - image & audio Frame::Frame(int64_t number, int width, int height, std::string color, int samples, int channels) - : number(number), pixel_ratio(1,1), channels(channels), width(width), height(height), color(color), - channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false), + : audio(std::make_shared(channels, samples)), + number(number), width(width), height(height), + pixel_ratio(1,1), color(color), qbuffer(NULL), + channels(channels), channel_layout(LAYOUT_STEREO), + sample_rate(44100), + has_audio_data(false), has_image_data(false), max_audio_sample(0) { - // Allocate and zero (fill with silence) the audio buffer - audio = std::make_shared(channels, samples); + // zero (fill with silence) the audio buffer audio->clear(); } +// Delegating Constructor - blank frame +Frame::Frame() : Frame::Frame(1, 1, 1, "#000000", 0, 2) {}; + +// Delegating Constructor - image only +Frame::Frame(int64_t number, int width, int height, std::string color) + : Frame::Frame(number, width, height, color, 0, 2) {}; + +// Delegating Constructor - audio only +Frame::Frame(int64_t number, int samples, int channels) + : Frame::Frame(number, 1, 1, "#000000", samples, channels) {}; + // Copy constructor Frame::Frame ( const Frame &other ) From 0974637a3a71a1acd115bac33634ea6d646c7472 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 13 Sep 2020 16:33:16 -0400 Subject: [PATCH 29/86] Pixelate: Fix missing includes --- include/effects/Pixelate.h | 1 - src/effects/Pixelate.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/effects/Pixelate.h b/include/effects/Pixelate.h index 4cdd440f2..793f4d46f 100644 --- a/include/effects/Pixelate.h +++ b/include/effects/Pixelate.h @@ -36,7 +36,6 @@ #include #include #include -#include "../Color.h" #include "../Json.h" #include "../KeyFrame.h" diff --git a/src/effects/Pixelate.cpp b/src/effects/Pixelate.cpp index c993915c5..41d97c5c2 100644 --- a/src/effects/Pixelate.cpp +++ b/src/effects/Pixelate.cpp @@ -29,6 +29,12 @@ */ #include "../../include/effects/Pixelate.h" +#include "Json.h" + +#include +#include +#include +#include using namespace openshot; From 92d33a1ebd5a7da249195db37afb7edb4a3abceb Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 13 Sep 2020 16:38:23 -0400 Subject: [PATCH 30/86] VideoRenderWidget: missing includes --- include/Qt/VideoRenderWidget.h | 6 ++++-- src/Qt/VideoRenderWidget.cpp | 8 +++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/Qt/VideoRenderWidget.h b/include/Qt/VideoRenderWidget.h index 07c61037b..4d9ac17ff 100644 --- a/include/Qt/VideoRenderWidget.h +++ b/include/Qt/VideoRenderWidget.h @@ -31,11 +31,13 @@ #ifndef OPENSHOT_VIDEO_RENDERER_WIDGET_H #define OPENSHOT_VIDEO_RENDERER_WIDGET_H -#include -#include #include "../Fraction.h" #include "VideoRenderer.h" +#include +#include +#include +#include class VideoRenderWidget : public QWidget { diff --git a/src/Qt/VideoRenderWidget.cpp b/src/Qt/VideoRenderWidget.cpp index 2bfe8fa23..4af1ac6ab 100644 --- a/src/Qt/VideoRenderWidget.cpp +++ b/src/Qt/VideoRenderWidget.cpp @@ -29,7 +29,13 @@ */ #include "../../include/Qt/VideoRenderWidget.h" -#include +#include +#include +#include +#include +#include +#include + VideoRenderWidget::VideoRenderWidget(QWidget *parent) : QWidget(parent), renderer(new VideoRenderer(this)) From 0bcf1e49247977e2821e093573d9d64cf7585caf Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 13 Sep 2020 19:56:40 -0400 Subject: [PATCH 31/86] Frame: Reduce code duplication --- src/Frame.cpp | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index 67371655e..c3c50bcd5 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -762,31 +762,31 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) } // Add (or replace) pixel data to the frame -void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_) +void Frame::AddImage( + int new_width, int new_height, int bytes_per_pixel, + QImage::Format type, const unsigned char *pixels_) { // Create new buffer - const GenericScopedLock lock(addingImageSection); - int buffer_size = new_width * new_height * bytes_per_pixel; - qbuffer = new unsigned char[buffer_size](); - - // Copy buffer data - memcpy((unsigned char*)qbuffer, pixels_, buffer_size); - - // Create new image object, and fill with pixel data - #pragma omp critical (AddImage) { - image = std::make_shared( - qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer); - - // Always convert to RGBA8888 (if different) - if (image->format() != QImage::Format_RGBA8888) - *image = image->convertToFormat(QImage::Format_RGBA8888); - - // Update height and width - width = image->width(); - height = image->height(); - has_image_data = true; - } + const GenericScopedLock lock(addingImageSection); + int buffer_size = new_width * new_height * bytes_per_pixel; + qbuffer = new unsigned char[buffer_size](); + + // Copy buffer data + memcpy((unsigned char*)qbuffer, pixels_, buffer_size); + + } // Release addingImageSection lock + + // Create new image object from pixel data + auto new_image = std::make_shared( + qbuffer, + new_width, new_height, + new_width * bytes_per_pixel, + type, + (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, + (void*) qbuffer + ); + AddImage(new_image); } // Add (or replace) pixel data to the frame @@ -826,7 +826,6 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) AddImage(new_image); } else { - // Ignore image of different sizes or formats bool ret=false; #pragma omp critical (AddImage) From 965de9df5bdd1597858d533128b7091128ab9358 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 3 Oct 2020 20:23:42 -0400 Subject: [PATCH 32/86] Add ENABLE_MAGICK CMake option (default ON) --- CMakeLists.txt | 1 + src/CMakeLists.txt | 48 +++++++++++++++++++------------------ tests/CMakeLists.txt | 57 ++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b09879b..77aede7d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF) option(ENABLE_TESTS "Build unit tests (requires UnitTest++)" ON) option(ENABLE_DOCS "Build API documentation (requires Doxygen)" ON) option(APPIMAGE_BUILD "Build to install in an AppImage (Linux only)" OFF) +option(ENABLE_MAGICK "Use ImageMagick, if available" ON) # Legacy commandline override if (DISABLE_TESTS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dec7c8535..04121c225 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,29 +65,31 @@ endif() ################ IMAGE MAGICK ################## # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) -IF (MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) -ELSE (MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) -ENDIF (MAGICKCORE_QUANTUM_DEPTH) -IF (MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) -ELSE (MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) -ENDIF (MAGICKCORE_HDRI_ENABLE) - -# Find the ImageMagick++ library -find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) -if (ImageMagick_FOUND) - # Include ImageMagick++ headers (needed for compile) - include_directories(${ImageMagick_INCLUDE_DIRS}) - - # define a global var (used in the C++) - add_definitions( -DUSE_IMAGEMAGICK=1 ) - list(APPEND CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") - - set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE) - mark_as_advanced(HAVE_IMAGEMAGICK) +if(ENABLE_MAGICK) + IF (MAGICKCORE_QUANTUM_DEPTH) + add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) + ELSE (MAGICKCORE_QUANTUM_DEPTH) + add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) + ENDIF (MAGICKCORE_QUANTUM_DEPTH) + IF (MAGICKCORE_HDRI_ENABLE) + add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) + ELSE (MAGICKCORE_HDRI_ENABLE) + add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) + ENDIF (MAGICKCORE_HDRI_ENABLE) + + # Find the ImageMagick++ library + find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) + if (ImageMagick_FOUND) + # Include ImageMagick++ headers (needed for compile) + include_directories(${ImageMagick_INCLUDE_DIRS}) + + # define a global var (used in the C++) + add_definitions( -DUSE_IMAGEMAGICK=1 ) + list(APPEND CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") + + set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE) + mark_as_advanced(HAVE_IMAGEMAGICK) + endif() endif() ################# LIBOPENSHOT-AUDIO ################### diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3d0dd2d48..78d4ae972 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -58,35 +58,36 @@ set_package_properties(UnitTest++ PROPERTIES PURPOSE "Unit testing framework") ################ IMAGE MAGICK ################## -# Set the Quantum Depth that ImageMagick was built with (default to 16 bits) -if(MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) -else() - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) -endif() - -if(MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) -else() - add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) +if(ENABLE_MAGICK) + # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) + if(MAGICKCORE_QUANTUM_DEPTH) + add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) + else() + add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) + endif() + + if(MAGICKCORE_HDRI_ENABLE) + add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) + else() + add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) + endif() + + if(OPENSHOT_IMAGEMAGICK_COMPATIBILITY) + add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=${OPENSHOT_IMAGEMAGICK_COMPATIBILITY} ) + else() + add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=0 ) + endif() + + # Find the ImageMagick++ library + find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) + if(ImageMagick_FOUND) + # Include ImageMagick++ headers (needed for compile) + include_directories(${ImageMagick_INCLUDE_DIRS}) + + # define a global var (used in the C++) + add_definitions( -DUSE_IMAGEMAGICK=1 ) + endif() endif() - -if(OPENSHOT_IMAGEMAGICK_COMPATIBILITY) - add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=${OPENSHOT_IMAGEMAGICK_COMPATIBILITY} ) -else() - add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=0 ) -endif() - -# Find the ImageMagick++ library -find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) -if(ImageMagick_FOUND) - # Include ImageMagick++ headers (needed for compile) - include_directories(${ImageMagick_INCLUDE_DIRS}) - - # define a global var (used in the C++) - add_definitions( -DUSE_IMAGEMAGICK=1 ) -endif() - ################# LIBOPENSHOT-AUDIO ################### # Find JUCE-based openshot Audio libraries find_package(OpenShotAudio 0.2.0 REQUIRED) From 172c3c0e0f1623297137c88bc23112805f0c0256 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 3 Oct 2020 20:24:13 -0400 Subject: [PATCH 33/86] CMake: Switch default build type to Release --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77aede7d8..38abe4f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,9 +130,9 @@ add_feature_info("Coverage" ENABLE_COVERAGE "analyze test coverage and generate # -DDEBUG for debug builds. We'll do this for all OSes, even # though only MacOS requires it. set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -# Make sure we've picked some build type, default to debug +# Make sure we've picked some build type, default to release if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_BUILD_TYPE "Debug") + set(CMAKE_BUILD_TYPE "Release") endif() ############## PROCESS src/ DIRECTORIES ############## From f9a717ef4b338db5d7188b6994b63f06bfb2aea9 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 4 Oct 2020 16:59:21 -0500 Subject: [PATCH 34/86] Large refactor of Timeline, TimelineBase, ClipBase, and Clip, to allow a Clip access to the parent timeline instance (if available), and thus, certain properties (preview size, timeline FPS, etc...). This allows for a simpler rendering of Clip keyframes (during the Clip::GetFrame method), and a simpler Timeline class, that can change the preview window size dynamically and no longer requires a Singleton Settings class. - Also removed "crop" from Clip class, as it was never implmeneted correctly, and we have a fully functional "crop" effect when needed - Added caching to Clip class, to optimize previewing of cached frames (much faster than previous) --- include/Clip.h | 45 +++--- include/ClipBase.h | 26 ++++ include/EffectBase.h | 21 ++- include/OpenShot.h | 1 + include/ReaderBase.h | 10 +- include/Timeline.h | 4 +- include/TimelineBase.h | 47 +++++++ include/effects/Bars.h | 20 ++- include/effects/Blur.h | 20 ++- include/effects/Brightness.h | 20 ++- include/effects/ChromaKey.h | 20 ++- include/effects/ColorShift.h | 20 ++- include/effects/Crop.h | 20 ++- include/effects/Deinterlace.h | 20 ++- include/effects/Hue.h | 20 ++- include/effects/Mask.h | 20 ++- include/effects/Negate.h | 20 ++- include/effects/Pixelate.h | 20 ++- include/effects/Saturation.h | 20 ++- include/effects/Shift.h | 20 ++- include/effects/Wave.h | 20 ++- src/CMakeLists.txt | 1 + src/ChunkReader.cpp | 2 - src/ChunkWriter.cpp | 8 -- src/Clip.cpp | 242 +++++++++++++-------------------- src/EffectBase.cpp | 10 ++ src/FFmpegReader.cpp | 17 +-- src/Qt/PlayerDemo.cpp | 5 - src/QtImageReader.cpp | 17 +-- src/ReaderBase.cpp | 10 +- src/Settings.cpp | 2 - src/Timeline.cpp | 81 ++++++----- src/TimelineBase.cpp | 33 +++++ src/bindings/python/openshot.i | 2 + src/bindings/ruby/openshot.i | 2 + 35 files changed, 519 insertions(+), 347 deletions(-) create mode 100644 include/TimelineBase.h create mode 100644 src/TimelineBase.cpp diff --git a/include/Clip.h b/include/Clip.h index 51f9a859e..2f1c5cf57 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -42,6 +42,7 @@ #include "Effects.h" #include "EffectInfo.h" #include "Fraction.h" +#include "Frame.h" #include "KeyFrame.h" #include "ReaderBase.h" #include "JuceHeader.h" @@ -146,9 +147,6 @@ namespace openshot { void reverse_buffer(juce::AudioSampleBuffer* buffer); public: - /// Final cache object used to hold final frames - CacheMemory final_cache; - openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent openshot::AnchorType anchor; ///< The anchor determines what parent a clip should snap to @@ -169,9 +167,8 @@ namespace openshot { /// Destructor virtual ~Clip(); - - /// Get the cache object used by this reader (always returns NULL for this object) - CacheMemory* GetCache() override { return &final_cache; }; + /// Get the cache object used by this clip + CacheMemory* GetCache() { return &cache; }; /// Determine if reader is open or closed bool IsOpen() override { return is_open; }; @@ -191,23 +188,24 @@ namespace openshot { /// Return the list of effects on the timeline std::list Effects() { return effects; }; - /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number - /// of samples match the source reader. + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. /// - /// @returns The requested frame (containing the image) - /// @param requested_frame The frame number that is requested - std::shared_ptr GetFrame(int64_t requested_frame); + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number); - /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number - /// of samples can be customized to match the Timeline, or any custom output. Extra samples will be moved to the - /// next Frame. Missing samples will be moved from the next Frame. + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// modified openshot::Frame object /// - /// @returns The requested frame (containing the image) - /// @param requested_frame The frame number that is requested - /// @param width The width of the image requested - /// @param height The height of the image requested - /// @param samples The number of samples requested - std::shared_ptr GetFrame(int64_t requested_frame, int width, int height, int samples); + /// A new openshot::Frame objects is returned, based on a copy from the source image, with all keyframes and clip effects + /// rendered. + /// + /// @returns The modified openshot::Frame object + /// @param frame This is ignored on Clip, due to caching optimizations. This frame instance is clobbered with the source frame. + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Open the internal reader void Open(); @@ -262,13 +260,6 @@ namespace openshot { /// Curve representing the color of the audio wave form openshot::Color wave_color; - // Crop settings and curves - openshot::GravityType crop_gravity; ///< Cropping needs to have a gravity to determine what side we are cropping - openshot::Keyframe crop_width; ///< Curve representing width in percent (0.0=0%, 1.0=100%) - openshot::Keyframe crop_height; ///< Curve representing height in percent (0.0=0%, 1.0=100%) - openshot::Keyframe crop_x; ///< Curve representing X offset in percent (-1.0=-100%, 0.0=0%, 1.0=100%) - openshot::Keyframe crop_y; ///< Curve representing Y offset in percent (-1.0=-100%, 0.0=0%, 1.0=100%) - // Perspective curves openshot::Keyframe perspective_c1_x; ///< Curves representing X for coordinate 1 openshot::Keyframe perspective_c1_y; ///< Curves representing Y for coordinate 1 diff --git a/include/ClipBase.h b/include/ClipBase.h index 1f7f55c47..e335c501b 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -33,10 +33,13 @@ #include #include +#include "CacheMemory.h" #include "Exceptions.h" +#include "Frame.h" #include "Point.h" #include "KeyFrame.h" #include "Json.h" +#include "TimelineBase.h" namespace openshot { @@ -54,6 +57,7 @@ namespace openshot { float start; ///< The position in seconds to start playing (used to trim the beginning of a clip) float end; ///< The position in seconds to end playing (used to trim the ending of a clip) std::string previous_properties; ///< This string contains the previous JSON properties + openshot::TimelineBase* timeline; ///< Pointer to the parent timeline instance (if any) /// Generate JSON for a property Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const; @@ -62,6 +66,7 @@ namespace openshot { Json::Value add_property_choice_json(std::string name, int value, int selected_value) const; public: + CacheMemory cache; /// Constructor for the base clip ClipBase() { }; @@ -72,6 +77,25 @@ namespace openshot { bool operator> ( ClipBase& a) { return (Position() > a.Position()); } bool operator>= ( ClipBase& a) { return (Position() >= a.Position()); } + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + virtual std::shared_ptr GetFrame(int64_t frame_number) = 0; + + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// modified openshot::Frame object + /// + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. + /// + /// @returns The modified openshot::Frame object + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + virtual std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) = 0; + /// Get basic properties std::string Id() const { return id; } ///< Get the Id of this clip object float Position() const { return position; } ///< Get position on timeline (in seconds) @@ -79,6 +103,7 @@ namespace openshot { float Start() const { return start; } ///< Get start position (in seconds) of clip (trim start of video) float End() const { return end; } ///< Get end position (in seconds) of clip (trim end of video) float Duration() const { return end - start; } ///< Get the length of this clip (in seconds) + openshot::TimelineBase* ParentTimeline() { return timeline; } ///< Get the associated Timeline pointer (if any) /// Set basic properties void Id(std::string value) { id = value; } ///> Set the Id of this clip object @@ -86,6 +111,7 @@ namespace openshot { void Layer(int value) { layer = value; } ///< Set layer of clip on timeline (lower number is covered by higher numbers) void Start(float value) { start = value; } ///< Set start position (in seconds) of clip (trim start of video) void End(float value) { end = value; } ///< Set end position (in seconds) of clip (trim end of video) + void ParentTimeline(openshot::TimelineBase* new_timeline) { timeline = new_timeline; } ///< Set associated Timeline pointer /// Get and Set JSON methods virtual std::string Json() const = 0; ///< Generate JSON string of this object diff --git a/include/EffectBase.h b/include/EffectBase.h index 1f967a021..353e18177 100644 --- a/include/EffectBase.h +++ b/include/EffectBase.h @@ -67,6 +67,10 @@ namespace openshot { private: int order; ///< The order to evaluate this effect. Effects are processed in this order (when more than one overlap). + + protected: + openshot::ClipBase* clip; ///< Pointer to the parent clip instance (if any) + public: /// Information about the current effect @@ -78,21 +82,16 @@ namespace openshot /// Constrain a color value from 0 to 255 int constrain(int color_value); - /// @brief This method is required for all derived classes of EffectBase, and returns a - /// modified openshot::Frame object - /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). - /// - /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - virtual std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) = 0; - /// Initialize the values of the EffectInfo struct. It is important for derived classes to call /// this method, or the EffectInfo struct values will not be initialized. void InitEffectInfo(); + /// Parent clip object of this effect (which can be unparented and NULL) + openshot::ClipBase* ParentClip(); + + /// Set parent clip object of this effect + void ParentClip(openshot::ClipBase* new_clip); + /// Get and Set JSON methods virtual std::string Json() const = 0; ///< Generate JSON string of this object virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object diff --git a/include/OpenShot.h b/include/OpenShot.h index 5273ff0d3..56f847d17 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -138,6 +138,7 @@ #include "QtHtmlReader.h" #include "QtImageReader.h" #include "QtTextReader.h" +#include "TimelineBase.h" #include "Timeline.h" #include "Settings.h" diff --git a/include/ReaderBase.h b/include/ReaderBase.h index ab7657537..e2d93841b 100644 --- a/include/ReaderBase.h +++ b/include/ReaderBase.h @@ -98,9 +98,9 @@ namespace openshot { protected: /// Section lock for multiple threads - juce::CriticalSection getFrameCriticalSection; - juce::CriticalSection processingCriticalSection; - openshot::ClipBase* parent; + juce::CriticalSection getFrameCriticalSection; + juce::CriticalSection processingCriticalSection; + openshot::ClipBase* clip; ///< Pointer to the parent clip instance (if any) public: @@ -111,10 +111,10 @@ namespace openshot openshot::ReaderInfo info; /// Parent clip object of this reader (which can be unparented and NULL) - openshot::ClipBase* GetClip(); + openshot::ClipBase* ParentClip(); /// Set parent clip object of this reader - void SetClip(openshot::ClipBase* clip); + void ParentClip(openshot::ClipBase* new_clip); /// Close the reader (and any resources it was consuming) virtual void Close() = 0; diff --git a/include/Timeline.h b/include/Timeline.h index 932b04acf..21cf0bf6a 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -54,6 +54,8 @@ #include "OpenMPUtilities.h" #include "ReaderBase.h" #include "Settings.h" +#include "TimelineBase.h" + namespace openshot { @@ -146,7 +148,7 @@ namespace openshot { * t.Close(); * @endcode */ - class Timeline : public ReaderBase { + class Timeline : public TimelineBase, public ReaderBase { private: bool is_open; /// + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_TIMELINE_BASE_H +#define OPENSHOT_TIMELINE_BASE_H + + +namespace openshot { + /** + * @brief This class represents a timeline (used for building generic timeline implementations) + */ + class TimelineBase { + + public: + int preview_width; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + int preview_height; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + }; +} + +#endif diff --git a/include/effects/Bars.h b/include/effects/Bars.h index 7c92255aa..a781a5205 100644 --- a/include/effects/Bars.h +++ b/include/effects/Bars.h @@ -77,16 +77,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) Bars(Color color, Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Blur.h b/include/effects/Blur.h index 60a0cd085..a3211e76e 100644 --- a/include/effects/Blur.h +++ b/include/effects/Blur.h @@ -89,16 +89,24 @@ namespace openshot /// @param new_iterations The curve to adjust the # of iterations (between 1 and 100) Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h index 5f25b94a0..5e36671f0 100644 --- a/include/effects/Brightness.h +++ b/include/effects/Brightness.h @@ -77,16 +77,24 @@ namespace openshot /// @param new_contrast The curve to adjust the contrast (3 is typical, 20 is a lot, 100 is max. 0 is invalid) Brightness(Keyframe new_brightness, Keyframe new_contrast); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/ChromaKey.h b/include/effects/ChromaKey.h index fcc8c3b1f..a59e582dc 100644 --- a/include/effects/ChromaKey.h +++ b/include/effects/ChromaKey.h @@ -74,16 +74,24 @@ namespace openshot /// @param fuzz The fuzz factor (or threshold) ChromaKey(Color color, Keyframe fuzz); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/ColorShift.h b/include/effects/ColorShift.h index 4ef56dc68..b6c596a9d 100644 --- a/include/effects/ColorShift.h +++ b/include/effects/ColorShift.h @@ -81,16 +81,24 @@ namespace openshot /// @param alpha_y The curve to adjust the Alpha y shift (between -1 and 1, percentage) ColorShift(Keyframe red_x, Keyframe red_y, Keyframe green_x, Keyframe green_y, Keyframe blue_x, Keyframe blue_y, Keyframe alpha_x, Keyframe alpha_y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Crop.h b/include/effects/Crop.h index f5ba07f24..ccdba3f1f 100644 --- a/include/effects/Crop.h +++ b/include/effects/Crop.h @@ -76,16 +76,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) Crop(Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Deinterlace.h b/include/effects/Deinterlace.h index 97c778538..83a9f2b2a 100644 --- a/include/effects/Deinterlace.h +++ b/include/effects/Deinterlace.h @@ -70,16 +70,24 @@ namespace openshot /// Default constructor Deinterlace(bool isOdd); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Hue.h b/include/effects/Hue.h index 833bf087b..e561bbf77 100644 --- a/include/effects/Hue.h +++ b/include/effects/Hue.h @@ -67,16 +67,24 @@ namespace openshot /// @param hue The curve to adjust the hue shift (between 0 and 1) Hue(Keyframe hue); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Mask.h b/include/effects/Mask.h index 8156b8436..910f1308d 100644 --- a/include/effects/Mask.h +++ b/include/effects/Mask.h @@ -89,16 +89,24 @@ namespace openshot /// @param mask_contrast The curve to adjust the contrast of the wipe's mask (3 is typical, 20 is a lot, 0 is invalid) Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Negate.h b/include/effects/Negate.h index c691a86c1..cab98f0a8 100644 --- a/include/effects/Negate.h +++ b/include/effects/Negate.h @@ -58,16 +58,24 @@ namespace openshot /// Default constructor Negate(); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Pixelate.h b/include/effects/Pixelate.h index 4cdd440f2..50f9b6737 100644 --- a/include/effects/Pixelate.h +++ b/include/effects/Pixelate.h @@ -76,16 +76,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom margin size (between 0 and 1) Pixelate(Keyframe pixelization, Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index f272305f9..127f68294 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -74,16 +74,24 @@ namespace openshot /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = black and white, 1.0 = normal, 2.0 = double saturation) Saturation(Keyframe new_saturation); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Shift.h b/include/effects/Shift.h index 7a7efbea4..b2c3242d5 100644 --- a/include/effects/Shift.h +++ b/include/effects/Shift.h @@ -70,16 +70,24 @@ namespace openshot /// @param y The curve to adjust the y shift (between -1 and 1, percentage) Shift(Keyframe x, Keyframe y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Wave.h b/include/effects/Wave.h index d4759c24e..3b922eb3d 100644 --- a/include/effects/Wave.h +++ b/include/effects/Wave.h @@ -76,16 +76,24 @@ namespace openshot /// @param speed_y The curve to adjust the vertical speed (0 to 10) Wave(Keyframe wavelength, Keyframe amplitude, Keyframe multiplier, Keyframe shift_x, Keyframe speed_y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dec7c8535..e1dfc36ad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -176,6 +176,7 @@ set(OPENSHOT_SOURCES QtPlayer.cpp QtTextReader.cpp Settings.cpp + TimelineBase.cpp Timeline.cpp) # Video effects diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp index c194ce33a..fb45e8f6d 100644 --- a/src/ChunkReader.cpp +++ b/src/ChunkReader.cpp @@ -222,7 +222,6 @@ std::shared_ptr ChunkReader::GetFrame(int64_t requested_frame) // Close existing reader (if needed) if (local_reader) { - std::cout << "Close READER" << std::endl; // Close and delete old reader local_reader->Close(); delete local_reader; @@ -230,7 +229,6 @@ std::shared_ptr ChunkReader::GetFrame(int64_t requested_frame) try { - std::cout << "Load READER: " << chunk_video_path << std::endl; // Load new FFmpegReader local_reader = new FFmpegReader(chunk_video_path); local_reader->Open(); // open reader diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp index f9d653b0c..b6256eaf0 100644 --- a/src/ChunkWriter.cpp +++ b/src/ChunkWriter.cpp @@ -157,10 +157,6 @@ void ChunkWriter::WriteFrame(std::shared_ptr frame) // Write the frames once it reaches the correct chunk size if (frame_count % chunk_size == 0 && frame_count >= chunk_size) { - std::cout << "Done with chunk" << std::endl; - std::cout << "frame_count: " << frame_count << std::endl; - std::cout << "chunk_size: " << chunk_size << std::endl; - // Pad an additional 12 frames for (int z = 0; z<12; z++) { @@ -229,10 +225,6 @@ void ChunkWriter::Close() // Write the frames once it reaches the correct chunk size if (is_writing) { - std::cout << "Final chunk" << std::endl; - std::cout << "frame_count: " << frame_count << std::endl; - std::cout << "chunk_size: " << chunk_size << std::endl; - // Pad an additional 12 frames for (int z = 0; z<12; z++) { diff --git a/src/Clip.cpp b/src/Clip.cpp index 7a504cc9d..91510576b 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -79,13 +79,6 @@ void Clip::init_settings() // Init audio waveform color wave_color = Color((unsigned char)0, (unsigned char)123, (unsigned char)255, (unsigned char)255); - // Init crop settings - crop_gravity = GRAVITY_TOP_LEFT; - crop_width = Keyframe(1.0); - crop_height = Keyframe(1.0); - crop_x = Keyframe(0.0); - crop_y = Keyframe(0.0); - // Init shear and perspective curves shear_x = Keyframe(0.0); shear_y = Keyframe(0.0); @@ -109,7 +102,7 @@ void Clip::init_settings() has_video = Keyframe(-1.0); // Initialize Clip cache - final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); } // Init reader's rotation (if any) @@ -156,7 +149,7 @@ Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), alloca // Update duration and set parent if (reader) { End(reader->info.duration); - reader->SetClip(this); + reader->ParentClip(this); } } @@ -177,7 +170,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Open common video format - reader = new FFmpegReader(path); + reader = new openshot::FFmpegReader(path); } catch(...) { } } @@ -186,7 +179,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Open common video format - reader = new Timeline(path, true); + reader = new openshot::Timeline(path, true); } catch(...) { } } @@ -198,13 +191,13 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Try an image reader - reader = new QtImageReader(path); + reader = new openshot::QtImageReader(path); } catch(...) { try { // Try a video reader - reader = new FFmpegReader(path); + reader = new openshot::FFmpegReader(path); } catch(...) { } } @@ -213,7 +206,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N // Update duration and set parent if (reader) { End(reader->info.duration); - reader->SetClip(this); + reader->ParentClip(this); allocated_reader = reader; init_reader_rotation(); } @@ -242,7 +235,7 @@ void Clip::Reader(ReaderBase* new_reader) reader = new_reader; // set parent - reader->SetClip(this); + reader->ParentClip(this); // Init rotation (if any) init_reader_rotation(); @@ -316,8 +309,8 @@ float Clip::End() const return end; } -// Get an openshot::Frame object for a specific frame number of this reader. -std::shared_ptr Clip::GetFrame(int64_t requested_frame) +// Create an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr Clip::GetFrame(int64_t frame_number) { // Check for open reader (or throw exception) if (!is_open) @@ -326,25 +319,19 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) if (reader) { // Adjust out of bounds frame number - requested_frame = adjust_frame_number_minimum(requested_frame); - - // Is a time map detected - int64_t new_frame_number = requested_frame; - int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); - if (time.GetLength() > 1) - new_frame_number = time_mapped_number; + frame_number = adjust_frame_number_minimum(frame_number); - // Get the # of audio samples from the time mapped Frame instance - std::shared_ptr time_mapped_original_frame = GetOrCreateFrame(new_frame_number); - return GetFrame(requested_frame, reader->info.width, reader->info.height, time_mapped_original_frame->GetAudioSamplesCount()); + // Get the original frame and pass it to GetFrame overload + std::shared_ptr original_frame = GetOrCreateFrame(frame_number); + return GetFrame(original_frame, frame_number); } else // Throw error if reader not initialized throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method."); } -// Get an openshot::Frame object for a specific frame number of this reader. -std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int height, int samples) +// Use an existing openshot::Frame object and draw this Clip's frame onto it +std::shared_ptr Clip::GetFrame(std::shared_ptr frame, int64_t frame_number) { // Check for open reader (or throw exception) if (!is_open) @@ -353,33 +340,33 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he if (reader) { // Adjust out of bounds frame number - requested_frame = adjust_frame_number_minimum(requested_frame); + frame_number = adjust_frame_number_minimum(frame_number); // Check the cache for this frame - std::shared_ptr cached_frame = final_cache.GetFrame(requested_frame); + std::shared_ptr cached_frame = cache.GetFrame(frame_number); if (cached_frame) { // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", requested_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", frame_number); // Return the cached frame return cached_frame; } // Adjust has_video and has_audio overrides - int enabled_audio = has_audio.GetInt(requested_frame); + int enabled_audio = has_audio.GetInt(frame_number); if (enabled_audio == -1 && reader && reader->info.has_audio) enabled_audio = 1; else if (enabled_audio == -1 && reader && !reader->info.has_audio) enabled_audio = 0; - int enabled_video = has_video.GetInt(requested_frame); + int enabled_video = has_video.GetInt(frame_number); if (enabled_video == -1 && reader && reader->info.has_video) enabled_video = 1; else if (enabled_video == -1 && reader && !reader->info.has_audio) enabled_video = 0; // Is a time map detected - int64_t new_frame_number = requested_frame; - int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); + int64_t new_frame_number = frame_number; + int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(frame_number)); if (time.GetLength() > 1) new_frame_number = time_mapped_number; @@ -387,13 +374,6 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he std::shared_ptr original_frame; original_frame = GetOrCreateFrame(new_frame_number); - // Create a new frame - std::shared_ptr frame(new Frame(new_frame_number, 1, 1, "#000000", original_frame->GetAudioSamplesCount(), original_frame->GetAudioChannelsCount())); - { - frame->SampleRate(original_frame->SampleRate()); - frame->ChannelsLayout(original_frame->ChannelsLayout()); - } - // Copy the image from the odd field if (enabled_video) frame->AddImage(std::shared_ptr(new QImage(*original_frame->GetImage()))); @@ -404,20 +384,29 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he frame->AddAudio(true, channel, 0, original_frame->GetAudioSamples(channel), original_frame->GetAudioSamplesCount(), 1.0); // Get time mapped frame number (used to increase speed, change direction, etc...) - // TODO: Handle variable # of samples, since this resamples audio for different speeds (only when time curve is set) - get_time_mapped_frame(frame, requested_frame); - - // Adjust # of samples to match requested (the interaction with time curves will make this tricky) - // TODO: Implement move samples to/from next frame + get_time_mapped_frame(frame, frame_number); // Apply effects to the frame (if any) apply_effects(frame); + // Determine size of image (from Timeline or Reader) + int width = 0; + int height = 0; + if (timeline) { + // Use timeline size (if available) + width = timeline->preview_width; + height = timeline->preview_height; + } else { + // Fallback to clip size + width = reader->info.width; + height = reader->info.height; + } + // Apply keyframe / transforms apply_keyframes(frame, width, height); // Cache frame - final_cache.Add(frame); + cache.Add(frame); // Return processed 'frame' return frame; @@ -677,17 +666,26 @@ int64_t Clip::adjust_frame_number_minimum(int64_t frame_number) // Get or generate a blank frame std::shared_ptr Clip::GetOrCreateFrame(int64_t number) { - std::shared_ptr new_frame; try { // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number); // Attempt to get a frame (but this could fail if a reader has just been closed) - new_frame = reader->GetFrame(number); + std::shared_ptr reader_frame = reader->GetFrame(number); // Return real frame - if (new_frame) - return new_frame; + if (reader_frame) { + // Create a new copy of reader frame + // This allows a clip to modify the pixels and audio of this frame without + // changing the underlying reader's frame data + //std::shared_ptr reader_copy(new Frame(number, 1, 1, "#000000", reader_frame->GetAudioSamplesCount(), reader_frame->GetAudioChannelsCount())); + std::shared_ptr reader_copy(new Frame(*reader_frame.get())); + { + reader_copy->SampleRate(reader_frame->SampleRate()); + reader_copy->ChannelsLayout(reader_frame->ChannelsLayout()); + } + return reader_copy; + } } catch (const ReaderClosed & e) { // ... @@ -704,7 +702,7 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number) ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "estimated_samples_in_frame", estimated_samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); + std::shared_ptr new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); new_frame->SampleRate(reader->info.sample_rate); new_frame->ChannelsLayout(reader->info.channel_layout); new_frame->AddAudioSilence(estimated_samples_in_frame); @@ -793,11 +791,6 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) const { root["has_video"]["choices"].append(add_property_choice_json("Off", 0, has_video.GetValue(requested_frame))); root["has_video"]["choices"].append(add_property_choice_json("On", 1, has_video.GetValue(requested_frame))); - root["crop_x"] = add_property_json("Crop X", crop_x.GetValue(requested_frame), "float", "", &crop_x, -1.0, 1.0, false, requested_frame); - root["crop_y"] = add_property_json("Crop Y", crop_y.GetValue(requested_frame), "float", "", &crop_y, -1.0, 1.0, false, requested_frame); - root["crop_width"] = add_property_json("Crop Width", crop_width.GetValue(requested_frame), "float", "", &crop_width, 0.0, 1.0, false, requested_frame); - root["crop_height"] = add_property_json("Crop Height", crop_height.GetValue(requested_frame), "float", "", &crop_height, 0.0, 1.0, false, requested_frame); - root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", &wave_color.red, 0, 255, false, requested_frame); root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", &wave_color.red, 0, 255, false, requested_frame); root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", &wave_color.blue, 0, 255, false, requested_frame); @@ -828,10 +821,6 @@ Json::Value Clip::JsonValue() const { root["time"] = time.JsonValue(); root["volume"] = volume.JsonValue(); root["wave_color"] = wave_color.JsonValue(); - root["crop_width"] = crop_width.JsonValue(); - root["crop_height"] = crop_height.JsonValue(); - root["crop_x"] = crop_x.JsonValue(); - root["crop_y"] = crop_y.JsonValue(); root["shear_x"] = shear_x.JsonValue(); root["shear_y"] = shear_y.JsonValue(); root["origin_x"] = origin_x.JsonValue(); @@ -891,7 +880,7 @@ void Clip::SetJsonValue(const Json::Value root) { ClipBase::SetJsonValue(root); // Clear cache - final_cache.Clear(); + cache.Clear(); // Set data from Json (if key is found) if (!root["gravity"].isNull()) @@ -924,14 +913,6 @@ void Clip::SetJsonValue(const Json::Value root) { volume.SetJsonValue(root["volume"]); if (!root["wave_color"].isNull()) wave_color.SetJsonValue(root["wave_color"]); - if (!root["crop_width"].isNull()) - crop_width.SetJsonValue(root["crop_width"]); - if (!root["crop_height"].isNull()) - crop_height.SetJsonValue(root["crop_height"]); - if (!root["crop_x"].isNull()) - crop_x.SetJsonValue(root["crop_x"]); - if (!root["crop_y"].isNull()) - crop_y.SetJsonValue(root["crop_y"]); if (!root["shear_x"].isNull()) shear_x.SetJsonValue(root["shear_x"]); if (!root["shear_y"].isNull()) @@ -1010,13 +991,13 @@ void Clip::SetJsonValue(const Json::Value root) { if (type == "FFmpegReader") { // Create new reader - reader = new FFmpegReader(root["reader"]["path"].asString(), false); + reader = new openshot::FFmpegReader(root["reader"]["path"].asString(), false); reader->SetJsonValue(root["reader"]); } else if (type == "QtImageReader") { // Create new reader - reader = new QtImageReader(root["reader"]["path"].asString(), false); + reader = new openshot::QtImageReader(root["reader"]["path"].asString(), false); reader->SetJsonValue(root["reader"]); #ifdef USE_IMAGEMAGICK @@ -1036,25 +1017,25 @@ void Clip::SetJsonValue(const Json::Value root) { } else if (type == "ChunkReader") { // Create new reader - reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt()); + reader = new openshot::ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt()); reader->SetJsonValue(root["reader"]); } else if (type == "DummyReader") { // Create new reader - reader = new DummyReader(); + reader = new openshot::DummyReader(); reader->SetJsonValue(root["reader"]); } else if (type == "Timeline") { // Create new reader (always load from file again) // This prevents FrameMappers from being loaded on accident - reader = new Timeline(root["reader"]["path"].asString(), true); + reader = new openshot::Timeline(root["reader"]["path"].asString(), true); } // mark as managed reader and set parent if (reader) { - reader->SetClip(this); + reader->ParentClip(this); allocated_reader = reader; } @@ -1076,6 +1057,9 @@ void Clip::sort_effects() // Add an effect to the clip void Clip::AddEffect(EffectBase* effect) { + // Set parent clip pointer + effect->ParentClip(this); + // Add effect to list effects.push_back(effect); @@ -1083,7 +1067,7 @@ void Clip::AddEffect(EffectBase* effect) sort_effects(); // Clear cache - final_cache.Clear(); + cache.Clear(); } // Remove an effect from the clip @@ -1092,7 +1076,7 @@ void Clip::RemoveEffect(EffectBase* effect) effects.remove(effect); // Clear cache - final_cache.Clear(); + cache.Clear(); } // Apply effects to the source frame (if any) @@ -1165,7 +1149,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width, height, Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_STRETCH): { @@ -1173,7 +1157,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width, height, Qt::IgnoreAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_CROP): { @@ -1187,7 +1171,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_NONE): { @@ -1199,50 +1183,11 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width * source_width_ratio, height * source_height_ratio, Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } } - float crop_x_value = crop_x.GetValue(frame->number); - float crop_y_value = crop_y.GetValue(frame->number); - float crop_w_value = crop_width.GetValue(frame->number); - float crop_h_value = crop_height.GetValue(frame->number); - switch(crop_gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - crop_x_value += 0.5; - break; - case (GRAVITY_TOP_RIGHT): - crop_x_value += 1.0; - break; - case (GRAVITY_LEFT): - crop_y_value += 0.5; - break; - case (GRAVITY_CENTER): - crop_x_value += 0.5; - crop_y_value += 0.5; - break; - case (GRAVITY_RIGHT): - crop_x_value += 1.0; - crop_y_value += 0.5; - break; - case (GRAVITY_BOTTOM_LEFT): - crop_y_value += 1.0; - break; - case (GRAVITY_BOTTOM): - crop_x_value += 0.5; - crop_y_value += 1.0; - break; - case (GRAVITY_BOTTOM_RIGHT): - crop_x_value += 1.0; - crop_y_value += 1.0; - break; - } - /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ float x = 0.0; // left float y = 0.0; // top @@ -1289,7 +1234,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) } // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); /* LOCATION, ROTATION, AND SCALE */ float r = rotation.GetValue(frame->number); // rotate in degrees @@ -1304,7 +1249,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) QTransform transform; // Transform source image (if needed) - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); if (!isEqual(x, 0) || !isEqual(y, 0)) { // TRANSLATE/MOVE CLIP @@ -1333,7 +1278,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) } // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; @@ -1352,31 +1297,34 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.drawImage(0, 0, *source_image); - // Draw frame #'s on top of image (if needed) - if (display != FRAME_DISPLAY_NONE) { - std::stringstream frame_number_str; - switch (display) - { - case (FRAME_DISPLAY_NONE): - // This is only here to prevent unused-enum warnings - break; + if (timeline) { + Timeline *t = (Timeline *) timeline; - case (FRAME_DISPLAY_CLIP): - frame_number_str << frame->number; - break; + // Draw frame #'s on top of image (if needed) + if (display != FRAME_DISPLAY_NONE) { + std::stringstream frame_number_str; + switch (display) { + case (FRAME_DISPLAY_NONE): + // This is only here to prevent unused-enum warnings + break; - case (FRAME_DISPLAY_TIMELINE): - frame_number_str << "N/A"; - break; + case (FRAME_DISPLAY_CLIP): + frame_number_str << frame->number; + break; - case (FRAME_DISPLAY_BOTH): - frame_number_str << "N/A" << " (" << frame->number << ")"; - break; - } + case (FRAME_DISPLAY_TIMELINE): + frame_number_str << (position * t->info.fps.ToFloat()) + frame->number; + break; - // Draw frame number on top of image - painter.setPen(QColor("#ffffff")); - painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + case (FRAME_DISPLAY_BOTH): + frame_number_str << (position * t->info.fps.ToFloat()) + frame->number << " (" << frame->number << ")"; + break; + } + + // Draw frame number on top of image + painter.setPen(QColor("#ffffff")); + painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + } } painter.end(); diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index 05ed97c2a..fcf006451 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -138,3 +138,13 @@ Json::Value EffectBase::JsonInfo() const { // return JsonValue return root; } + +/// Parent clip object of this reader (which can be unparented and NULL) +openshot::ClipBase* EffectBase::ParentClip() { + return clip; +} + +/// Set parent clip object of this reader +void EffectBase::ParentClip(openshot::ClipBase* new_clip) { + clip = new_clip; +} \ No newline at end of file diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index c8ce141fe..aa116e3a2 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1286,15 +1286,16 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { // without losing quality. NOTE: We cannot go smaller than the timeline itself, or the add_layer timeline // method will scale it back to timeline size before scaling it smaller again. This needs to be fixed in // the future. - int max_width = openshot::Settings::Instance()->MAX_WIDTH; - if (max_width <= 0) - max_width = info.width; - int max_height = openshot::Settings::Instance()->MAX_HEIGHT; - if (max_height <= 0) - max_height = info.height; - - Clip *parent = (Clip *) GetClip(); + int max_width = info.width; + int max_height = info.height; + + Clip *parent = (Clip *) ParentClip(); if (parent) { + if (parent->ParentTimeline()) { + // Set max width/height based on parent clip's timeline (if attached to a timeline) + max_width = parent->ParentTimeline()->preview_width; + max_height = parent->ParentTimeline()->preview_height; + } if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) { // Best fit or Stretch scaling (based on max timeline size * scaling keyframes) float max_scale_x = parent->scale_x.GetMaxPoint().co.Y; diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp index e5f0e11d0..54813b377 100644 --- a/src/Qt/PlayerDemo.cpp +++ b/src/Qt/PlayerDemo.cpp @@ -105,7 +105,6 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) } else if (event->key() == Qt::Key_J) { - std::cout << "BACKWARD" << player->Speed() - 1 << std::endl; if (player->Speed() - 1 != 0) player->Speed(player->Speed() - 1); else @@ -115,7 +114,6 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) player->Play(); } else if (event->key() == Qt::Key_L) { - std::cout << "FORWARD" << player->Speed() + 1 << std::endl; if (player->Speed() + 1 != 0) player->Speed(player->Speed() + 1); else @@ -126,19 +124,16 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) } else if (event->key() == Qt::Key_Left) { - std::cout << "FRAME STEP -1" << std::endl; if (player->Speed() != 0) player->Speed(0); player->Seek(player->Position() - 1); } else if (event->key() == Qt::Key_Right) { - std::cout << "FRAME STEP +1" << std::endl; if (player->Speed() != 0) player->Speed(0); player->Seek(player->Position() + 1); } else if (event->key() == Qt::Key_Escape) { - std::cout << "QUIT PLAYER" << std::endl; QWidget *pWin = QApplication::activeWindow(); pWin->hide(); diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index cf64ef930..725d88180 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -180,15 +180,16 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) // without losing quality. NOTE: We cannot go smaller than the timeline itself, or the add_layer timeline // method will scale it back to timeline size before scaling it smaller again. This needs to be fixed in // the future. - int max_width = Settings::Instance()->MAX_WIDTH; - if (max_width <= 0) - max_width = info.width; - int max_height = Settings::Instance()->MAX_HEIGHT; - if (max_height <= 0) - max_height = info.height; - - Clip* parent = (Clip*) GetClip(); + int max_width = info.width; + int max_height = info.height; + + Clip* parent = (Clip*) ParentClip(); if (parent) { + if (parent->ParentTimeline()) { + // Set max width/height based on parent clip's timeline (if attached to a timeline) + max_width = parent->ParentTimeline()->preview_width; + max_height = parent->ParentTimeline()->preview_height; + } if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) { // Best fit or Stretch scaling (based on max timeline size * scaling keyframes) float max_scale_x = parent->scale_x.GetMaxPoint().co.Y; diff --git a/src/ReaderBase.cpp b/src/ReaderBase.cpp index 474dc624e..653756c51 100644 --- a/src/ReaderBase.cpp +++ b/src/ReaderBase.cpp @@ -63,7 +63,7 @@ ReaderBase::ReaderBase() info.audio_timebase = Fraction(); // Init parent clip - parent = NULL; + clip = NULL; } // Display file information @@ -251,11 +251,11 @@ void ReaderBase::SetJsonValue(const Json::Value root) { } /// Parent clip object of this reader (which can be unparented and NULL) -openshot::ClipBase* ReaderBase::GetClip() { - return parent; +openshot::ClipBase* ReaderBase::ParentClip() { + return clip; } /// Set parent clip object of this reader -void ReaderBase::SetClip(openshot::ClipBase* clip) { - parent = clip; +void ReaderBase::ParentClip(openshot::ClipBase* new_clip) { + clip = new_clip; } diff --git a/src/Settings.cpp b/src/Settings.cpp index e48fd981c..d946d2277 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -45,8 +45,6 @@ Settings *Settings::Instance() m_pInstance = new Settings; m_pInstance->HARDWARE_DECODER = 0; m_pInstance->HIGH_QUALITY_SCALING = false; - m_pInstance->MAX_WIDTH = 0; - m_pInstance->MAX_HEIGHT = 0; m_pInstance->WAIT_FOR_VIDEO_PROCESSING_TASK = false; m_pInstance->OMP_THREADS = 12; m_pInstance->FF_THREADS = 8; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index d9b23ca5f..8c1429b2e 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -52,6 +52,8 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha // Init FileInfo struct (clear all values) info.width = width; info.height = height; + preview_width = info.width; + preview_height = info.height; info.fps = fps; info.sample_rate = sample_rate; info.channels = channels; @@ -229,6 +231,9 @@ Timeline::~Timeline() { // Add an openshot::Clip to the timeline void Timeline::AddClip(Clip* clip) { + // Assign timeline to clip + clip->ParentTimeline(this); + // All clips should be converted to the frame rate of this timeline if (auto_map_clips) // Apply framemapper (or update existing framemapper) @@ -244,6 +249,9 @@ void Timeline::AddClip(Clip* clip) // Add an effect to the timeline void Timeline::AddEffect(EffectBase* effect) { + // Assign timeline to effect + effect->ParentTimeline(this); + // Add effect to list effects.push_back(effect); @@ -368,7 +376,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame)); + new_frame = std::shared_ptr(clip->GetFrame(number)); // Return real frame return new_frame; @@ -385,7 +393,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetOrCreateFrame (create blank)", "number", number, "samples_in_frame", samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, "#000000", samples_in_frame, info.channels); + new_frame = std::make_shared(number, preview_width, preview_height, "#000000", samples_in_frame, info.channels); #pragma omp critical (T_GetOtCreateFrame) { new_frame->SampleRate(info.sample_rate); @@ -487,29 +495,8 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Skip the rest of the image processing for performance reasons return; - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Get Source Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); - - // Get actual frame image data - source_image = source_frame->GetImage(); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); - - /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ - std::shared_ptr new_image; - new_image = new_frame->GetImage(); - - // Load timeline's new frame image into a QPainter - QPainter painter(new_image.get()); - - // Composite a new layer onto the image - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); - painter.end(); - // Add new QImage to frame - new_frame->AddImage(new_image); + new_frame->AddImage(source_frame->GetImage()); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); @@ -668,10 +655,9 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Get clip frame # long clip_start_frame = (clip->Start() * info.fps.ToDouble()) + 1; long clip_frame_number = frame_number - clip_start_position + clip_start_frame; - int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Cache clip object - clip->GetFrame(clip_frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame); + clip->GetFrame(clip_frame_number); } } } @@ -689,7 +675,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Create blank frame (which will become the requested frame) - std::shared_ptr new_frame(std::make_shared(frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, "#000000", samples_in_frame, info.channels)); + std::shared_ptr new_frame(std::make_shared(frame_number, preview_width, preview_height, "#000000", samples_in_frame, info.channels)); #pragma omp critical (T_GetFrame) { new_frame->AddAudioSilence(samples_in_frame); @@ -703,7 +689,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Add Background Color to 1st layer (if animated or not black) if ((color.red.GetCount() > 1 || color.green.GetCount() > 1 || color.blue.GetCount() > 1) || (color.red.GetValue(frame_number) != 0.0 || color.green.GetValue(frame_number) != 0.0 || color.blue.GetValue(frame_number) != 0.0)) - new_frame->AddColor(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, color.GetColorHex(frame_number)); + new_frame->AddColor(preview_width, preview_height, color.GetColorHex(frame_number)); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size()); @@ -967,6 +953,10 @@ void Timeline::SetJsonValue(const Json::Value root) { info.video_length = info.fps.ToFloat() * info.duration; } + // Update preview settings + preview_width = info.width; + preview_height = info.height; + // Re-open if needed if (was_open) Open(); @@ -1195,6 +1185,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef // Add Effect to Timeline AddEffect(e); + + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) e->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } } } else if (change_type == "update") { @@ -1207,6 +1203,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef int64_t old_ending_frame = ((existing_effect->Position() + existing_effect->Duration()) * info.fps.ToDouble()) + 1; final_cache->Remove(old_starting_frame - 8, old_ending_frame + 8); + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) existing_effect->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } + // Update effect properties from JSON existing_effect->SetJsonValue(change["value"]); } @@ -1221,6 +1223,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef int64_t old_ending_frame = ((existing_effect->Position() + existing_effect->Duration()) * info.fps.ToDouble()) + 1; final_cache->Remove(old_starting_frame - 8, old_ending_frame + 8); + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) existing_effect->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } + // Remove effect from timeline RemoveEffect(existing_effect); } @@ -1239,7 +1247,7 @@ void Timeline::apply_json_to_timeline(Json::Value change) { sub_key = change["key"][(uint)1].asString(); // Clear entire cache - final_cache->Clear(); + ClearAllCache(); // Determine type of change operation if (change_type == "insert" || change_type == "update") { @@ -1263,12 +1271,16 @@ void Timeline::apply_json_to_timeline(Json::Value change) { info.duration = change["value"].asDouble(); info.video_length = info.fps.ToFloat() * info.duration; } - else if (root_key == "width") + else if (root_key == "width") { // Set width info.width = change["value"].asInt(); - else if (root_key == "height") + preview_width = info.width; + } + else if (root_key == "height") { // Set height info.height = change["value"].asInt(); + preview_height = info.height; + } else if (root_key == "fps" && sub_key == "" && change["value"].isObject()) { // Set fps fraction if (!change["value"]["num"].isNull()) @@ -1360,6 +1372,7 @@ void Timeline::ClearAllCache() { for (auto clip : clips) { // Clear cache on clip + clip->GetCache()->Clear(); clip->Reader()->GetCache()->Clear(); // Clear nested Reader (if any) @@ -1382,7 +1395,7 @@ void Timeline::SetMaxSize(int width, int height) { // Scale QSize up to proposed size display_ratio_size.scale(proposed_size, Qt::KeepAspectRatio); - // Set max size - Settings::Instance()->MAX_WIDTH = display_ratio_size.width(); - Settings::Instance()->MAX_HEIGHT = display_ratio_size.height(); + // Update preview settings + preview_width = display_ratio_size.width(); + preview_height = display_ratio_size.height(); } diff --git a/src/TimelineBase.cpp b/src/TimelineBase.cpp new file mode 100644 index 000000000..f75e1ddb9 --- /dev/null +++ b/src/TimelineBase.cpp @@ -0,0 +1,33 @@ +/** + * @file + * @brief Source file for Timeline class + * @author Jonathan Thomas + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/TimelineBase.h" + +using namespace openshot; diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index 53e514c15..b5be39c4c 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -92,6 +92,7 @@ #include "KeyFrame.h" #include "RendererBase.h" #include "Settings.h" +#include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" #include "AudioDeviceInfo.h" @@ -203,6 +204,7 @@ %include "KeyFrame.h" %include "RendererBase.h" %include "Settings.h" +%include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" %include "AudioDeviceInfo.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 2f24d2200..d36990dd5 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -103,6 +103,7 @@ namespace std { #include "KeyFrame.h" #include "RendererBase.h" #include "Settings.h" +#include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" #include "AudioDeviceInfo.h" @@ -192,6 +193,7 @@ namespace std { %include "KeyFrame.h" %include "RendererBase.h" %include "Settings.h" +%include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" %include "AudioDeviceInfo.h" From 206578df3fd31ddd2122e99a6d3cde6ff546456c Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 5 Oct 2020 23:08:31 -0500 Subject: [PATCH 35/86] Fixing some regressions on image merging --- include/ClipBase.h | 10 +++++++++- include/TimelineBase.h | 3 +++ src/Timeline.cpp | 23 ++++++++++++++++++++++- src/TimelineBase.cpp | 8 ++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/ClipBase.h b/include/ClipBase.h index e335c501b..11d2271fa 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -69,7 +69,15 @@ namespace openshot { CacheMemory cache; /// Constructor for the base clip - ClipBase() { }; + ClipBase() { + // Initialize values + position = 0.0; + layer = 0; + start = 0.0; + end = 0.0; + previous_properties = ""; + timeline = NULL; + }; // Compare a clip using the Position() property bool operator< ( ClipBase& a) { return (Position() < a.Position()); } diff --git a/include/TimelineBase.h b/include/TimelineBase.h index d065a6de1..af6a65a20 100644 --- a/include/TimelineBase.h +++ b/include/TimelineBase.h @@ -41,6 +41,9 @@ namespace openshot { public: int preview_width; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. int preview_height; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + + /// Constructor for the base timeline + TimelineBase(); }; } diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 8c1429b2e..f75becaf0 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -495,8 +495,29 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Skip the rest of the image processing for performance reasons return; + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Get Source Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); + + // Get actual frame image data + source_image = source_frame->GetImage(); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); + + /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ + std::shared_ptr new_image; + new_image = new_frame->GetImage(); + + // Load timeline's new frame image into a QPainter + QPainter painter(new_image.get()); + + // Composite a new layer onto the image + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); + painter.end(); + // Add new QImage to frame - new_frame->AddImage(source_frame->GetImage()); + new_frame->AddImage(new_image); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); diff --git a/src/TimelineBase.cpp b/src/TimelineBase.cpp index f75e1ddb9..cc59a2433 100644 --- a/src/TimelineBase.cpp +++ b/src/TimelineBase.cpp @@ -31,3 +31,11 @@ #include "../include/TimelineBase.h" using namespace openshot; + +/// Constructor for the base timeline +TimelineBase::TimelineBase() +{ + // Init preview size (default) + preview_width = 1920; + preview_height = 1080; +} From 9eb859fa2c1c0c26b28bb5e32fc20ac30999feb8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Thu, 8 Oct 2020 14:44:01 -0500 Subject: [PATCH 36/86] Initialize parent clip variable --- src/EffectBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index fcf006451..fb7b22697 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -41,6 +41,7 @@ void EffectBase::InitEffectInfo() Start(0.0); End(0.0); Order(0); + ParentClip(NULL); info.has_video = false; info.has_audio = false; From 58cedb612c0c56ae76b58a574231b470590873c8 Mon Sep 17 00:00:00 2001 From: Brenno Date: Sat, 10 Oct 2020 17:01:24 -0300 Subject: [PATCH 37/86] Implemented position remapper inside FrameMapper to fix audio noise when exporting to different fps The FrameMapper class now receives the updated clip position and returns the correct amount of samples for a given frame number --- include/FrameMapper.h | 8 +- src/FrameMapper.cpp | 39 +++++-- src/Timeline.cpp | 10 +- tests/FrameMapper_Tests.cpp | 197 ++++++++++++++++++++++++++++++++++-- 4 files changed, 229 insertions(+), 25 deletions(-) diff --git a/include/FrameMapper.h b/include/FrameMapper.h index e78401a9b..85c933d25 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -147,6 +147,9 @@ namespace openshot bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping SWRCONTEXT *avr; // Audio resampling context object + float position; + float start; + // Internal methods used by init void AddField(int64_t frame); void AddField(Field field); @@ -166,13 +169,13 @@ namespace openshot std::vector frames; // List of all frames /// Default constructor for openshot::FrameMapper class - FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); + FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); /// Destructor virtual ~FrameMapper(); /// Change frame rate or audio mapping details - void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); + void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); /// Close the openshot::FrameMapper and internal reader void Close() override; @@ -218,6 +221,7 @@ namespace openshot /// Resample audio and map channels (if needed) void ResampleMappedAudio(std::shared_ptr frame, int64_t original_frame_number); + int64_t ConvPositon(int64_t clip_frame_number); }; } diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index 4c561f8fe..d05cf7f66 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -33,7 +33,7 @@ using namespace std; using namespace openshot; -FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) : +FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) : reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL) { // Set the original frame rate from the reader @@ -52,6 +52,8 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe info.width = reader->info.width; info.height = reader->info.height; + position = clipPosition; + start = clipStart; // Used to toggle odd / even fields field_toggle = true; @@ -257,12 +259,12 @@ void FrameMapper::Init() // the original sample rate. int64_t end_samples_frame = start_samples_frame; int end_samples_position = start_samples_position; - int remaining_samples = Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels); + int remaining_samples = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels); while (remaining_samples > 0) { // get original samples - int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position; + int original_samples = Frame::GetSamplesPerFrame(ConvPositon(end_samples_frame), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; // Enough samples if (original_samples >= remaining_samples) @@ -282,12 +284,12 @@ void FrameMapper::Init() // Create the sample mapping struct - SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels)}; + SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels)}; // Reset the audio variables start_samples_frame = end_samples_frame; start_samples_position = end_samples_position + 1; - if (start_samples_position >= Frame::GetSamplesPerFrame(start_samples_frame, original, reader->info.sample_rate, reader->info.channels)) + if (start_samples_position >= Frame::GetSamplesPerFrame(ConvPositon(start_samples_frame), original, reader->info.sample_rate, reader->info.channels)) { start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one) start_samples_position = 0; // reset to 0, since we wrapped @@ -354,7 +356,7 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number) std::shared_ptr new_frame; // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now) - int samples_in_frame = Frame::GetSamplesPerFrame(number, target, reader->info.sample_rate, reader->info.channels); + int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(number), target, reader->info.sample_rate, reader->info.channels); try { // Debug output @@ -427,7 +429,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) // Get # of channels in the actual frame int channels_in_frame = mapped_frame->GetAudioChannelsCount(); - int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, target, mapped_frame->SampleRate(), channels_in_frame); + int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, mapped_frame->SampleRate(), channels_in_frame); // Determine if mapped frame is identical to source frame // including audio sample distribution according to mapped.Samples, @@ -687,6 +689,7 @@ Json::Value FrameMapper::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties root["type"] = "FrameMapper"; + root["position"] = position; // return JsonValue return root; @@ -715,6 +718,10 @@ void FrameMapper::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); + if(!root["position"].isNull()){ + position = root["position"].asDouble(); + } + // Re-Open path, and re-init everything (if needed) if (reader) { @@ -724,7 +731,7 @@ void FrameMapper::SetJsonValue(const Json::Value root) { } // Change frame rate or audio mapping details -void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) +void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) { ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ChangeMapping", "target_fps.num", target_fps.num, "target_fps.den", target_fps.den, "target_pulldown", target_pulldown, "target_sample_rate", target_sample_rate, "target_channels", target_channels, "target_channel_layout", target_channel_layout); @@ -743,6 +750,9 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow info.channels = target_channels; info.channel_layout = target_channel_layout; + position = clipPosition; + start = clipStart; + // Clear cache final_cache.Clear(); @@ -826,7 +836,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig } // Update total samples & input frame size (due to bigger or smaller data types) - total_frame_samples = Frame::GetSamplesPerFrame(frame->number, target, info.sample_rate, info.channels); + total_frame_samples = Frame::GetSamplesPerFrame(ConvPositon(frame->number), target, info.sample_rate, info.channels); ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ResampleMappedAudio (adjust # of samples)", "total_frame_samples", total_frame_samples, "info.sample_rate", info.sample_rate, "sample_rate_in_frame", sample_rate_in_frame, "info.channels", info.channels, "channels_in_frame", channels_in_frame, "original_frame_number", original_frame_number); @@ -935,3 +945,14 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig delete[] resampled_samples; resampled_samples = NULL; } + +int64_t FrameMapper::ConvPositon(int64_t clip_frame_number){ + + int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1; + int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1; + + int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame; + + ///std::cout << "Conv Position " << round(position * info.fps.ToDouble()) << " position: " << position << " info::fps: " << info.fps.ToDouble() << std::endl; + return frame_number; +} \ No newline at end of file diff --git a/src/Timeline.cpp b/src/Timeline.cpp index b2f46519c..f40272d84 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -340,14 +340,14 @@ void Timeline::apply_mapper_to_clip(Clip* clip) } else { // Create a new FrameMapper to wrap the current reader - FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); + FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); allocated_frame_mappers.insert(mapper); clip_reader = (ReaderBase*) mapper; } // Update the mapping FrameMapper* clip_mapped_reader = (FrameMapper*) clip_reader; - clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); + clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); // Update clip reader clip->Reader(clip_reader); @@ -545,11 +545,12 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Currently, the ResampleContext sometimes leaves behind a few samples for the next call, and the // number of samples returned is variable... and does not match the number expected. // This is a crude solution at best. =) - if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()) + if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()){ // Force timeline frame to match the source frame #pragma omp critical (T_addLayer) - new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout); + new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout); + } // Copy audio samples (and set initial volume). Mix samples with existing audio samples. The gains are added together, to // be sure to set the gain's correctly, so the sum does not exceed 1.0 (of audio distortion will happen). #pragma omp critical (T_addLayer) @@ -909,6 +910,7 @@ bool Timeline::isEqual(double a, double b) // Get an openshot::Frame object for a specific frame number of this reader. std::shared_ptr Timeline::GetFrame(int64_t requested_frame) { + // Adjust out of bounds frame number if (requested_frame < 1) requested_frame = 1; diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 921f3a155..02634c373 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -42,7 +42,7 @@ TEST(FrameMapper_Get_Valid_Frame) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping between 24 fps and 29.97 fps using classic pulldown - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); try { @@ -63,7 +63,7 @@ TEST(FrameMapper_Invalid_Frame_Too_Small) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); // Check invalid frame number CHECK_THROW(mapping.GetMappedFrame(0), OutOfBoundsFrame); @@ -76,7 +76,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Classic) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); @@ -93,7 +93,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Advanced) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -113,7 +113,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_None) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -130,7 +130,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Classic) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -150,7 +150,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Advanced) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -170,7 +170,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_None) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -189,7 +189,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) FFmpegReader r(path.str()); // Map to 30 fps, 3 channels surround, 44100 sample rate - FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND); + FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND, 0.0, 0.0); map.Open(); // Check details @@ -199,7 +199,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) CHECK_EQUAL(1470, map.GetFrame(50)->GetAudioSamplesCount()); // Change mapping data - map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO); + map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO, 0.0, 0.0); // Check details CHECK_EQUAL(1, map.GetFrame(1)->GetAudioChannelsCount()); @@ -210,3 +210,180 @@ TEST(FrameMapper_resample_audio_48000_to_41000) // Close mapper map.Close(); } + +TEST(FrameMapper_AudioSample_Distribution) +{ + + CacheMemory cache; + int OFFSET = 0; + float AMPLITUDE = 0.2; + double ANGLE = 0.0; + int NUM_SAMPLES = 100; + std::cout << "Starting Resample Test" << std::endl; + + for (int64_t frame_number = 1; frame_number <= 90; 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)); + + // Create test samples with sin wave (predictable values) + float *audio_buffer = new float[sample_count * 2]; + + for (int sample_number = 0; sample_number < sample_count; sample_number++) + { + // Calculate sin wave + // TODO: I'm using abs(), because calling AddAudio only seems to be adding the positive values and it's bizarre + float sample_value = float(AMPLITUDE * sin(ANGLE) + OFFSET); + audio_buffer[sample_number] = sample_value;//abs(sample_value); + ANGLE += (2 * M_PI) / NUM_SAMPLES; + + // Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source, + f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1 + f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2 + + // Add test frame to dummy reader + 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.info.has_audio = true; + r.Open(); // Open the reader + + // Map to 24 fps, which should create a variable # of samples per frame + ///FrameMapper map(&r, Fraction(24, 1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO); + //map.info.has_audio = true; + //map.Open(); + + Timeline t1(1920, 1080, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); + + Clip c1; + c1.Reader(&r); + c1.Layer(1); + c1.Position(0.0); + c1.Start(0.0); + c1.End(10.0); + + // Create 2nd map to 24 fps, which should create a variable # of samples per frame + + //FrameMapper map2(&r, Fraction(24, 1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO); + + //map2.info.has_audio = true; + //map2.Open(); + + Clip c2; + c2.Reader(&r); + c2.Layer(2); + + // Position 1 frame into the video, this should mis-align the audio and create situations + // which overlapping Frame instances have different # of samples for the Timeline. + // TODO: Moving to 0.0 position, to simplify this test for now + + + c2.Position(0.041666667 * 14); + c2.Start(1.0); + c2.End(10.0); + + // Add clips + + t1.AddClip(&c1); + t1.AddClip(&c2); + + std::string json_val = t1.Json(); + + std::cout << json_val << std::endl; + + //t1.SetJson(t1.Json()); + t1.Open(); + + FFmpegWriter w("output-resample.mp4"); + + // Set options + w.SetAudioOptions("aac", 44100, 192000); + w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); + + // Open writer + w.Open(); + + + w.WriteFrame(&t1, 5, 50); + + //for (int64_t frame_number = 1; frame_number <= 90; frame_number++){ + // w.WriteFrame(t1.GetFrame(frame_number)); + //} + + // Close writer & reader + w.Close(); + + //map.Close(); + //map2.Close(); + + t1.Close(); + + // Clean up + cache.Clear(); + + r.Close(); + +} + + +/* +TEST(FrameMapperVideoEdition){ + + stringstream path; + path << TEST_MEDIA_PATH << "baseline.mkv"; + FFmpegReader r(path.str()); + r.Open(); + + Clip c1; + c1.Reader(&r); + c1.Layer(1); + c1.Position(0.0); + c1.Start(0.0); + c1.End(45.0); + + Clip c2; + c2.Reader(&r); + c2.Layer(1); + c2.Position(30.0); + c2.Start(0.0); + c2.End(45.0); + + Timeline t1(1280, 720, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); + t1.AddClip(&c1); + t1.AddClip(&c2); + + t1.Open(); + + + FFmpegWriter w("simple-edit.mp4"); + + // Set options + w.SetAudioOptions("aac", 44100, 192000); + w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); + + // Open writer + w.Open(); + + + w.WriteFrame(&t1, 1, t1.GetMaxFrame()); + + // Close writer & reader + w.Close(); + + //map.Close(); + //map2.Close(); + + t1.Close(); + + + r.Close(); + + + +}*/ \ No newline at end of file From 57b48f31a2ccf92aa5d5afa4fe9e3d5f595f3672 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 14:55:25 -0500 Subject: [PATCH 38/86] Initializing Clip info struct, and fixing clip cache settings --- include/Clip.h | 15 +++++++++------ src/Clip.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index fd28c162a..0bc99e849 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -98,6 +98,15 @@ namespace openshot { /// Section lock for multiple threads juce::CriticalSection getFrameCriticalSection; + /// Init default settings for a clip + void init_settings(); + + /// Init reader info details + void init_reader_settings(); + + /// Update default rotation from reader + void init_reader_rotation(); + private: bool waveform; ///< Should a waveform be used instead of the clip's image std::list effects; /// frame, int64_t frame_number); - /// Init default settings for a clip - void init_settings(); - - /// Update default rotation from reader - void init_reader_rotation(); - /// Compare 2 floating point numbers bool isEqual(double a, double b); diff --git a/src/Clip.cpp b/src/Clip.cpp index 1d2b392d8..d81b5c610 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -69,9 +69,6 @@ void Clip::init_settings() // Init alpha alpha = Keyframe(1.0); - // Init rotation - init_reader_rotation(); - // Init time & volume time = Keyframe(1.0); volume = Keyframe(1.0); @@ -101,8 +98,22 @@ void Clip::init_settings() has_audio = Keyframe(-1.0); has_video = Keyframe(-1.0); - // Initialize Clip cache - cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + // Init reader info struct and cache size + init_reader_settings(); +} + +// Init reader info details +void Clip::init_reader_settings() { + if (reader) { + // Init rotation (if any) + init_reader_rotation(); + + // Initialize info struct + info = reader->info; + + // Initialize Clip cache + cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + } } // Init reader's rotation (if any) @@ -208,8 +219,8 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N End(reader->info.duration); reader->ParentClip(this); allocated_reader = reader; - init_reader_rotation(); - } + // Init reader info struct and cache size + init_reader_settings(); } } // Destructor @@ -237,8 +248,8 @@ void Clip::Reader(ReaderBase* new_reader) // set parent reader->ParentClip(this); - // Init rotation (if any) - init_reader_rotation(); + // Init reader info struct and cache size + init_reader_settings(); } /// Get the current reader From 91945f03dc3d1ee9848e542db5189bad97c640de Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 17:08:27 -0500 Subject: [PATCH 39/86] Replacing audio fix implementation with ParentClip(), to access clip start and position (if any) --- include/FrameMapper.h | 12 +++--- src/FrameMapper.cpp | 85 +++++++++++++++++++++---------------- src/Timeline.cpp | 4 +- tests/FrameMapper_Tests.cpp | 85 ++++++------------------------------- 4 files changed, 67 insertions(+), 119 deletions(-) diff --git a/include/FrameMapper.h b/include/FrameMapper.h index 85c933d25..7f6c9c165 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -147,9 +147,6 @@ namespace openshot bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping SWRCONTEXT *avr; // Audio resampling context object - float position; - float start; - // Internal methods used by init void AddField(int64_t frame); void AddField(Field field); @@ -157,6 +154,9 @@ namespace openshot // Get Frame or Generate Blank Frame std::shared_ptr GetOrCreateFrame(int64_t number); + /// Adjust frame number for Clip position and start (which can result in a different number) + int64_t AdjustFrameNumber(int64_t clip_frame_number, float position, float start); + // Use the original and target frame rates and a pull-down technique to create // a mapping between the original fields and frames or a video to a new frame rate. // This might repeat or skip fields and frames of the original video, depending on @@ -169,13 +169,13 @@ namespace openshot std::vector frames; // List of all frames /// Default constructor for openshot::FrameMapper class - FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); + FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); /// Destructor virtual ~FrameMapper(); /// Change frame rate or audio mapping details - void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); + void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); /// Close the openshot::FrameMapper and internal reader void Close() override; @@ -220,8 +220,6 @@ namespace openshot /// Resample audio and map channels (if needed) void ResampleMappedAudio(std::shared_ptr frame, int64_t original_frame_number); - - int64_t ConvPositon(int64_t clip_frame_number); }; } diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index d05cf7f66..332211737 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -29,11 +29,12 @@ */ #include "../include/FrameMapper.h" +#include "../include/Clip.h" using namespace std; using namespace openshot; -FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) : +FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) : reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL) { // Set the original frame rate from the reader @@ -52,8 +53,6 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe info.width = reader->info.width; info.height = reader->info.height; - position = clipPosition; - start = clipStart; // Used to toggle odd / even fields field_toggle = true; @@ -118,6 +117,15 @@ void FrameMapper::Init() // Clear cache final_cache.Clear(); + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Some framerates are handled special, and some use a generic Keyframe curve to // map the framerates. These are the special framerates: if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) && @@ -259,12 +267,12 @@ void FrameMapper::Init() // the original sample rate. int64_t end_samples_frame = start_samples_frame; int end_samples_position = start_samples_position; - int remaining_samples = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels); + int remaining_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels); while (remaining_samples > 0) { // get original samples - int original_samples = Frame::GetSamplesPerFrame(ConvPositon(end_samples_frame), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; + int original_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(end_samples_frame, clipPosition, clipStart), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; // Enough samples if (original_samples >= remaining_samples) @@ -284,12 +292,12 @@ void FrameMapper::Init() // Create the sample mapping struct - SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels)}; + SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels)}; // Reset the audio variables start_samples_frame = end_samples_frame; start_samples_position = end_samples_position + 1; - if (start_samples_position >= Frame::GetSamplesPerFrame(ConvPositon(start_samples_frame), original, reader->info.sample_rate, reader->info.channels)) + if (start_samples_position >= Frame::GetSamplesPerFrame(AdjustFrameNumber(start_samples_frame, clipPosition, clipStart), original, reader->info.sample_rate, reader->info.channels)) { start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one) start_samples_position = 0; // reset to 0, since we wrapped @@ -355,8 +363,17 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number) { std::shared_ptr new_frame; + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now) - int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(number), target, reader->info.sample_rate, reader->info.channels); + int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels); try { // Debug output @@ -406,6 +423,15 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) final_frame = final_cache.GetFrame(requested_frame); if (final_frame) return final_frame; + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Minimum number of frames to process (for performance reasons) // Dialing this down to 1 for now, as it seems to improve performance, and reduce export crashes int minimum_frames = 1; @@ -429,7 +455,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) // Get # of channels in the actual frame int channels_in_frame = mapped_frame->GetAudioChannelsCount(); - int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, mapped_frame->SampleRate(), channels_in_frame); + int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, mapped_frame->SampleRate(), channels_in_frame); // Determine if mapped frame is identical to source frame // including audio sample distribution according to mapped.Samples, @@ -596,21 +622,6 @@ void FrameMapper::PrintMapping() // Recalculate mappings Init(); - // Get the difference (in frames) between the original and target frame rates - float difference = target.ToInt() - original.ToInt(); - - int field_interval = 0; - int frame_interval = 0; - - if (difference != 0) - { - // Find the number (i.e. interval) of fields that need to be skipped or repeated - field_interval = round(fabs(original.ToInt() / difference)); - - // Get frame interval (2 fields per frame) - frame_interval = field_interval * 2.0f; - } - // Loop through frame mappings for (float map = 1; map <= frames.size(); map++) { @@ -621,7 +632,6 @@ void FrameMapper::PrintMapping() } - // Determine if reader is open or closed bool FrameMapper::IsOpen() { if (reader) @@ -630,7 +640,6 @@ bool FrameMapper::IsOpen() { return false; } - // Open the internal reader void FrameMapper::Open() { @@ -689,7 +698,6 @@ Json::Value FrameMapper::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties root["type"] = "FrameMapper"; - root["position"] = position; // return JsonValue return root; @@ -718,10 +726,6 @@ void FrameMapper::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); - if(!root["position"].isNull()){ - position = root["position"].asDouble(); - } - // Re-Open path, and re-init everything (if needed) if (reader) { @@ -731,7 +735,7 @@ void FrameMapper::SetJsonValue(const Json::Value root) { } // Change frame rate or audio mapping details -void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) +void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) { ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ChangeMapping", "target_fps.num", target_fps.num, "target_fps.den", target_fps.den, "target_pulldown", target_pulldown, "target_sample_rate", target_sample_rate, "target_channels", target_channels, "target_channel_layout", target_channel_layout); @@ -750,9 +754,6 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow info.channels = target_channels; info.channel_layout = target_channel_layout; - position = clipPosition; - start = clipStart; - // Clear cache final_cache.Clear(); @@ -775,6 +776,15 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig // Recalculate mappings Init(); + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Init audio buffers / variables int total_frame_samples = 0; int channels_in_frame = frame->GetAudioChannelsCount(); @@ -836,7 +846,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig } // Update total samples & input frame size (due to bigger or smaller data types) - total_frame_samples = Frame::GetSamplesPerFrame(ConvPositon(frame->number), target, info.sample_rate, info.channels); + total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number, clipPosition, clipStart), target, info.sample_rate, info.channels); ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ResampleMappedAudio (adjust # of samples)", "total_frame_samples", total_frame_samples, "info.sample_rate", info.sample_rate, "sample_rate_in_frame", sample_rate_in_frame, "info.channels", info.channels, "channels_in_frame", channels_in_frame, "original_frame_number", original_frame_number); @@ -946,7 +956,8 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig resampled_samples = NULL; } -int64_t FrameMapper::ConvPositon(int64_t clip_frame_number){ +// Adjust frame number for Clip position and start (which can result in a different number) +int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number, float position, float start) { int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1; int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index e45a0991a..736bee156 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -348,14 +348,14 @@ void Timeline::apply_mapper_to_clip(Clip* clip) } else { // Create a new FrameMapper to wrap the current reader - FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); + FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); allocated_frame_mappers.insert(mapper); clip_reader = (ReaderBase*) mapper; } // Update the mapping FrameMapper* clip_mapped_reader = (FrameMapper*) clip_reader; - clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); + clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); // Update clip reader clip->Reader(clip_reader); diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 02634c373..54e76e64c 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -42,7 +42,7 @@ TEST(FrameMapper_Get_Valid_Frame) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping between 24 fps and 29.97 fps using classic pulldown - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); try { @@ -63,7 +63,7 @@ TEST(FrameMapper_Invalid_Frame_Too_Small) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); // Check invalid frame number CHECK_THROW(mapping.GetMappedFrame(0), OutOfBoundsFrame); @@ -76,7 +76,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Classic) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); @@ -93,7 +93,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Advanced) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -113,7 +113,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_None) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -130,7 +130,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Classic) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -150,7 +150,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Advanced) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -170,7 +170,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_None) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -189,7 +189,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) FFmpegReader r(path.str()); // Map to 30 fps, 3 channels surround, 44100 sample rate - FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND, 0.0, 0.0); + FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND); map.Open(); // Check details @@ -199,7 +199,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) CHECK_EQUAL(1470, map.GetFrame(50)->GetAudioSamplesCount()); // Change mapping data - map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO, 0.0, 0.0); + map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO); // Check details CHECK_EQUAL(1, map.GetFrame(1)->GetAudioChannelsCount()); @@ -213,13 +213,12 @@ TEST(FrameMapper_resample_audio_48000_to_41000) TEST(FrameMapper_AudioSample_Distribution) { - CacheMemory cache; int OFFSET = 0; float AMPLITUDE = 0.2; double ANGLE = 0.0; int NUM_SAMPLES = 100; - std::cout << "Starting Resample Test" << std::endl; + //std::cout << "Starting Resample Test" << std::endl; for (int64_t frame_number = 1; frame_number <= 90; frame_number++) { @@ -283,7 +282,6 @@ TEST(FrameMapper_AudioSample_Distribution) // which overlapping Frame instances have different # of samples for the Timeline. // TODO: Moving to 0.0 position, to simplify this test for now - c2.Position(0.041666667 * 14); c2.Start(1.0); c2.End(10.0); @@ -295,7 +293,7 @@ TEST(FrameMapper_AudioSample_Distribution) std::string json_val = t1.Json(); - std::cout << json_val << std::endl; + //std::cout << json_val << std::endl; //t1.SetJson(t1.Json()); t1.Open(); @@ -309,7 +307,6 @@ TEST(FrameMapper_AudioSample_Distribution) // Open writer w.Open(); - w.WriteFrame(&t1, 5, 50); //for (int64_t frame_number = 1; frame_number <= 90; frame_number++){ @@ -328,62 +325,4 @@ TEST(FrameMapper_AudioSample_Distribution) cache.Clear(); r.Close(); - } - - -/* -TEST(FrameMapperVideoEdition){ - - stringstream path; - path << TEST_MEDIA_PATH << "baseline.mkv"; - FFmpegReader r(path.str()); - r.Open(); - - Clip c1; - c1.Reader(&r); - c1.Layer(1); - c1.Position(0.0); - c1.Start(0.0); - c1.End(45.0); - - Clip c2; - c2.Reader(&r); - c2.Layer(1); - c2.Position(30.0); - c2.Start(0.0); - c2.End(45.0); - - Timeline t1(1280, 720, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); - t1.AddClip(&c1); - t1.AddClip(&c2); - - t1.Open(); - - - FFmpegWriter w("simple-edit.mp4"); - - // Set options - w.SetAudioOptions("aac", 44100, 192000); - w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); - - // Open writer - w.Open(); - - - w.WriteFrame(&t1, 1, t1.GetMaxFrame()); - - // Close writer & reader - w.Close(); - - //map.Close(); - //map2.Close(); - - t1.Close(); - - - r.Close(); - - - -}*/ \ No newline at end of file From 94059828d563e7511e424d044a8dfac1d0fb146e Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 18:18:10 -0500 Subject: [PATCH 40/86] Converting RGB8888 to ARGB32_Premultiplied (for performance reasons) --- src/CacheDisk.cpp | 2 +- src/FFmpegReader.cpp | 4 ++-- src/Frame.cpp | 28 ++++++++++++++-------------- src/QtHtmlReader.cpp | 2 +- src/QtImageReader.cpp | 5 ----- src/QtTextReader.cpp | 2 +- src/effects/Bars.cpp | 2 +- src/effects/Crop.cpp | 2 +- src/effects/Deinterlace.cpp | 2 +- 9 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index 91153c8f4..789ab25ce 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -235,7 +235,7 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number) image->load(frame_path); // Set pixel formatimage-> - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); + image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_ARGB32_Premultiplied))); // Create frame object std::shared_ptr frame(new Frame()); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 0b9ef7401..d336654a3 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1363,7 +1363,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { scale_mode = SWS_BICUBIC; } SwsContext *img_convert_ctx = sws_getContext(info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, - height, PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL); + height, AV_PIX_FMT_RGB32, scale_mode, NULL, NULL, NULL); // Resize / Convert to RGB sws_scale(img_convert_ctx, my_frame->data, my_frame->linesize, 0, @@ -1373,7 +1373,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { std::shared_ptr f = CreateFrame(current_frame); // Add Image data to frame - f->AddImage(width, height, 4, QImage::Format_RGBA8888, buffer); + f->AddImage(width, height, 4, QImage::Format_ARGB32_Premultiplied, buffer); // Update working cache working_cache.Add(f); diff --git a/src/Frame.cpp b/src/Frame.cpp index 483df7688..805192924 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -231,7 +231,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G } // Create blank image - wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_RGBA8888)); + wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_ARGB32_Premultiplied)); wave_image->fill(QColor(0,0,0,0)); // Load QPainter with wave_image device @@ -262,7 +262,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G else { // No audio samples present - wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); wave_image->fill(QColor(QString::fromStdString("#000000"))); } @@ -618,7 +618,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate) { // Create blank thumbnail image & fill background color - std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888)); + std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_ARGB32_Premultiplied)); thumbnail->fill(QColor(QString::fromStdString(background_color))); // Create painter @@ -673,7 +673,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri overlay->load(QString::fromStdString(overlay_path)); // Set pixel format - overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888))); + overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_ARGB32_Premultiplied))); // Resize to fit overlay = std::shared_ptr(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); @@ -691,7 +691,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri mask->load(QString::fromStdString(mask_path)); // Set pixel format - mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_RGBA8888))); + mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_ARGB32_Premultiplied))); // Resize to fit mask = std::shared_ptr(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); @@ -747,7 +747,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888)); + image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_ARGB32_Premultiplied)); // Fill with solid color image->fill(QColor(QString::fromStdString(color))); @@ -775,8 +775,8 @@ void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage: image = std::shared_ptr(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer)); // Always convert to RGBA8888 (if different) - if (image->format() != QImage::Format_RGBA8888) - *image = image->convertToFormat(QImage::Format_RGBA8888); + if (image->format() != QImage::Format_ARGB32_Premultiplied) + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); // Update height and width width = image->width(); @@ -798,9 +798,9 @@ void Frame::AddImage(std::shared_ptr new_image) { image = new_image; - // Always convert to RGBA8888 (if different) - if (image->format() != QImage::Format_RGBA8888) - *image = image->convertToFormat(QImage::Format_RGBA8888); + // Always convert to Format_ARGB32_Premultiplied (if different) + if (image->format() != QImage::Format_ARGB32_Premultiplied) + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); // Update height and width width = image->width(); @@ -830,8 +830,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) if (image == new_image || image->size() != new_image->size()) { ret = true; } - else if (new_image->format() != image->format()) { - new_image = std::shared_ptr(new QImage(new_image->convertToFormat(image->format()))); + else if (new_image->format() != QImage::Format_ARGB32_Premultiplied) { + new_image = std::shared_ptr(new QImage(new_image->convertToFormat(QImage::Format_ARGB32_Premultiplied))); } } if (ret) { @@ -970,7 +970,7 @@ void Frame::AddMagickImage(std::shared_ptr new_image) MagickCore::ExportImagePixels(new_image->constImage(), 0, 0, new_image->columns(), new_image->rows(), "RGBA", Magick::CharPixel, buffer, &exception); // Create QImage of frame data - image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer)); + image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_ARGB32_Premultiplied, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer)); // Update height and width width = image->width(); diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 6b502fbd4..776fb1425 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -62,7 +62,7 @@ void QtHtmlReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); image->fill(QColor(background_color.c_str())); //start painting diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 725d88180..0f3e789b3 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -104,9 +104,6 @@ void QtImageReader::Open() throw InvalidFile("File could not be opened.", path.toStdString()); } - // Convert to proper format - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); - // Update image properties info.has_audio = false; info.has_video = true; @@ -257,8 +254,6 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) cached_image = std::shared_ptr(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); } - cached_image = std::shared_ptr(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888))); - // Set max size (to later determine if max_size is changed) max_size.setWidth(max_width); max_size.setHeight(max_height); diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index d91d164e6..9a048feb8 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -67,7 +67,7 @@ void QtTextReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); image->fill(QColor(background_color.c_str())); QPainter painter; diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index 3f9aac344..14064a71a 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -68,7 +68,7 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get bar color (and create small color image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888)); + std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied)); tempColor->fill(QColor(QString::fromStdString(color.GetColorHex(frame_number)))); // Get current keyframe values diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index b1c3d38d9..5ef7f7e68 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -68,7 +68,7 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get transparent color (and create small transparent image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888)); + std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied)); tempColor->fill(QColor(QString::fromStdString("transparent"))); // Get current keyframe values diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 39b3316a5..c9b8e17e3 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -73,7 +73,7 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 const unsigned char* pixels = image->bits(); // Create a smaller, new image - QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_RGBA8888); + QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_ARGB32_Premultiplied); const unsigned char* deinterlaced_pixels = deinterlaced_image.bits(); // Loop through the scanlines of the image (even or odd) From 6bd7fb72353a7f113739790228c4fed50e0cb22e Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 14 Oct 2020 03:06:30 -0500 Subject: [PATCH 41/86] Replacing ARGB32_Premultiplied with Format_RGBA8888_Premultiplied, which still seems to benefit from performance, but keeps the byte order the same as before. win win --- src/CacheDisk.cpp | 2 +- src/FFmpegReader.cpp | 4 ++-- src/Frame.cpp | 28 ++++++++++++++-------------- src/QtHtmlReader.cpp | 2 +- src/QtImageReader.cpp | 4 ++-- src/QtTextReader.cpp | 2 +- src/effects/Bars.cpp | 2 +- src/effects/Crop.cpp | 2 +- src/effects/Deinterlace.cpp | 2 +- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index 789ab25ce..e4ef4c9d3 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -235,7 +235,7 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number) image->load(frame_path); // Set pixel formatimage-> - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_ARGB32_Premultiplied))); + image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888_Premultiplied))); // Create frame object std::shared_ptr frame(new Frame()); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index d336654a3..19bca924b 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1363,7 +1363,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { scale_mode = SWS_BICUBIC; } SwsContext *img_convert_ctx = sws_getContext(info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, - height, AV_PIX_FMT_RGB32, scale_mode, NULL, NULL, NULL); + height, PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL); // Resize / Convert to RGB sws_scale(img_convert_ctx, my_frame->data, my_frame->linesize, 0, @@ -1373,7 +1373,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { std::shared_ptr f = CreateFrame(current_frame); // Add Image data to frame - f->AddImage(width, height, 4, QImage::Format_ARGB32_Premultiplied, buffer); + f->AddImage(width, height, 4, QImage::Format_RGBA8888_Premultiplied, buffer); // Update working cache working_cache.Add(f); diff --git a/src/Frame.cpp b/src/Frame.cpp index 805192924..dcd26dd8e 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -231,7 +231,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G } // Create blank image - wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_ARGB32_Premultiplied)); + wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_RGBA8888_Premultiplied)); wave_image->fill(QColor(0,0,0,0)); // Load QPainter with wave_image device @@ -262,7 +262,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G else { // No audio samples present - wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); + wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888_Premultiplied)); wave_image->fill(QColor(QString::fromStdString("#000000"))); } @@ -618,7 +618,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate) { // Create blank thumbnail image & fill background color - std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_ARGB32_Premultiplied)); + std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888_Premultiplied)); thumbnail->fill(QColor(QString::fromStdString(background_color))); // Create painter @@ -673,7 +673,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri overlay->load(QString::fromStdString(overlay_path)); // Set pixel format - overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_ARGB32_Premultiplied))); + overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888_Premultiplied))); // Resize to fit overlay = std::shared_ptr(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); @@ -691,7 +691,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri mask->load(QString::fromStdString(mask_path)); // Set pixel format - mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_ARGB32_Premultiplied))); + mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_RGBA8888_Premultiplied))); // Resize to fit mask = std::shared_ptr(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); @@ -747,7 +747,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_ARGB32_Premultiplied)); + image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888_Premultiplied)); // Fill with solid color image->fill(QColor(QString::fromStdString(color))); @@ -775,8 +775,8 @@ void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage: image = std::shared_ptr(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer)); // Always convert to RGBA8888 (if different) - if (image->format() != QImage::Format_ARGB32_Premultiplied) - *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); // Update height and width width = image->width(); @@ -798,9 +798,9 @@ void Frame::AddImage(std::shared_ptr new_image) { image = new_image; - // Always convert to Format_ARGB32_Premultiplied (if different) - if (image->format() != QImage::Format_ARGB32_Premultiplied) - *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + // Always convert to Format_RGBA8888_Premultiplied (if different) + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); // Update height and width width = image->width(); @@ -830,8 +830,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) if (image == new_image || image->size() != new_image->size()) { ret = true; } - else if (new_image->format() != QImage::Format_ARGB32_Premultiplied) { - new_image = std::shared_ptr(new QImage(new_image->convertToFormat(QImage::Format_ARGB32_Premultiplied))); + else if (new_image->format() != QImage::Format_RGBA8888_Premultiplied) { + new_image = std::shared_ptr(new QImage(new_image->convertToFormat(QImage::Format_RGBA8888_Premultiplied))); } } if (ret) { @@ -970,7 +970,7 @@ void Frame::AddMagickImage(std::shared_ptr new_image) MagickCore::ExportImagePixels(new_image->constImage(), 0, 0, new_image->columns(), new_image->rows(), "RGBA", Magick::CharPixel, buffer, &exception); // Create QImage of frame data - image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_ARGB32_Premultiplied, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer)); + image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888_Premultiplied, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer)); // Update height and width width = image->width(); diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 776fb1425..e27f1c0f0 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -62,7 +62,7 @@ void QtHtmlReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); + image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888_Premultiplied)); image->fill(QColor(background_color.c_str())); //start painting diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 0f3e789b3..0f4fdc38a 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -82,7 +82,7 @@ void QtImageReader::Open() ResvgRenderer renderer(path); if (renderer.isValid()) { - image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied)); + image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_RGBA8888_Premultiplied)); image->fill(Qt::transparent); QPainter p(image.get()); @@ -236,7 +236,7 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) svg_size.scale(max_width, max_height, Qt::KeepAspectRatio); // Create empty QImage - cached_image = std::shared_ptr(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_ARGB32_Premultiplied)); + cached_image = std::shared_ptr(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_RGBA8888_Premultiplied)); cached_image->fill(Qt::transparent); // Render SVG into QImage diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index 9a048feb8..4764cd0de 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -67,7 +67,7 @@ void QtTextReader::Open() if (!is_open) { // create image - image = std::shared_ptr(new QImage(width, height, QImage::Format_ARGB32_Premultiplied)); + image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888_Premultiplied)); image->fill(QColor(background_color.c_str())); QPainter painter; diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index 14064a71a..7180bdcdb 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -68,7 +68,7 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get bar color (and create small color image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied)); + std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888_Premultiplied)); tempColor->fill(QColor(QString::fromStdString(color.GetColorHex(frame_number)))); // Get current keyframe values diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index 5ef7f7e68..170f6fa53 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -68,7 +68,7 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram std::shared_ptr frame_image = frame->GetImage(); // Get transparent color (and create small transparent image) - std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied)); + std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888_Premultiplied)); tempColor->fill(QColor(QString::fromStdString("transparent"))); // Get current keyframe values diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index c9b8e17e3..24402288d 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -73,7 +73,7 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 const unsigned char* pixels = image->bits(); // Create a smaller, new image - QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_ARGB32_Premultiplied); + QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_RGBA8888_Premultiplied); const unsigned char* deinterlaced_pixels = deinterlaced_image.bits(); // Loop through the scanlines of the image (even or odd) From 1eecda3d4ef3d49285acca580e571f7c5813be33 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 14 Oct 2020 14:19:26 -0500 Subject: [PATCH 42/86] Fix alpha and mask effects, so they correctly multiply the alpha to all colors (since we have switched to a premulitplied alpha format) --- src/Clip.cpp | 6 +++++- src/effects/Mask.cpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index d81b5c610..166f716cb 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1155,7 +1155,11 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) // Loop through pixels for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) { - // Apply alpha to pixel + // Apply alpha to pixel values (since we use a premultiplied value, we must + // multiply the alpha with all colors). + pixels[byte_index + 0] *= alpha_value; + pixels[byte_index + 1] *= alpha_value; + pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index 11c37f053..c5ed16c12 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -117,6 +117,7 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram R = mask_pixels[byte_index]; G = mask_pixels[byte_index + 1]; B = mask_pixels[byte_index + 2]; + A = mask_pixels[byte_index + 3]; // Get the average luminosity gray_value = qGray(R, G, B); @@ -131,16 +132,23 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram // Constrain the value from 0 to 255 gray_value = constrain(gray_value); + // Calculate the % change in alpha + float alpha_percent = float(constrain(A - gray_value)) / 255.0; + // Set the alpha channel to the gray value if (replace_image) { - // Replace frame pixels with gray value + // Replace frame pixels with gray value (including alpha channel) pixels[byte_index + 0] = gray_value; pixels[byte_index + 1] = gray_value; pixels[byte_index + 2] = gray_value; + pixels[byte_index + 3] = gray_value; } else { - // Set alpha channel - A = pixels[byte_index + 3]; - pixels[byte_index + 3] = constrain(A - gray_value); + // Mulitply new alpha value with all the colors (since we are using a premultiplied + // alpha format) + pixels[byte_index + 0] *= alpha_percent; + pixels[byte_index + 1] *= alpha_percent; + pixels[byte_index + 2] *= alpha_percent; + pixels[byte_index + 3] *= alpha_percent; } } From 88677a9c3c03d903c82b69fd81365a5329a97960 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 02:54:54 -0500 Subject: [PATCH 43/86] Update Saturation.cpp Removing alpha channel access (undefined) --- src/effects/Saturation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 5ce958a50..25fc51bc3 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -154,7 +154,6 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ pixels[byte_index] = R; pixels[byte_index + 1] = G; pixels[byte_index + 2] = B; - pixels[byte_index + 3] = A; // leave the alpha value alone } // return the modified frame From a7fe41cc1a796036a85f4917a7e845285bc64c6c Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 03:03:26 -0500 Subject: [PATCH 44/86] Update Saturation.cpp Fixing regression after conflict resolution --- src/effects/Saturation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 25fc51bc3..cad9c08d7 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -151,9 +151,9 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ B = constrain(B); // Set all pixels to new value - pixels[byte_index] = R; - pixels[byte_index + 1] = G; - pixels[byte_index + 2] = B; + pixels[pixel * 4] = R; + pixels[pixel * 4 + 1] = G; + pixels[pixel * 4 + 2] = B; } // return the modified frame From f4d0d9d7bb4bf97184bd8692e0ee619562ba5c66 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 18:04:10 -0500 Subject: [PATCH 45/86] Fixing some cpp_test complaints --- src/Clip.cpp | 3 ++- tests/FrameMapper_Tests.cpp | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index 1970884dc..0035d766e 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1150,7 +1150,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) float alpha_value = alpha.GetValue(frame->number); // Get source image's pixels - unsigned char *pixels = (unsigned char *) source_image->bits(); + unsigned char *pixels = source_image->bits(); // Loop through pixels for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) @@ -1162,6 +1162,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } + pixels = NULL; // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 54e76e64c..9caa2a18c 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -287,13 +287,8 @@ TEST(FrameMapper_AudioSample_Distribution) c2.End(10.0); // Add clips - t1.AddClip(&c1); t1.AddClip(&c2); - - std::string json_val = t1.Json(); - - //std::cout << json_val << std::endl; //t1.SetJson(t1.Json()); t1.Open(); From 29107bc427d467ed4a0018e496fa2318ecbe77cd Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 18:22:42 -0500 Subject: [PATCH 46/86] Fixing some additional cpp_test complaints --- src/Clip.cpp | 1 - src/Timeline.cpp | 2 +- src/effects/Mask.cpp | 18 ++++++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index 0035d766e..3c2c05190 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1162,7 +1162,6 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } - pixels = NULL; // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index f7f018e7c..4d12b1374 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -492,7 +492,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in /* Apply effects to the source frame (if any). If multiple clips are overlapping, only process the * effects on the top clip. */ - if (is_top_clip && source_frame) { + if (is_top_clip) { #pragma omp critical (T_addLayer) source_frame = apply_effects(source_frame, timeline_frame_number, source_clip->Layer()); } diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index d0e86cd16..a631775fa 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -102,12 +102,6 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram unsigned char *pixels = (unsigned char *) frame_image->bits(); unsigned char *mask_pixels = (unsigned char *) original_mask->bits(); - int R = 0; - int G = 0; - int B = 0; - int A = 0; - int gray_value = 0; - float factor = 0.0; double contrast_value = (contrast.GetValue(frame_number)); double brightness_value = (brightness.GetValue(frame_number)); @@ -115,16 +109,16 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram for (int pixel = 0, byte_index=0; pixel < original_mask->width() * original_mask->height(); pixel++, byte_index+=4) { // Get the RGB values from the pixel - R = mask_pixels[byte_index]; - G = mask_pixels[byte_index + 1]; - B = mask_pixels[byte_index + 2]; - A = mask_pixels[byte_index + 3]; + int R = mask_pixels[byte_index]; + int G = mask_pixels[byte_index + 1]; + int B = mask_pixels[byte_index + 2]; + int A = mask_pixels[byte_index + 3]; // Get the average luminosity - gray_value = qGray(R, G, B); + int gray_value = qGray(R, G, B); // Adjust the contrast - factor = (259 * (contrast_value + 255)) / (255 * (259 - contrast_value)); + float factor = (259 * (contrast_value + 255)) / (255 * (259 - contrast_value)); gray_value = constrain((factor * (gray_value - 128)) + 128); // Adjust the brightness From 9c3a2975be569df5cd18fe790dd6941b3b13f8ba Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 18:56:34 -0500 Subject: [PATCH 47/86] Update the stale github message --- .github/stale.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index c5ec1048c..bba68742f 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -11,9 +11,17 @@ exemptLabels: staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > - This issue has been automatically marked as **stale** because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. + Thank you so much for submitting an issue to help improve OpenShot Video Editor. We are sorry about this, but this particular issue has gone unnoticed for quite some time. To help keep the OpenShot GitHub Issue Tracker organized and focused, we must ensure that every issue is correctly labelled and triaged, to get the proper attention. + + This issue will be closed, as it meets the following criteria: + - No activity in the past 90 days + - No one is assigned to this issue + + We'd like to ask you to help us out and determine whether this issue should be reopened. + - If this issue is reporting a bug, please can you attempt to reproduce on the [latest daily build](https://www.openshot.org/download/#daily) to help us to understand whether the bug still needs our attention. + - If this issue is proposing a new feature, please can you verify whether the feature proposal is still relevant. + + Thanks again for your help! # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false # Only close issues From 06d45cb88aed5af39c08142147169eced2cf7f2b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 17 Oct 2020 05:56:02 -0400 Subject: [PATCH 48/86] ImageReader: Consolidate ctors using default arg --- include/ImageReader.h | 18 +++++++++--------- src/ImageReader.cpp | 9 +-------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/include/ImageReader.h b/include/ImageReader.h index 5aafcc8f7..aa96272fd 100644 --- a/include/ImageReader.h +++ b/include/ImageReader.h @@ -76,15 +76,15 @@ namespace openshot bool is_open; public: - - /// Constructor for ImageReader. This automatically opens the media file and loads - /// frame 1, or it throws one of the following exceptions. - ImageReader(std::string path); - - /// Constructor for ImageReader. This only opens the media file to inspect its properties - /// if inspect_reader=true. When not inspecting the media file, it's much faster, and useful - /// when you are inflating the object using JSON after instantiating it. - ImageReader(std::string path, bool inspect_reader); + /// @brief Constructor for ImageReader. + /// + /// Opens the media file to inspect its properties and loads frame 1, + /// iff inspect_reader == true (the default). Pass a false value in + /// the optional parameter to defer this initial Open()/Close() cycle. + /// + /// When not inspecting the media file, it's much faster, and useful + /// when you are inflating the object using JSON after instantiation. + ImageReader(const std::string& path, bool inspect_reader=true); /// Close File void Close() override; diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index 9a001879f..2a4d411ed 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -35,14 +35,7 @@ using namespace openshot; -ImageReader::ImageReader(std::string path) : path(path), is_open(false) -{ - // Open and Close the reader, to populate its attributes (such as height, width, etc...) - Open(); - Close(); -} - -ImageReader::ImageReader(std::string path, bool inspect_reader) : path(path), is_open(false) +ImageReader::ImageReader(const std::string& path, bool inspect_reader) : path(path), is_open(false) { // Open and Close the reader, to populate its attributes (such as height, width, etc...) if (inspect_reader) { From 6e4476dc6c34029544cc01eb660a954d6a76ed4d Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 17 Oct 2020 05:56:39 -0400 Subject: [PATCH 49/86] Tests: Increase coverage for ImageReader/Writer --- tests/ImageWriter_Tests.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/ImageWriter_Tests.cpp b/tests/ImageWriter_Tests.cpp index bdf73a07d..6d3a2e229 100644 --- a/tests/ImageWriter_Tests.cpp +++ b/tests/ImageWriter_Tests.cpp @@ -37,17 +37,33 @@ using namespace std; using namespace openshot; #ifdef USE_IMAGEMAGICK -TEST(ImageWriter_Test_Gif) +SUITE(ImageWriter) { - // Reader + +TEST(Gif) +{ + // Reader --------------- + + // Bad path + FFmpegReader bad_r("/tmp/bleeblorp.xls", false); + CHECK_THROW(bad_r.Open(), InvalidFile); + + // Good path stringstream path; path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4"; FFmpegReader r(path.str()); + + // Read-before-open error + CHECK_THROW(r.GetFrame(1), ReaderClosed); + r.Open(); /* WRITER ---------------- */ ImageWriter w("output1.gif"); + // Check for exception on write-before-open + CHECK_THROW(w.WriteFrame(&r, 500, 504), WriterClosed); + // Set the image output settings (format, fps, width, height, quality, loops, combine) w.SetVideoOptions("GIF", r.info.fps, r.info.width, r.info.height, 70, 1, true); @@ -82,4 +98,6 @@ TEST(ImageWriter_Test_Gif) CHECK_CLOSE(11, (int)pixels[pixel_index + 2], 5); CHECK_CLOSE(255, (int)pixels[pixel_index + 3], 5); } + +} // SUITE #endif From 6c656dd7f7c1f54b9d71cb428d1c9f32ecef1a7b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 17 Oct 2020 06:23:44 -0400 Subject: [PATCH 50/86] QtImageReader: Consolidate ctors --- include/QtImageReader.h | 18 +++++++++--------- src/QtImageReader.cpp | 7 ------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/include/QtImageReader.h b/include/QtImageReader.h index 3848065a1..5162b8f45 100644 --- a/include/QtImageReader.h +++ b/include/QtImageReader.h @@ -72,15 +72,15 @@ namespace openshot QSize max_size; ///> Current max_size as calculated with Clip properties public: - - /// Constructor for QtImageReader. This automatically opens the media file and loads - /// frame 1, or it throws one of the following exceptions. - QtImageReader(std::string path); - - /// Constructor for QtImageReader. This only opens the media file to inspect its properties - /// if inspect_reader=true. When not inspecting the media file, it's much faster, and useful - /// when you are inflating the object using JSON after instantiating it. - QtImageReader(std::string path, bool inspect_reader); + /// @brief Constructor for QtImageReader. + /// + /// Opens the media file to inspect its properties and loads frame 1, + /// iff inspect_reader == true (the default). Pass a false value in + /// the optional parameter to defer this initial Open()/Close() cycle. + /// + /// When not inspecting the media file, it's much faster, and useful + /// when you are inflating the object using JSON after instantiation. + QtImageReader(std::string path, bool inspect_reader=true); virtual ~QtImageReader(); diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 2e8d06426..78c2b009a 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -44,13 +44,6 @@ using namespace openshot; -QtImageReader::QtImageReader(std::string path) : path{QString::fromStdString(path)}, is_open(false) -{ - // Open and Close the reader, to populate its attributes (such as height, width, etc...) - Open(); - Close(); -} - QtImageReader::QtImageReader(std::string path, bool inspect_reader) : path{QString::fromStdString(path)}, is_open(false) { // Open and Close the reader, to populate its attributes (such as height, width, etc...) From a6441d822d6e5c4dcff784e3ade27d0134deea66 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:05:29 -0400 Subject: [PATCH 51/86] Reorganize source tree (headers in src/) - src/bindings/ moves to /bindings/ - src/examples/ moves to /examples/ - Contents of include/ merged into src/ with same hierarchy - src/Qt/demo/ moves to examples/qt-demo/ --- {src/bindings => bindings}/CMakeLists.txt | 0 {src/bindings => bindings}/python/CMakeLists.txt | 0 {src/bindings => bindings}/python/openshot.i | 0 {src/bindings => bindings}/ruby/CMakeLists.txt | 0 {src/bindings => bindings}/ruby/openshot.i | 0 {src/examples => examples}/Example.cpp | 0 {src/examples => examples}/Example.py | 0 {src/examples => examples}/Example.rb | 0 {src/examples => examples}/ExampleBlackmagic.cpp | 0 {src/examples => examples}/ExampleHtml.cpp | 0 {src/examples => examples}/ExampleHtml.py | 0 {src/examples => examples}/OpenShot Wipe Tests.py | 0 {src/examples => examples}/back.png | Bin {src/examples => examples}/final-composite.png | Bin {src/examples => examples}/front.png | Bin {src/examples => examples}/front3.png | Bin {src/examples => examples}/interlaced.png | Bin {src/examples => examples}/mask.png | Bin {src/examples => examples}/mask2.png | Bin {src/examples => examples}/output-final.png | Bin {src/examples => examples}/piano-mono.wav | Bin {src/examples => examples}/piano.wav | Bin {src/Qt/demo => examples/qt-demo}/main.cpp | 0 {src/examples => examples}/sintel_trailer-720p.mp4 | Bin {src/examples => examples}/test.mp4 | Bin {src/examples => examples}/test.wav | Bin {src/examples => examples}/test1.mp4 | Bin {include => src}/AudioBufferSource.h | 0 {include => src}/AudioDeviceInfo.h | 0 {include => src}/AudioReaderSource.h | 0 {include => src}/AudioResampler.h | 0 {include => src}/CacheBase.h | 0 {include => src}/CacheDisk.h | 0 {include => src}/CacheMemory.h | 0 {include => src}/ChannelLayouts.h | 0 {include => src}/ChunkReader.h | 0 {include => src}/ChunkWriter.h | 0 {include => src}/Clip.h | 0 {include => src}/ClipBase.h | 0 {include => src}/Color.h | 0 {include => src}/Coordinate.h | 0 {include => src}/CrashHandler.h | 0 {include => src}/DecklinkInput.h | 0 {include => src}/DecklinkOutput.h | 0 {include => src}/DecklinkReader.h | 0 {include => src}/DecklinkWriter.h | 0 {include => src}/DummyReader.h | 0 {include => src}/EffectBase.h | 0 {include => src}/EffectInfo.h | 0 {include => src}/Effects.h | 0 {include => src}/Enums.h | 0 {include => src}/Exceptions.h | 0 {include => src}/FFmpegReader.h | 0 {include => src}/FFmpegUtilities.h | 0 {include => src}/FFmpegWriter.h | 0 {include => src}/Fraction.h | 0 {include => src}/Frame.h | 0 {include => src}/FrameMapper.h | 0 {include => src}/ImageReader.h | 0 {include => src}/ImageWriter.h | 0 {include => src}/Json.h | 0 {include => src}/KeyFrame.h | 0 {include => src}/MagickUtilities.h | 0 {include => src}/OpenMPUtilities.h | 0 {include => src}/OpenShot.h | 0 {include => src}/OpenShotVersion.h.in | 0 {include => src}/PlayerBase.h | 0 {include => src}/Point.h | 0 {include => src}/Profiles.h | 0 {include => src}/Qt/AudioPlaybackThread.h | 0 {include => src}/Qt/PlayerDemo.h | 0 {include => src}/Qt/PlayerPrivate.h | 0 {include => src}/Qt/VideoCacheThread.h | 0 {include => src}/Qt/VideoPlaybackThread.h | 0 {include => src}/Qt/VideoRenderWidget.h | 0 {include => src}/Qt/VideoRenderer.h | 0 {include => src}/QtHtmlReader.h | 0 {include => src}/QtImageReader.h | 0 {include => src}/QtPlayer.h | 0 {include => src}/QtTextReader.h | 0 {include => src}/QtUtilities.h | 0 {include => src}/ReaderBase.h | 0 {include => src}/RendererBase.h | 0 {include => src}/Settings.h | 0 {include => src}/TextReader.h | 0 {include => src}/Timeline.h | 0 {include => src}/WriterBase.h | 0 {include => src}/ZmqLogger.h | 0 {include => src}/effects/Bars.h | 0 {include => src}/effects/Blur.h | 0 {include => src}/effects/Brightness.h | 0 {include => src}/effects/ChromaKey.h | 0 {include => src}/effects/ColorShift.h | 0 {include => src}/effects/Crop.h | 0 {include => src}/effects/Deinterlace.h | 0 {include => src}/effects/Hue.h | 0 {include => src}/effects/Mask.h | 0 {include => src}/effects/Negate.h | 0 {include => src}/effects/Pixelate.h | 0 {include => src}/effects/Saturation.h | 0 {include => src}/effects/Shift.h | 0 {include => src}/effects/Wave.h | 0 102 files changed, 0 insertions(+), 0 deletions(-) rename {src/bindings => bindings}/CMakeLists.txt (100%) rename {src/bindings => bindings}/python/CMakeLists.txt (100%) rename {src/bindings => bindings}/python/openshot.i (100%) rename {src/bindings => bindings}/ruby/CMakeLists.txt (100%) rename {src/bindings => bindings}/ruby/openshot.i (100%) rename {src/examples => examples}/Example.cpp (100%) rename {src/examples => examples}/Example.py (100%) rename {src/examples => examples}/Example.rb (100%) rename {src/examples => examples}/ExampleBlackmagic.cpp (100%) rename {src/examples => examples}/ExampleHtml.cpp (100%) rename {src/examples => examples}/ExampleHtml.py (100%) rename {src/examples => examples}/OpenShot Wipe Tests.py (100%) rename {src/examples => examples}/back.png (100%) rename {src/examples => examples}/final-composite.png (100%) rename {src/examples => examples}/front.png (100%) rename {src/examples => examples}/front3.png (100%) rename {src/examples => examples}/interlaced.png (100%) rename {src/examples => examples}/mask.png (100%) rename {src/examples => examples}/mask2.png (100%) rename {src/examples => examples}/output-final.png (100%) rename {src/examples => examples}/piano-mono.wav (100%) rename {src/examples => examples}/piano.wav (100%) rename {src/Qt/demo => examples/qt-demo}/main.cpp (100%) rename {src/examples => examples}/sintel_trailer-720p.mp4 (100%) rename {src/examples => examples}/test.mp4 (100%) rename {src/examples => examples}/test.wav (100%) rename {src/examples => examples}/test1.mp4 (100%) rename {include => src}/AudioBufferSource.h (100%) rename {include => src}/AudioDeviceInfo.h (100%) rename {include => src}/AudioReaderSource.h (100%) rename {include => src}/AudioResampler.h (100%) rename {include => src}/CacheBase.h (100%) rename {include => src}/CacheDisk.h (100%) rename {include => src}/CacheMemory.h (100%) rename {include => src}/ChannelLayouts.h (100%) rename {include => src}/ChunkReader.h (100%) rename {include => src}/ChunkWriter.h (100%) rename {include => src}/Clip.h (100%) rename {include => src}/ClipBase.h (100%) rename {include => src}/Color.h (100%) rename {include => src}/Coordinate.h (100%) rename {include => src}/CrashHandler.h (100%) rename {include => src}/DecklinkInput.h (100%) rename {include => src}/DecklinkOutput.h (100%) rename {include => src}/DecklinkReader.h (100%) rename {include => src}/DecklinkWriter.h (100%) rename {include => src}/DummyReader.h (100%) rename {include => src}/EffectBase.h (100%) rename {include => src}/EffectInfo.h (100%) rename {include => src}/Effects.h (100%) rename {include => src}/Enums.h (100%) rename {include => src}/Exceptions.h (100%) rename {include => src}/FFmpegReader.h (100%) rename {include => src}/FFmpegUtilities.h (100%) rename {include => src}/FFmpegWriter.h (100%) rename {include => src}/Fraction.h (100%) rename {include => src}/Frame.h (100%) rename {include => src}/FrameMapper.h (100%) rename {include => src}/ImageReader.h (100%) rename {include => src}/ImageWriter.h (100%) rename {include => src}/Json.h (100%) rename {include => src}/KeyFrame.h (100%) rename {include => src}/MagickUtilities.h (100%) rename {include => src}/OpenMPUtilities.h (100%) rename {include => src}/OpenShot.h (100%) rename {include => src}/OpenShotVersion.h.in (100%) rename {include => src}/PlayerBase.h (100%) rename {include => src}/Point.h (100%) rename {include => src}/Profiles.h (100%) rename {include => src}/Qt/AudioPlaybackThread.h (100%) rename {include => src}/Qt/PlayerDemo.h (100%) rename {include => src}/Qt/PlayerPrivate.h (100%) rename {include => src}/Qt/VideoCacheThread.h (100%) rename {include => src}/Qt/VideoPlaybackThread.h (100%) rename {include => src}/Qt/VideoRenderWidget.h (100%) rename {include => src}/Qt/VideoRenderer.h (100%) rename {include => src}/QtHtmlReader.h (100%) rename {include => src}/QtImageReader.h (100%) rename {include => src}/QtPlayer.h (100%) rename {include => src}/QtTextReader.h (100%) rename {include => src}/QtUtilities.h (100%) rename {include => src}/ReaderBase.h (100%) rename {include => src}/RendererBase.h (100%) rename {include => src}/Settings.h (100%) rename {include => src}/TextReader.h (100%) rename {include => src}/Timeline.h (100%) rename {include => src}/WriterBase.h (100%) rename {include => src}/ZmqLogger.h (100%) rename {include => src}/effects/Bars.h (100%) rename {include => src}/effects/Blur.h (100%) rename {include => src}/effects/Brightness.h (100%) rename {include => src}/effects/ChromaKey.h (100%) rename {include => src}/effects/ColorShift.h (100%) rename {include => src}/effects/Crop.h (100%) rename {include => src}/effects/Deinterlace.h (100%) rename {include => src}/effects/Hue.h (100%) rename {include => src}/effects/Mask.h (100%) rename {include => src}/effects/Negate.h (100%) rename {include => src}/effects/Pixelate.h (100%) rename {include => src}/effects/Saturation.h (100%) rename {include => src}/effects/Shift.h (100%) rename {include => src}/effects/Wave.h (100%) diff --git a/src/bindings/CMakeLists.txt b/bindings/CMakeLists.txt similarity index 100% rename from src/bindings/CMakeLists.txt rename to bindings/CMakeLists.txt diff --git a/src/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt similarity index 100% rename from src/bindings/python/CMakeLists.txt rename to bindings/python/CMakeLists.txt diff --git a/src/bindings/python/openshot.i b/bindings/python/openshot.i similarity index 100% rename from src/bindings/python/openshot.i rename to bindings/python/openshot.i diff --git a/src/bindings/ruby/CMakeLists.txt b/bindings/ruby/CMakeLists.txt similarity index 100% rename from src/bindings/ruby/CMakeLists.txt rename to bindings/ruby/CMakeLists.txt diff --git a/src/bindings/ruby/openshot.i b/bindings/ruby/openshot.i similarity index 100% rename from src/bindings/ruby/openshot.i rename to bindings/ruby/openshot.i diff --git a/src/examples/Example.cpp b/examples/Example.cpp similarity index 100% rename from src/examples/Example.cpp rename to examples/Example.cpp diff --git a/src/examples/Example.py b/examples/Example.py similarity index 100% rename from src/examples/Example.py rename to examples/Example.py diff --git a/src/examples/Example.rb b/examples/Example.rb similarity index 100% rename from src/examples/Example.rb rename to examples/Example.rb diff --git a/src/examples/ExampleBlackmagic.cpp b/examples/ExampleBlackmagic.cpp similarity index 100% rename from src/examples/ExampleBlackmagic.cpp rename to examples/ExampleBlackmagic.cpp diff --git a/src/examples/ExampleHtml.cpp b/examples/ExampleHtml.cpp similarity index 100% rename from src/examples/ExampleHtml.cpp rename to examples/ExampleHtml.cpp diff --git a/src/examples/ExampleHtml.py b/examples/ExampleHtml.py similarity index 100% rename from src/examples/ExampleHtml.py rename to examples/ExampleHtml.py diff --git a/src/examples/OpenShot Wipe Tests.py b/examples/OpenShot Wipe Tests.py similarity index 100% rename from src/examples/OpenShot Wipe Tests.py rename to examples/OpenShot Wipe Tests.py diff --git a/src/examples/back.png b/examples/back.png similarity index 100% rename from src/examples/back.png rename to examples/back.png diff --git a/src/examples/final-composite.png b/examples/final-composite.png similarity index 100% rename from src/examples/final-composite.png rename to examples/final-composite.png diff --git a/src/examples/front.png b/examples/front.png similarity index 100% rename from src/examples/front.png rename to examples/front.png diff --git a/src/examples/front3.png b/examples/front3.png similarity index 100% rename from src/examples/front3.png rename to examples/front3.png diff --git a/src/examples/interlaced.png b/examples/interlaced.png similarity index 100% rename from src/examples/interlaced.png rename to examples/interlaced.png diff --git a/src/examples/mask.png b/examples/mask.png similarity index 100% rename from src/examples/mask.png rename to examples/mask.png diff --git a/src/examples/mask2.png b/examples/mask2.png similarity index 100% rename from src/examples/mask2.png rename to examples/mask2.png diff --git a/src/examples/output-final.png b/examples/output-final.png similarity index 100% rename from src/examples/output-final.png rename to examples/output-final.png diff --git a/src/examples/piano-mono.wav b/examples/piano-mono.wav similarity index 100% rename from src/examples/piano-mono.wav rename to examples/piano-mono.wav diff --git a/src/examples/piano.wav b/examples/piano.wav similarity index 100% rename from src/examples/piano.wav rename to examples/piano.wav diff --git a/src/Qt/demo/main.cpp b/examples/qt-demo/main.cpp similarity index 100% rename from src/Qt/demo/main.cpp rename to examples/qt-demo/main.cpp diff --git a/src/examples/sintel_trailer-720p.mp4 b/examples/sintel_trailer-720p.mp4 similarity index 100% rename from src/examples/sintel_trailer-720p.mp4 rename to examples/sintel_trailer-720p.mp4 diff --git a/src/examples/test.mp4 b/examples/test.mp4 similarity index 100% rename from src/examples/test.mp4 rename to examples/test.mp4 diff --git a/src/examples/test.wav b/examples/test.wav similarity index 100% rename from src/examples/test.wav rename to examples/test.wav diff --git a/src/examples/test1.mp4 b/examples/test1.mp4 similarity index 100% rename from src/examples/test1.mp4 rename to examples/test1.mp4 diff --git a/include/AudioBufferSource.h b/src/AudioBufferSource.h similarity index 100% rename from include/AudioBufferSource.h rename to src/AudioBufferSource.h diff --git a/include/AudioDeviceInfo.h b/src/AudioDeviceInfo.h similarity index 100% rename from include/AudioDeviceInfo.h rename to src/AudioDeviceInfo.h diff --git a/include/AudioReaderSource.h b/src/AudioReaderSource.h similarity index 100% rename from include/AudioReaderSource.h rename to src/AudioReaderSource.h diff --git a/include/AudioResampler.h b/src/AudioResampler.h similarity index 100% rename from include/AudioResampler.h rename to src/AudioResampler.h diff --git a/include/CacheBase.h b/src/CacheBase.h similarity index 100% rename from include/CacheBase.h rename to src/CacheBase.h diff --git a/include/CacheDisk.h b/src/CacheDisk.h similarity index 100% rename from include/CacheDisk.h rename to src/CacheDisk.h diff --git a/include/CacheMemory.h b/src/CacheMemory.h similarity index 100% rename from include/CacheMemory.h rename to src/CacheMemory.h diff --git a/include/ChannelLayouts.h b/src/ChannelLayouts.h similarity index 100% rename from include/ChannelLayouts.h rename to src/ChannelLayouts.h diff --git a/include/ChunkReader.h b/src/ChunkReader.h similarity index 100% rename from include/ChunkReader.h rename to src/ChunkReader.h diff --git a/include/ChunkWriter.h b/src/ChunkWriter.h similarity index 100% rename from include/ChunkWriter.h rename to src/ChunkWriter.h diff --git a/include/Clip.h b/src/Clip.h similarity index 100% rename from include/Clip.h rename to src/Clip.h diff --git a/include/ClipBase.h b/src/ClipBase.h similarity index 100% rename from include/ClipBase.h rename to src/ClipBase.h diff --git a/include/Color.h b/src/Color.h similarity index 100% rename from include/Color.h rename to src/Color.h diff --git a/include/Coordinate.h b/src/Coordinate.h similarity index 100% rename from include/Coordinate.h rename to src/Coordinate.h diff --git a/include/CrashHandler.h b/src/CrashHandler.h similarity index 100% rename from include/CrashHandler.h rename to src/CrashHandler.h diff --git a/include/DecklinkInput.h b/src/DecklinkInput.h similarity index 100% rename from include/DecklinkInput.h rename to src/DecklinkInput.h diff --git a/include/DecklinkOutput.h b/src/DecklinkOutput.h similarity index 100% rename from include/DecklinkOutput.h rename to src/DecklinkOutput.h diff --git a/include/DecklinkReader.h b/src/DecklinkReader.h similarity index 100% rename from include/DecklinkReader.h rename to src/DecklinkReader.h diff --git a/include/DecklinkWriter.h b/src/DecklinkWriter.h similarity index 100% rename from include/DecklinkWriter.h rename to src/DecklinkWriter.h diff --git a/include/DummyReader.h b/src/DummyReader.h similarity index 100% rename from include/DummyReader.h rename to src/DummyReader.h diff --git a/include/EffectBase.h b/src/EffectBase.h similarity index 100% rename from include/EffectBase.h rename to src/EffectBase.h diff --git a/include/EffectInfo.h b/src/EffectInfo.h similarity index 100% rename from include/EffectInfo.h rename to src/EffectInfo.h diff --git a/include/Effects.h b/src/Effects.h similarity index 100% rename from include/Effects.h rename to src/Effects.h diff --git a/include/Enums.h b/src/Enums.h similarity index 100% rename from include/Enums.h rename to src/Enums.h diff --git a/include/Exceptions.h b/src/Exceptions.h similarity index 100% rename from include/Exceptions.h rename to src/Exceptions.h diff --git a/include/FFmpegReader.h b/src/FFmpegReader.h similarity index 100% rename from include/FFmpegReader.h rename to src/FFmpegReader.h diff --git a/include/FFmpegUtilities.h b/src/FFmpegUtilities.h similarity index 100% rename from include/FFmpegUtilities.h rename to src/FFmpegUtilities.h diff --git a/include/FFmpegWriter.h b/src/FFmpegWriter.h similarity index 100% rename from include/FFmpegWriter.h rename to src/FFmpegWriter.h diff --git a/include/Fraction.h b/src/Fraction.h similarity index 100% rename from include/Fraction.h rename to src/Fraction.h diff --git a/include/Frame.h b/src/Frame.h similarity index 100% rename from include/Frame.h rename to src/Frame.h diff --git a/include/FrameMapper.h b/src/FrameMapper.h similarity index 100% rename from include/FrameMapper.h rename to src/FrameMapper.h diff --git a/include/ImageReader.h b/src/ImageReader.h similarity index 100% rename from include/ImageReader.h rename to src/ImageReader.h diff --git a/include/ImageWriter.h b/src/ImageWriter.h similarity index 100% rename from include/ImageWriter.h rename to src/ImageWriter.h diff --git a/include/Json.h b/src/Json.h similarity index 100% rename from include/Json.h rename to src/Json.h diff --git a/include/KeyFrame.h b/src/KeyFrame.h similarity index 100% rename from include/KeyFrame.h rename to src/KeyFrame.h diff --git a/include/MagickUtilities.h b/src/MagickUtilities.h similarity index 100% rename from include/MagickUtilities.h rename to src/MagickUtilities.h diff --git a/include/OpenMPUtilities.h b/src/OpenMPUtilities.h similarity index 100% rename from include/OpenMPUtilities.h rename to src/OpenMPUtilities.h diff --git a/include/OpenShot.h b/src/OpenShot.h similarity index 100% rename from include/OpenShot.h rename to src/OpenShot.h diff --git a/include/OpenShotVersion.h.in b/src/OpenShotVersion.h.in similarity index 100% rename from include/OpenShotVersion.h.in rename to src/OpenShotVersion.h.in diff --git a/include/PlayerBase.h b/src/PlayerBase.h similarity index 100% rename from include/PlayerBase.h rename to src/PlayerBase.h diff --git a/include/Point.h b/src/Point.h similarity index 100% rename from include/Point.h rename to src/Point.h diff --git a/include/Profiles.h b/src/Profiles.h similarity index 100% rename from include/Profiles.h rename to src/Profiles.h diff --git a/include/Qt/AudioPlaybackThread.h b/src/Qt/AudioPlaybackThread.h similarity index 100% rename from include/Qt/AudioPlaybackThread.h rename to src/Qt/AudioPlaybackThread.h diff --git a/include/Qt/PlayerDemo.h b/src/Qt/PlayerDemo.h similarity index 100% rename from include/Qt/PlayerDemo.h rename to src/Qt/PlayerDemo.h diff --git a/include/Qt/PlayerPrivate.h b/src/Qt/PlayerPrivate.h similarity index 100% rename from include/Qt/PlayerPrivate.h rename to src/Qt/PlayerPrivate.h diff --git a/include/Qt/VideoCacheThread.h b/src/Qt/VideoCacheThread.h similarity index 100% rename from include/Qt/VideoCacheThread.h rename to src/Qt/VideoCacheThread.h diff --git a/include/Qt/VideoPlaybackThread.h b/src/Qt/VideoPlaybackThread.h similarity index 100% rename from include/Qt/VideoPlaybackThread.h rename to src/Qt/VideoPlaybackThread.h diff --git a/include/Qt/VideoRenderWidget.h b/src/Qt/VideoRenderWidget.h similarity index 100% rename from include/Qt/VideoRenderWidget.h rename to src/Qt/VideoRenderWidget.h diff --git a/include/Qt/VideoRenderer.h b/src/Qt/VideoRenderer.h similarity index 100% rename from include/Qt/VideoRenderer.h rename to src/Qt/VideoRenderer.h diff --git a/include/QtHtmlReader.h b/src/QtHtmlReader.h similarity index 100% rename from include/QtHtmlReader.h rename to src/QtHtmlReader.h diff --git a/include/QtImageReader.h b/src/QtImageReader.h similarity index 100% rename from include/QtImageReader.h rename to src/QtImageReader.h diff --git a/include/QtPlayer.h b/src/QtPlayer.h similarity index 100% rename from include/QtPlayer.h rename to src/QtPlayer.h diff --git a/include/QtTextReader.h b/src/QtTextReader.h similarity index 100% rename from include/QtTextReader.h rename to src/QtTextReader.h diff --git a/include/QtUtilities.h b/src/QtUtilities.h similarity index 100% rename from include/QtUtilities.h rename to src/QtUtilities.h diff --git a/include/ReaderBase.h b/src/ReaderBase.h similarity index 100% rename from include/ReaderBase.h rename to src/ReaderBase.h diff --git a/include/RendererBase.h b/src/RendererBase.h similarity index 100% rename from include/RendererBase.h rename to src/RendererBase.h diff --git a/include/Settings.h b/src/Settings.h similarity index 100% rename from include/Settings.h rename to src/Settings.h diff --git a/include/TextReader.h b/src/TextReader.h similarity index 100% rename from include/TextReader.h rename to src/TextReader.h diff --git a/include/Timeline.h b/src/Timeline.h similarity index 100% rename from include/Timeline.h rename to src/Timeline.h diff --git a/include/WriterBase.h b/src/WriterBase.h similarity index 100% rename from include/WriterBase.h rename to src/WriterBase.h diff --git a/include/ZmqLogger.h b/src/ZmqLogger.h similarity index 100% rename from include/ZmqLogger.h rename to src/ZmqLogger.h diff --git a/include/effects/Bars.h b/src/effects/Bars.h similarity index 100% rename from include/effects/Bars.h rename to src/effects/Bars.h diff --git a/include/effects/Blur.h b/src/effects/Blur.h similarity index 100% rename from include/effects/Blur.h rename to src/effects/Blur.h diff --git a/include/effects/Brightness.h b/src/effects/Brightness.h similarity index 100% rename from include/effects/Brightness.h rename to src/effects/Brightness.h diff --git a/include/effects/ChromaKey.h b/src/effects/ChromaKey.h similarity index 100% rename from include/effects/ChromaKey.h rename to src/effects/ChromaKey.h diff --git a/include/effects/ColorShift.h b/src/effects/ColorShift.h similarity index 100% rename from include/effects/ColorShift.h rename to src/effects/ColorShift.h diff --git a/include/effects/Crop.h b/src/effects/Crop.h similarity index 100% rename from include/effects/Crop.h rename to src/effects/Crop.h diff --git a/include/effects/Deinterlace.h b/src/effects/Deinterlace.h similarity index 100% rename from include/effects/Deinterlace.h rename to src/effects/Deinterlace.h diff --git a/include/effects/Hue.h b/src/effects/Hue.h similarity index 100% rename from include/effects/Hue.h rename to src/effects/Hue.h diff --git a/include/effects/Mask.h b/src/effects/Mask.h similarity index 100% rename from include/effects/Mask.h rename to src/effects/Mask.h diff --git a/include/effects/Negate.h b/src/effects/Negate.h similarity index 100% rename from include/effects/Negate.h rename to src/effects/Negate.h diff --git a/include/effects/Pixelate.h b/src/effects/Pixelate.h similarity index 100% rename from include/effects/Pixelate.h rename to src/effects/Pixelate.h diff --git a/include/effects/Saturation.h b/src/effects/Saturation.h similarity index 100% rename from include/effects/Saturation.h rename to src/effects/Saturation.h diff --git a/include/effects/Shift.h b/src/effects/Shift.h similarity index 100% rename from include/effects/Shift.h rename to src/effects/Shift.h diff --git a/include/effects/Wave.h b/src/effects/Wave.h similarity index 100% rename from include/effects/Wave.h rename to src/effects/Wave.h From aa40ea29e050fe0bca5104a68872dc7401071c8b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 07:43:37 -0400 Subject: [PATCH 52/86] Update include paths in all source/header files --- src/AudioBufferSource.cpp | 2 +- src/AudioReaderSource.cpp | 2 +- src/AudioResampler.cpp | 2 +- src/CacheBase.cpp | 2 +- src/CacheDisk.cpp | 4 ++-- src/CacheMemory.cpp | 2 +- src/ChunkReader.cpp | 6 ++++-- src/ChunkReader.h | 12 +++--------- src/ChunkWriter.cpp | 2 +- src/Clip.cpp | 18 +++++++++--------- src/ClipBase.cpp | 2 +- src/Color.cpp | 2 +- src/Coordinate.cpp | 2 +- src/CrashHandler.cpp | 2 +- src/DecklinkInput.cpp | 2 +- src/DecklinkOutput.cpp | 2 +- src/DecklinkReader.cpp | 2 +- src/DecklinkWriter.cpp | 2 +- src/DummyReader.cpp | 2 +- src/EffectBase.cpp | 2 +- src/EffectInfo.cpp | 3 +-- src/FFmpegReader.cpp | 2 +- src/FFmpegWriter.cpp | 2 +- src/Fraction.cpp | 2 +- src/Frame.cpp | 2 +- src/FrameMapper.cpp | 2 +- src/ImageReader.cpp | 2 +- src/ImageWriter.cpp | 2 +- src/Json.cpp | 2 +- src/KeyFrame.cpp | 2 +- src/PlayerBase.cpp | 2 +- src/Point.cpp | 2 +- src/Profiles.cpp | 2 +- src/Qt/AudioPlaybackThread.cpp | 2 +- src/Qt/PlayerDemo.cpp | 8 +++++--- src/Qt/PlayerPrivate.cpp | 2 +- src/Qt/VideoCacheThread.cpp | 2 +- src/Qt/VideoPlaybackThread.cpp | 2 +- src/Qt/VideoRenderWidget.cpp | 2 +- src/Qt/VideoRenderer.cpp | 2 +- src/QtHtmlReader.cpp | 2 +- src/QtImageReader.cpp | 8 ++++---- src/QtPlayer.cpp | 12 ++++++------ src/QtTextReader.cpp | 2 +- src/ReaderBase.cpp | 2 +- src/RendererBase.cpp | 2 +- src/Settings.cpp | 2 +- src/TextReader.cpp | 2 +- src/Timeline.cpp | 2 +- src/WriterBase.cpp | 2 +- src/ZmqLogger.cpp | 2 +- src/effects/Bars.cpp | 2 +- src/effects/Bars.h | 6 +++--- src/effects/Blur.cpp | 2 +- src/effects/Blur.h | 17 ++++------------- src/effects/Brightness.cpp | 2 +- src/effects/Brightness.h | 16 ++++------------ src/effects/ChromaKey.cpp | 2 +- src/effects/ChromaKey.h | 10 ++++------ src/effects/ColorShift.cpp | 2 +- src/effects/Crop.cpp | 2 +- src/effects/Crop.h | 7 ++++--- src/effects/Deinterlace.cpp | 2 +- src/effects/Deinterlace.h | 9 ++------- src/effects/Hue.cpp | 2 +- src/effects/Hue.h | 7 ++++--- src/effects/Mask.cpp | 7 ++++++- src/effects/Mask.h | 21 +++++---------------- src/effects/Negate.cpp | 2 +- src/effects/Negate.h | 14 ++++++-------- src/effects/Pixelate.cpp | 2 +- src/effects/Pixelate.h | 6 +++--- src/effects/Saturation.cpp | 2 +- src/effects/Shift.cpp | 2 +- src/effects/Shift.h | 7 ++++--- src/effects/Wave.cpp | 2 +- src/effects/Wave.h | 7 ++++--- 77 files changed, 143 insertions(+), 174 deletions(-) diff --git a/src/AudioBufferSource.cpp b/src/AudioBufferSource.cpp index 69e5713ad..6b2bc59e2 100644 --- a/src/AudioBufferSource.cpp +++ b/src/AudioBufferSource.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/AudioBufferSource.h" +#include "AudioBufferSource.h" using namespace std; using namespace openshot; diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp index 999d109d2..b6a01a890 100644 --- a/src/AudioReaderSource.cpp +++ b/src/AudioReaderSource.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/AudioReaderSource.h" +#include "AudioReaderSource.h" using namespace std; using namespace openshot; diff --git a/src/AudioResampler.cpp b/src/AudioResampler.cpp index 145f5d91f..8c27043e5 100644 --- a/src/AudioResampler.cpp +++ b/src/AudioResampler.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/AudioResampler.h" +#include "AudioResampler.h" using namespace std; using namespace openshot; diff --git a/src/CacheBase.cpp b/src/CacheBase.cpp index bc57f3f45..72381792c 100644 --- a/src/CacheBase.cpp +++ b/src/CacheBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/CacheBase.h" +#include "CacheBase.h" using namespace std; using namespace openshot; diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index b3fd119fd..c252d0b25 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -28,8 +28,8 @@ * along with OpenShot Library. If not, see . */ -#include "../include/CacheDisk.h" -#include "../include/QtUtilities.h" +#include "CacheDisk.h" +#include "QtUtilities.h" #include #include #include diff --git a/src/CacheMemory.cpp b/src/CacheMemory.cpp index 70feef03d..685c86ecf 100644 --- a/src/CacheMemory.cpp +++ b/src/CacheMemory.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/CacheMemory.h" +#include "CacheMemory.h" using namespace std; using namespace openshot; diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp index c194ce33a..5301c6874 100644 --- a/src/ChunkReader.cpp +++ b/src/ChunkReader.cpp @@ -28,8 +28,10 @@ * along with OpenShot Library. If not, see . */ -#include "../include/ChunkReader.h" -#include "../include/FFmpegReader.h" +#include "ChunkReader.h" +#include "FFmpegReader.h" + +#include using namespace openshot; diff --git a/src/ChunkReader.h b/src/ChunkReader.h index 50b260838..7a041f714 100644 --- a/src/ChunkReader.h +++ b/src/ChunkReader.h @@ -32,18 +32,12 @@ #define OPENSHOT_CHUNK_READER_H #include "ReaderBase.h" -#include -#include -#include -#include -#include -#include -#include +#include #include -#include + +#include "Frame.h" #include "Json.h" #include "CacheMemory.h" -#include "Exceptions.h" namespace openshot { diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp index c7752cd57..bbb10be91 100644 --- a/src/ChunkWriter.cpp +++ b/src/ChunkWriter.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/ChunkWriter.h" +#include "ChunkWriter.h" using namespace openshot; diff --git a/src/Clip.cpp b/src/Clip.cpp index 1f7382828..f3660f0a8 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -28,17 +28,17 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Clip.h" -#include "../include/FFmpegReader.h" -#include "../include/FrameMapper.h" +#include "Clip.h" +#include "FFmpegReader.h" +#include "FrameMapper.h" #ifdef USE_IMAGEMAGICK - #include "../include/ImageReader.h" - #include "../include/TextReader.h" + #include "ImageReader.h" + #include "TextReader.h" #endif -#include "../include/QtImageReader.h" -#include "../include/ChunkReader.h" -#include "../include/DummyReader.h" -#include "../include/Timeline.h" +#include "QtImageReader.h" +#include "ChunkReader.h" +#include "DummyReader.h" +#include "Timeline.h" using namespace openshot; diff --git a/src/ClipBase.cpp b/src/ClipBase.cpp index a51c65735..f76959046 100644 --- a/src/ClipBase.cpp +++ b/src/ClipBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/ClipBase.h" +#include "ClipBase.h" using namespace openshot; diff --git a/src/Color.cpp b/src/Color.cpp index 705ece4a1..622a912ca 100644 --- a/src/Color.cpp +++ b/src/Color.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Color.h" +#include "Color.h" using namespace openshot; diff --git a/src/Coordinate.cpp b/src/Coordinate.cpp index f87fb7a0e..0d4f577a6 100644 --- a/src/Coordinate.cpp +++ b/src/Coordinate.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Coordinate.h" +#include "Coordinate.h" using namespace std; using namespace openshot; diff --git a/src/CrashHandler.cpp b/src/CrashHandler.cpp index 1782f5bab..7b6c4e7e6 100644 --- a/src/CrashHandler.cpp +++ b/src/CrashHandler.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/CrashHandler.h" +#include "CrashHandler.h" using namespace std; using namespace openshot; diff --git a/src/DecklinkInput.cpp b/src/DecklinkInput.cpp index b03ad8e4a..21c4f7bb6 100644 --- a/src/DecklinkInput.cpp +++ b/src/DecklinkInput.cpp @@ -53,7 +53,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/DecklinkInput.h" +#include "DecklinkInput.h" using namespace std; diff --git a/src/DecklinkOutput.cpp b/src/DecklinkOutput.cpp index 2dee7e9e3..13da185f4 100644 --- a/src/DecklinkOutput.cpp +++ b/src/DecklinkOutput.cpp @@ -53,7 +53,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/DecklinkOutput.h" +#include "DecklinkOutput.h" using namespace std; diff --git a/src/DecklinkReader.cpp b/src/DecklinkReader.cpp index 14c2f87ca..bb31f316d 100644 --- a/src/DecklinkReader.cpp +++ b/src/DecklinkReader.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/DecklinkReader.h" +#include "DecklinkReader.h" using namespace openshot; diff --git a/src/DecklinkWriter.cpp b/src/DecklinkWriter.cpp index 3eafda1cf..1bfb59a8d 100644 --- a/src/DecklinkWriter.cpp +++ b/src/DecklinkWriter.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/DecklinkWriter.h" +#include "DecklinkWriter.h" using namespace openshot; diff --git a/src/DummyReader.cpp b/src/DummyReader.cpp index 8b6f752f7..c295c2985 100644 --- a/src/DummyReader.cpp +++ b/src/DummyReader.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/DummyReader.h" +#include "DummyReader.h" using namespace openshot; diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index 05ed97c2a..f1d7d0301 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/EffectBase.h" +#include "EffectBase.h" using namespace openshot; diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 6829f4eb5..1359238d5 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -28,8 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/EffectInfo.h" - +#include "EffectInfo.h" using namespace openshot; diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 5b7b7ddd4..f17e03591 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -31,7 +31,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/FFmpegReader.h" +#include "FFmpegReader.h" #include // for std::this_thread::sleep_for #include // for std::chrono::milliseconds diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 76bc98905..e5e92fd4f 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -31,7 +31,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/FFmpegWriter.h" +#include "FFmpegWriter.h" #include diff --git a/src/Fraction.cpp b/src/Fraction.cpp index c9cdad55d..869ef35ff 100644 --- a/src/Fraction.cpp +++ b/src/Fraction.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Fraction.h" +#include "Fraction.h" using namespace openshot; diff --git a/src/Frame.cpp b/src/Frame.cpp index 8be002466..3bae8854b 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Frame.h" +#include "Frame.h" #include "JuceHeader.h" #include diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index 8eff6e702..85c6bfdfc 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/FrameMapper.h" +#include "FrameMapper.h" using namespace std; using namespace openshot; diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index 9a001879f..1b7c1af0d 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -31,7 +31,7 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "../include/ImageReader.h" +#include "ImageReader.h" using namespace openshot; diff --git a/src/ImageWriter.cpp b/src/ImageWriter.cpp index 1b7a01e9b..275ea2703 100644 --- a/src/ImageWriter.cpp +++ b/src/ImageWriter.cpp @@ -34,7 +34,7 @@ //Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "../include/ImageWriter.h" +#include "ImageWriter.h" using namespace openshot; diff --git a/src/Json.cpp b/src/Json.cpp index 0c83d9d62..9d6d9ba4d 100644 --- a/src/Json.cpp +++ b/src/Json.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Json.h" +#include "Json.h" const Json::Value openshot::stringToJson(const std::string value) { diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index 57e424cf7..b113e6325 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/KeyFrame.h" +#include "KeyFrame.h" #include #include #include diff --git a/src/PlayerBase.cpp b/src/PlayerBase.cpp index f152fbbb9..844b8d605 100644 --- a/src/PlayerBase.cpp +++ b/src/PlayerBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/PlayerBase.h" +#include "PlayerBase.h" using namespace openshot; diff --git a/src/Point.cpp b/src/Point.cpp index 136799778..4636b437d 100644 --- a/src/Point.cpp +++ b/src/Point.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Point.h" +#include "Point.h" using namespace std; using namespace openshot; diff --git a/src/Profiles.cpp b/src/Profiles.cpp index 5351520e9..167affe2c 100644 --- a/src/Profiles.cpp +++ b/src/Profiles.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Profiles.h" +#include "Profiles.h" using namespace openshot; diff --git a/src/Qt/AudioPlaybackThread.cpp b/src/Qt/AudioPlaybackThread.cpp index 2ee1009c5..178642f6d 100644 --- a/src/Qt/AudioPlaybackThread.cpp +++ b/src/Qt/AudioPlaybackThread.cpp @@ -29,7 +29,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/AudioPlaybackThread.h" +#include "AudioPlaybackThread.h" #include // for std::this_thread::sleep_for #include // for std::chrono::milliseconds diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp index e5f0e11d0..e8751a629 100644 --- a/src/Qt/PlayerDemo.cpp +++ b/src/Qt/PlayerDemo.cpp @@ -28,9 +28,11 @@ * along with OpenShot Library. If not, see . */ -#include -#include "../../include/QtPlayer.h" -#include "../../include/Qt/PlayerDemo.h" +#include + +#include "PlayerDemo.h" +#include "../QtPlayer.h" + #include #include #include diff --git a/src/Qt/PlayerPrivate.cpp b/src/Qt/PlayerPrivate.cpp index db6f2ff10..75052fc31 100644 --- a/src/Qt/PlayerPrivate.cpp +++ b/src/Qt/PlayerPrivate.cpp @@ -29,7 +29,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/PlayerPrivate.h" +#include "PlayerPrivate.h" #include // for std::this_thread::sleep_for #include // for std::chrono milliseconds, high_resolution_clock diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp index 4279e2381..f817c93f3 100644 --- a/src/Qt/VideoCacheThread.cpp +++ b/src/Qt/VideoCacheThread.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/VideoCacheThread.h" +#include "VideoCacheThread.h" #include #include // for std::this_thread::sleep_for diff --git a/src/Qt/VideoPlaybackThread.cpp b/src/Qt/VideoPlaybackThread.cpp index f1cff7565..01e2871bd 100644 --- a/src/Qt/VideoPlaybackThread.cpp +++ b/src/Qt/VideoPlaybackThread.cpp @@ -29,7 +29,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/VideoPlaybackThread.h" +#include "VideoPlaybackThread.h" namespace openshot { diff --git a/src/Qt/VideoRenderWidget.cpp b/src/Qt/VideoRenderWidget.cpp index 4af1ac6ab..528983720 100644 --- a/src/Qt/VideoRenderWidget.cpp +++ b/src/Qt/VideoRenderWidget.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/VideoRenderWidget.h" +#include "VideoRenderWidget.h" #include #include #include diff --git a/src/Qt/VideoRenderer.cpp b/src/Qt/VideoRenderer.cpp index 8d0e3a1d3..e70e85451 100644 --- a/src/Qt/VideoRenderer.cpp +++ b/src/Qt/VideoRenderer.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/Qt/VideoRenderer.h" +#include "VideoRenderer.h" VideoRenderer::VideoRenderer(QObject *parent) diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 4925d3a88..b40041ac4 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -30,7 +30,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/QtHtmlReader.h" +#include "QtHtmlReader.h" #include #include #include diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 2e8d06426..281cb3630 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -28,10 +28,10 @@ * along with OpenShot Library. If not, see . */ -#include "../include/QtImageReader.h" -#include "../include/Settings.h" -#include "../include/Clip.h" -#include "../include/CacheMemory.h" +#include "QtImageReader.h" +#include "Settings.h" +#include "Clip.h" +#include "CacheMemory.h" #include #include #include diff --git a/src/QtPlayer.cpp b/src/QtPlayer.cpp index e4d862d19..345bc24f0 100644 --- a/src/QtPlayer.cpp +++ b/src/QtPlayer.cpp @@ -29,12 +29,12 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Clip.h" -#include "../include/FFmpegReader.h" -#include "../include/Timeline.h" -#include "../include/QtPlayer.h" -#include "../include/Qt/PlayerPrivate.h" -#include "../include/Qt/VideoRenderer.h" +#include "Clip.h" +#include "FFmpegReader.h" +#include "Timeline.h" +#include "QtPlayer.h" +#include "Qt/PlayerPrivate.h" +#include "Qt/VideoRenderer.h" namespace openshot { diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index bd157ebd2..2c3d1c3ac 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -30,7 +30,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/QtTextReader.h" +#include "QtTextReader.h" #include #include diff --git a/src/ReaderBase.cpp b/src/ReaderBase.cpp index 6799d95ec..220925d7a 100644 --- a/src/ReaderBase.cpp +++ b/src/ReaderBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/ReaderBase.h" +#include "ReaderBase.h" using namespace openshot; diff --git a/src/RendererBase.cpp b/src/RendererBase.cpp index b2bea40e5..ca9ad26ec 100644 --- a/src/RendererBase.cpp +++ b/src/RendererBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/RendererBase.h" +#include "RendererBase.h" using namespace openshot; RendererBase::RendererBase() diff --git a/src/Settings.cpp b/src/Settings.cpp index e48fd981c..e3f44aed8 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Settings.h" +#include "Settings.h" using namespace std; using namespace openshot; diff --git a/src/TextReader.cpp b/src/TextReader.cpp index be8c7375f..0407460a2 100644 --- a/src/TextReader.cpp +++ b/src/TextReader.cpp @@ -31,7 +31,7 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "../include/TextReader.h" +#include "TextReader.h" using namespace openshot; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 9ccf9a84e..71ab8efa2 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/Timeline.h" +#include "Timeline.h" using namespace openshot; diff --git a/src/WriterBase.cpp b/src/WriterBase.cpp index 388219179..9755c61d5 100644 --- a/src/WriterBase.cpp +++ b/src/WriterBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/WriterBase.h" +#include "WriterBase.h" using namespace openshot; diff --git a/src/ZmqLogger.cpp b/src/ZmqLogger.cpp index b72992308..8d72f352e 100644 --- a/src/ZmqLogger.cpp +++ b/src/ZmqLogger.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/ZmqLogger.h" +#include "ZmqLogger.h" #if USE_RESVG == 1 #include "ResvgQt.h" diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index e653b7dda..6b3195691 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Bars.h" +#include "Bars.h" using namespace openshot; diff --git a/src/effects/Bars.h b/src/effects/Bars.h index 7c92255aa..c2e24f738 100644 --- a/src/effects/Bars.h +++ b/src/effects/Bars.h @@ -33,13 +33,13 @@ #include "../EffectBase.h" -#include -#include -#include #include "../Color.h" +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include namespace openshot { diff --git a/src/effects/Blur.cpp b/src/effects/Blur.cpp index 6ddba88e6..c7e36b7bc 100644 --- a/src/effects/Blur.cpp +++ b/src/effects/Blur.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Blur.h" +#include "Blur.h" using namespace openshot; diff --git a/src/effects/Blur.h b/src/effects/Blur.h index 60a0cd085..59b75fff7 100644 --- a/src/effects/Blur.h +++ b/src/effects/Blur.h @@ -33,21 +33,12 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include -#include -#include -#include "../Color.h" -#include "../Exceptions.h" +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" -#include "../ReaderBase.h" -#include "../FFmpegReader.h" -#include "../QtImageReader.h" -#include "../ChunkReader.h" + +#include +#include namespace openshot { diff --git a/src/effects/Brightness.cpp b/src/effects/Brightness.cpp index 321a19a78..d73de8993 100644 --- a/src/effects/Brightness.cpp +++ b/src/effects/Brightness.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Brightness.h" +#include "Brightness.h" using namespace openshot; diff --git a/src/effects/Brightness.h b/src/effects/Brightness.h index 5f25b94a0..228600685 100644 --- a/src/effects/Brightness.h +++ b/src/effects/Brightness.h @@ -33,20 +33,12 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include -#include -#include "../Color.h" -#include "../Exceptions.h" +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" -#include "../ReaderBase.h" -#include "../FFmpegReader.h" -#include "../QtImageReader.h" -#include "../ChunkReader.h" + +#include +#include namespace openshot { diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp index dbb56a693..0f4d626e5 100644 --- a/src/effects/ChromaKey.cpp +++ b/src/effects/ChromaKey.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/ChromaKey.h" +#include "ChromaKey.h" using namespace openshot; diff --git a/src/effects/ChromaKey.h b/src/effects/ChromaKey.h index fcc8c3b1f..7c2676425 100644 --- a/src/effects/ChromaKey.h +++ b/src/effects/ChromaKey.h @@ -33,16 +33,14 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include -#include #include "../Color.h" +#include "../Frame.h" #include "../Exceptions.h" #include "../KeyFrame.h" +#include +#include + namespace openshot { diff --git a/src/effects/ColorShift.cpp b/src/effects/ColorShift.cpp index 7ec62e14c..361f852d8 100644 --- a/src/effects/ColorShift.cpp +++ b/src/effects/ColorShift.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/ColorShift.h" +#include "ColorShift.h" using namespace openshot; diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index f0e0aa953..02e3f17c1 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Crop.h" +#include "Crop.h" using namespace openshot; diff --git a/src/effects/Crop.h b/src/effects/Crop.h index f5ba07f24..2b746a016 100644 --- a/src/effects/Crop.h +++ b/src/effects/Crop.h @@ -33,13 +33,14 @@ #include "../EffectBase.h" -#include -#include -#include #include "../Color.h" +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include + namespace openshot { diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 984336aa0..36014349f 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Deinterlace.h" +#include "Deinterlace.h" using namespace openshot; diff --git a/src/effects/Deinterlace.h b/src/effects/Deinterlace.h index 97c778538..84212d676 100644 --- a/src/effects/Deinterlace.h +++ b/src/effects/Deinterlace.h @@ -33,14 +33,9 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include +#include #include -#include "../Color.h" -#include "../Exceptions.h" +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" diff --git a/src/effects/Hue.cpp b/src/effects/Hue.cpp index eb4808e31..35ee37098 100644 --- a/src/effects/Hue.cpp +++ b/src/effects/Hue.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Hue.h" +#include "Hue.h" using namespace openshot; diff --git a/src/effects/Hue.h b/src/effects/Hue.h index 833bf087b..039aa3a84 100644 --- a/src/effects/Hue.h +++ b/src/effects/Hue.h @@ -33,12 +33,13 @@ #include "../EffectBase.h" -#include -#include -#include +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include + namespace openshot { diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index f270e4109..9fbc18245 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -28,7 +28,12 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Mask.h" +#include "Mask.h" +#include "FFmpegReader.h" +#ifdef USE_IMAGEMAGICK + #include "ImageReader.h" +#endif +#include "ReaderBase.h" using namespace openshot; diff --git a/src/effects/Mask.h b/src/effects/Mask.h index 8156b8436..1a76ea473 100644 --- a/src/effects/Mask.h +++ b/src/effects/Mask.h @@ -33,27 +33,16 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include -#include -#include "../Color.h" -#include "../Exceptions.h" #include "../Json.h" #include "../KeyFrame.h" -#include "../ReaderBase.h" -#include "../FFmpegReader.h" -#include "../QtImageReader.h" -#include "../ChunkReader.h" -#ifdef USE_IMAGEMAGICK - #include "../MagickUtilities.h" - #include "../ImageReader.h" -#endif + +#include +#include namespace openshot { + // Forward declaration + class ReaderBase; /** * @brief This class uses the image libraries to apply alpha (or transparency) masks diff --git a/src/effects/Negate.cpp b/src/effects/Negate.cpp index 18a5d194c..9cffd613b 100644 --- a/src/effects/Negate.cpp +++ b/src/effects/Negate.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Negate.h" +#include "Negate.h" using namespace openshot; diff --git a/src/effects/Negate.h b/src/effects/Negate.h index c691a86c1..c3a67efd5 100644 --- a/src/effects/Negate.h +++ b/src/effects/Negate.h @@ -33,16 +33,14 @@ #include "../EffectBase.h" -#include -#include -#include -#include -#include -#include -#include "../Color.h" -#include "../Exceptions.h" +#include "../Frame.h" #include "../KeyFrame.h" +#include +#include + +#include + namespace openshot { diff --git a/src/effects/Pixelate.cpp b/src/effects/Pixelate.cpp index 057b28e29..15c416fbb 100644 --- a/src/effects/Pixelate.cpp +++ b/src/effects/Pixelate.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Pixelate.h" +#include "Pixelate.h" #include "Json.h" #include diff --git a/src/effects/Pixelate.h b/src/effects/Pixelate.h index 793f4d46f..112231502 100644 --- a/src/effects/Pixelate.h +++ b/src/effects/Pixelate.h @@ -33,12 +33,12 @@ #include "../EffectBase.h" -#include -#include -#include +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include namespace openshot { diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index cad9c08d7..df2ddad3b 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Saturation.h" +#include "Saturation.h" using namespace openshot; diff --git a/src/effects/Shift.cpp b/src/effects/Shift.cpp index ee5815912..e591a1715 100644 --- a/src/effects/Shift.cpp +++ b/src/effects/Shift.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Shift.h" +#include "Shift.h" using namespace openshot; diff --git a/src/effects/Shift.h b/src/effects/Shift.h index 7a7efbea4..86e4ad37c 100644 --- a/src/effects/Shift.h +++ b/src/effects/Shift.h @@ -33,12 +33,13 @@ #include "../EffectBase.h" -#include -#include -#include +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include + namespace openshot { diff --git a/src/effects/Wave.cpp b/src/effects/Wave.cpp index 499fc9588..1e796d4c0 100644 --- a/src/effects/Wave.cpp +++ b/src/effects/Wave.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Wave.h" +#include "Wave.h" using namespace openshot; diff --git a/src/effects/Wave.h b/src/effects/Wave.h index d4759c24e..7f6e37b70 100644 --- a/src/effects/Wave.h +++ b/src/effects/Wave.h @@ -33,12 +33,13 @@ #include "../EffectBase.h" -#include -#include -#include +#include "../Frame.h" #include "../Json.h" #include "../KeyFrame.h" +#include +#include + namespace openshot { From 990155ad93add5e445df3a6de43316e110f2812b Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 07:46:11 -0400 Subject: [PATCH 53/86] Update src/CMakeLists.txt for source tree reorg --- src/CMakeLists.txt | 104 ++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 73 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04121c225..e4b99567c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,7 +85,7 @@ if(ENABLE_MAGICK) # define a global var (used in the C++) add_definitions( -DUSE_IMAGEMAGICK=1 ) - list(APPEND CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") + list(APPEND CMAKE_SWIG_FLAGS -DUSE_IMAGEMAGICK=1) set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE) mark_as_advanced(HAVE_IMAGEMAGICK) @@ -98,24 +98,6 @@ find_package(OpenShotAudio 0.2.0 REQUIRED) # Include Juce headers (needed for compile) include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS}) - -################# BLACKMAGIC DECKLINK ################### -# Find BlackMagic DeckLinkAPI libraries -if (ENABLE_BLACKMAGIC) - - find_package(BlackMagic) - - if (BLACKMAGIC_FOUND) - # Include Blackmagic headers (needed for compile) - include_directories(${BLACKMAGIC_INCLUDE_DIR}) - - # define a global var (used in the C++) - add_definitions( -DUSE_BLACKMAGIC=1 ) - list(APPEND CMAKE_SWIG_FLAGS "-DUSE_BLACKMAGIC=1") - endif() - -endif() - ############### PROFILING ################# #set(PROFILER "/usr/lib/libprofiler.so.0.3.2") #set(PROFILER "/usr/lib/libtcmalloc.so.4") @@ -207,9 +189,6 @@ set(QT_PLAYER_SOURCES Qt/VideoRenderer.cpp Qt/VideoRenderWidget.cpp) -# Get list of MOC'able headers -file(GLOB_RECURSE OPENSHOT_QT_HEADERS ${CMAKE_SOURCE_DIR}/include/Qt/*.h) - # Disable RPATH set(CMAKE_MACOSX_RPATH 0) @@ -221,7 +200,6 @@ target_sources(openshot PRIVATE ${OPENSHOT_SOURCES} ${EFFECTS_SOURCES} ${QT_PLAYER_SOURCES} - ${OPENSHOT_QT_HEADERS} ) # Set SONAME and other library properties @@ -230,7 +208,7 @@ set_target_properties(openshot PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_SO_VERSION} INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" - ) +) # Add optional ImageMagic-dependent sources if(ImageMagick_FOUND) @@ -252,11 +230,11 @@ endif() # Location of our includes, both internally and when installed target_include_directories(openshot PRIVATE - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} PUBLIC - $ - $ + $ + $ $) @@ -378,12 +356,26 @@ if (TARGET RESVG::resvg) #include_directories(${RESVG_INCLUDE_DIRS}) target_link_libraries(openshot PUBLIC RESVG::resvg) - target_compile_definitions(openshot PUBLIC "-DUSE_RESVG=1") - list(APPEND CMAKE_SWIG_FLAGS "-DUSE_RESVG=1") + target_compile_definitions(openshot PUBLIC -DUSE_RESVG=1) + list(APPEND CMAKE_SWIG_FLAGS -DUSE_RESVG=1) set(HAVE_RESVG TRUE CACHE BOOL "Building with Resvg support" FORCE) mark_as_advanced(HAVE_RESVG) +endif() + +################# BLACKMAGIC DECKLINK ################### +# Find BlackMagic DeckLinkAPI libraries +if (ENABLE_BLACKMAGIC) + find_package(BlackMagic) + + if (BLACKMAGIC_FOUND) + # Include Blackmagic headers (needed for compile) + target_include_directories(openshot PRIVATE ${BLACKMAGIC_INCLUDE_DIR}) + # define a preprocessor macro (used in the C++) + target_compile_definitions(openshot PUBLIC -DUSE_BLACKMAGIC=1) + list(APPEND CMAKE_SWIG_FLAGS -DUSE_BLACKMAGIC=1) + endif() endif() ############### LINK LIBRARY ################# @@ -405,56 +397,22 @@ if(WIN32) target_link_libraries(openshot PUBLIC "imagehlp" "dbghelp" ) endif() - -############### CLI EXECUTABLES ################ -# Create test executable -add_executable(openshot-example examples/Example.cpp) - -# Define path to test input files -set(TEST_MEDIA_PATH "${PROJECT_SOURCE_DIR}/src/examples/") -if (WIN32) - string(REPLACE "/" "\\\\" TEST_MEDIA_PATH TEST_MEDIA_PATH) -endif() -target_compile_definitions(openshot-example PRIVATE - -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) - -# Link test executable to the new library -target_link_libraries(openshot-example openshot) - -add_executable(openshot-html-test examples/ExampleHtml.cpp) -target_link_libraries(openshot-html-test openshot Qt5::Gui) - -############### PLAYER EXECUTABLE ################ -# Create test executable -add_executable(openshot-player Qt/demo/main.cpp) - -# Link test executable to the new library -target_link_libraries(openshot-player openshot) - -############### TEST BLACKMAGIC CAPTURE APP ################ -if (BLACKMAGIC_FOUND) - # Create test executable - add_executable(openshot-blackmagic - examples/ExampleBlackmagic.cpp) - - # Link test executable to the new library - target_link_libraries(openshot-blackmagic openshot) -endif() - -############### INCLUDE SWIG BINDINGS ################ -add_subdirectory(bindings) - -############### INSTALL HEADERS & LIBRARY ################ +### +### INSTALL HEADERS & LIBRARY +### # Install primary library install(TARGETS openshot ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ +install( + DIRECTORY . DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot - FILES_MATCHING PATTERN "*.h") + FILES_MATCHING PATTERN "*.h" +) ############### CPACK PACKAGING ############## if(MINGW) From 630e7ac4df172608aa26fb45f068caaf491d3981 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 07:46:53 -0400 Subject: [PATCH 54/86] Use new OpenShot::Audio target in CMakeLists.txt FindOpenShotAudio.cmake: Enhance with targets - Also, migrate as much config as possible from CMakeLists.txt files to properties of IMPORTED OpenShot::Audio target (including platform- specific configs) --- cmake/Modules/FindOpenShotAudio.cmake | 101 ++++++++++++++++++++------ src/CMakeLists.txt | 35 ++------- tests/CMakeLists.txt | 20 ----- 3 files changed, 87 insertions(+), 69 deletions(-) diff --git a/cmake/Modules/FindOpenShotAudio.cmake b/cmake/Modules/FindOpenShotAudio.cmake index a0cb9200c..0683a864a 100644 --- a/cmake/Modules/FindOpenShotAudio.cmake +++ b/cmake/Modules/FindOpenShotAudio.cmake @@ -11,12 +11,14 @@ endif() # Find the libopenshot-audio header files (check env/cache vars first) find_path( - LIBOPENSHOT_AUDIO_INCLUDE_DIR + OpenShotAudio_INCLUDE_DIR JuceHeader.h HINTS ENV LIBOPENSHOT_AUDIO_DIR PATHS ${LIBOPENSHOT_AUDIO_DIR} + ${OpenShotAudio_ROOT} + ${OpenShotAudio_INCLUDE_DIR} PATH_SUFFIXES include/libopenshot-audio libopenshot-audio @@ -26,12 +28,14 @@ find_path( # Find the libopenshot-audio header files (fallback to std. paths) find_path( - LIBOPENSHOT_AUDIO_INCLUDE_DIR + OpenShotAudio_INCLUDE_DIR JuceHeader.h HINTS ENV LIBOPENSHOT_AUDIO_DIR PATHS ${LIBOPENSHOT_AUDIO_DIR} + ${OpenShotAudio_ROOT} + ${OpenShotAudio_INCLUDE_DIR} PATH_SUFFIXES include/libopenshot-audio libopenshot-audio @@ -40,7 +44,7 @@ find_path( # Find libopenshot-audio.so / libopenshot-audio.dll (check env/cache vars first) find_library( - LIBOPENSHOT_AUDIO_LIBRARY + OpenShotAudio_LIBRARY NAMES libopenshot-audio openshot-audio @@ -48,6 +52,8 @@ find_library( ENV LIBOPENSHOT_AUDIO_DIR PATHS ${LIBOPENSHOT_AUDIO_DIR} + ${OpenShotAudio_ROOT} + ${OpenShotAudio_LIBRARY} PATH_SUFFIXES lib/libopenshot-audio libopenshot-audio @@ -57,7 +63,7 @@ find_library( # Find libopenshot-audio.so / libopenshot-audio.dll (fallback) find_library( - LIBOPENSHOT_AUDIO_LIBRARY + OpenShotAudio_LIBRARY NAMES libopenshot-audio openshot-audio @@ -65,39 +71,41 @@ find_library( ENV LIBOPENSHOT_AUDIO_DIR PATHS ${LIBOPENSHOT_AUDIO_DIR} + ${OpenShotAudio_ROOT} + ${OpenShotAudio_LIBRARY} PATH_SUFFIXES lib/libopenshot-audio libopenshot-audio lib ) -set(LIBOPENSHOT_AUDIO_LIBRARIES "${LIBOPENSHOT_AUDIO_LIBRARY}") -set(LIBOPENSHOT_AUDIO_LIBRARY "${LIBOPENSHOT_AUDIO_LIBRARIES}") -set(LIBOPENSHOT_AUDIO_INCLUDE_DIRS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}") +set(OpenShotAudio_LIBRARIES "${OpenShotAudio_LIBRARY}") +set(OpenShotAudio_LIBRARY "${OpenShotAudio_LIBRARIES}") +set(OpenShotAudio_INCLUDE_DIRS "${OpenShotAudio_INCLUDE_DIR}") -if(LIBOPENSHOT_AUDIO_INCLUDE_DIR AND EXISTS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}/JuceHeader.h") - file(STRINGS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}/JuceHeader.h" libosa_version_str +if(OpenShotAudio_INCLUDE_DIR AND EXISTS "${OpenShotAudio_INCLUDE_DIR}/JuceHeader.h") + file(STRINGS "${OpenShotAudio_INCLUDE_DIR}/JuceHeader.h" libosa_version_str REGEX "versionString.*=.*\"[^\"]+\"") if(libosa_version_str MATCHES "versionString.*=.*\"([^\"]+)\"") - set(LIBOPENSHOT_AUDIO_VERSION_STRING ${CMAKE_MATCH_1}) + set(OpenShotAudio_VERSION_STRING ${CMAKE_MATCH_1}) endif() unset(libosa_version_str) string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+).*$" "\\1" - LIBOPENSHOT_AUDIO_VERSION "${LIBOPENSHOT_AUDIO_VERSION_STRING}") + OpenShotAudio_VERSION "${OpenShotAudio_VERSION_STRING}") endif() # If we couldn't parse M.N.B version, don't keep any of it -if(NOT LIBOPENSHOT_AUDIO_VERSION) - unset(LIBOPENSHOT_AUDIO_VERSION) - unset(LIBOPENSHOT_AUDIO_VERSION_STRING) +if(NOT OpenShotAudio_VERSION) + unset(OpenShotAudio_VERSION) + unset(OpenShotAudio_VERSION_STRING) endif() # Determine compatibility with requested version in find_package() -if(OpenShotAudio_FIND_VERSION AND LIBOPENSHOT_AUDIO_VERSION) - if("${OpenShotAudio_FIND_VERSION}" STREQUAL "${LIBOPENSHOT_AUDIO_VERSION}") +if(OpenShotAudio_FIND_VERSION AND OpenShotAudio_VERSION) + if("${OpenShotAudio_FIND_VERSION}" STREQUAL "${OpenShotAudio_VERSION}") set(OpenShotAudio_VERSION_EXACT TRUE) endif() - if("${OpenShotAudio_FIND_VERSION}" VERSION_GREATER "${LIBOPENSHOT_AUDIO_VERSION}") + if("${OpenShotAudio_FIND_VERSION}" VERSION_GREATER "${OpenShotAudio_VERSION}") set(OpenShotAudio_VERSION_COMPATIBLE FALSE) else() set(OpenShotAudio_VERSION_COMPATIBLE TRUE) @@ -105,12 +113,63 @@ if(OpenShotAudio_FIND_VERSION AND LIBOPENSHOT_AUDIO_VERSION) endif() include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LIBOPENSHOT_AUDIO_FOUND to TRUE +# handle the QUIETLY and REQUIRED arguments and set OpenShotAudio_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(OpenShotAudio REQUIRED_VARS - LIBOPENSHOT_AUDIO_LIBRARY - LIBOPENSHOT_AUDIO_INCLUDE_DIRS + OpenShotAudio_LIBRARIES + OpenShotAudio_INCLUDE_DIRS VERSION_VAR - LIBOPENSHOT_AUDIO_VERSION_STRING + OpenShotAudio_VERSION_STRING ) + +if(OpenShotAudio_FOUND) + set(OpenShotAudio_INCLUDE_DIRS "${OpenShotAudio_INCLUDE_DIRS}" + CACHE PATH "The paths to libopenshot-audio's header files" FORCE) + set(OpenShotAudio_LIBRARIES "${OpenShotAudio_LIBRARIES}" + CACHE STRING "The libopenshot-audio library to link with" FORCE) + if(DEFINED OpenShotAudio_VERSION) + set(OpenShotAudio_VERSION ${OpenShotAudio_VERSION} + CACHE STRING "The version of libopenshot-audio detected" FORCE) + endif() +endif() + +if(OpenShotAudio_FOUND AND NOT TARGET OpenShot::Audio) + message(STATUS "Creating IMPORTED target OpenShot::Audio") + if(WIN32) + add_library(OpenShot::Audio UNKNOWN IMPORTED) + else() + add_library(OpenShot::Audio SHARED IMPORTED) + endif() + + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${OpenShotAudio_INCLUDE_DIRS}") + + if(WIN32) + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS -DIGNORE_JUCE_HYPOT=1) + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS -include cmath) + elseif(APPLE) + # Prevent compiling with __cxx11 + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS -D_GLIBCXX_USE_CXX11_ABI=0) + list(APPEND framework_deps + "-framework Carbon" + "-framework Cocoa" + "-framework CoreFoundation" + "-framework CoreAudio" + "-framework CoreMidi" + "-framework IOKit" + "-framework AGL" + "-framework AudioToolbox" + "-framework QuartzCore" + "-lobjc" + "-framework Accelerate" + ) + target_link_libraries(OpenShot::Audio PRIVATE ${framework_deps}) + endif() + + set_property(TARGET OpenShot::Audio APPEND PROPERTY + IMPORTED_LOCATION "${OpenShotAudio_LIBRARIES}") +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4b99567c..16b95f320 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,25 +44,6 @@ if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "Debug") endif() -################ WINDOWS ################## -# Set some compiler options for Windows -# required for libopenshot-audio headers -if (WIN32) - add_definitions( -DIGNORE_JUCE_HYPOT=1 ) - set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") -endif() - -if (APPLE) - # If you still get errors compiling with GCC 4.8, mac headers need to be patched: http://hamelot.co.uk/programming/osx-gcc-dispatch_block_t-has-not-been-declared-invalid-typedef/ - set_property(GLOBAL PROPERTY JUCE_MAC "JUCE_MAC") - set(EXTENSION "mm") - set(JUCE_PLATFORM_SPECIFIC_DIR build/macosx/platform_specific_code) - set(JUCE_PLATFORM_SPECIFIC_LIBRARIES "-framework Carbon -framework Cocoa -framework CoreFoundation -framework CoreAudio -framework CoreMidi -framework IOKit -framework AGL -framework AudioToolbox -framework QuartzCore -lobjc -framework Accelerate") - - # Prevent compiling with __cxx11 - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) -endif() - ################ IMAGE MAGICK ################## # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) if(ENABLE_MAGICK) @@ -92,12 +73,6 @@ if(ENABLE_MAGICK) endif() endif() -################# LIBOPENSHOT-AUDIO ################### -# Find JUCE-based openshot Audio libraries -find_package(OpenShotAudio 0.2.0 REQUIRED) - -# Include Juce headers (needed for compile) -include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS}) ############### PROFILING ################# #set(PROFILER "/usr/lib/libprofiler.so.0.3.2") #set(PROFILER "/usr/lib/libtcmalloc.so.4") @@ -237,6 +212,10 @@ target_include_directories(openshot $ $) +################# LIBOPENSHOT-AUDIO ################### +# Find JUCE-based openshot Audio libraries +find_package(OpenShotAudio 0.2.0 REQUIRED) +target_link_libraries(openshot PUBLIC OpenShot::Audio) ################### JSONCPP ##################### # Include jsoncpp headers (needed for JSON parsing) @@ -380,9 +359,9 @@ endif() ############### LINK LIBRARY ################# # Link remaining dependency libraries -target_link_libraries(openshot PUBLIC - ${LIBOPENSHOT_AUDIO_LIBRARIES} - ${PROFILER}) +if(DEFINED PROFILER) + target_link_libraries(openshot PUBLIC ${PROFILER}) +endif() if(ImageMagick_FOUND) target_link_libraries(openshot PUBLIC ${ImageMagick_LIBRARIES}) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 78d4ae972..146ffad18 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,19 +28,6 @@ file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/src/examples/" TEST_MEDIA_PATH) add_definitions( -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) -################ WINDOWS ################## -# Set some compiler options for Windows -# required for libopenshot-audio headers -if(WIN32) - add_definitions( -DIGNORE_JUCE_HYPOT=1 ) - set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") -endif() - -if (APPLE) - # Prevent compiling with __cxx11 - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) -endif() - ################### UNITTEST++ ##################### # Find UnitTest++ libraries (used for unit testing) find_package(UnitTest++) @@ -88,13 +75,6 @@ if(ENABLE_MAGICK) add_definitions( -DUSE_IMAGEMAGICK=1 ) endif() endif() -################# LIBOPENSHOT-AUDIO ################### -# Find JUCE-based openshot Audio libraries -find_package(OpenShotAudio 0.2.0 REQUIRED) - -# Include Juce headers (needed for compile) -include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS}) - ################# BLACKMAGIC DECKLINK ################### if(ENABLE_BLACKMAGIC) From fcebbead83d68f68db358b7b7f472c6e707cb742 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:10:06 -0400 Subject: [PATCH 55/86] tests: Update include paths --- tests/Cache_Tests.cpp | 4 ++-- tests/Clip_Tests.cpp | 2 +- tests/Color_Tests.cpp | 2 +- tests/Coordinate_Tests.cpp | 2 +- tests/DummyReader_Tests.cpp | 2 +- tests/FFmpegReader_Tests.cpp | 2 +- tests/FFmpegWriter_Tests.cpp | 2 +- tests/Fraction_Tests.cpp | 2 +- tests/FrameMapper_Tests.cpp | 2 +- tests/Frame_Tests.cpp | 2 +- tests/ImageWriter_Tests.cpp | 2 +- tests/KeyFrame_Tests.cpp | 2 +- tests/Point_Tests.cpp | 2 +- tests/ReaderBase_Tests.cpp | 2 +- tests/Settings_Tests.cpp | 2 +- tests/Timeline_Tests.cpp | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/Cache_Tests.cpp b/tests/Cache_Tests.cpp index ddf698f54..d834a0031 100644 --- a/tests/Cache_Tests.cpp +++ b/tests/Cache_Tests.cpp @@ -31,8 +31,8 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" -#include "../include/Json.h" +#include "OpenShot.h" +#include "Json.h" using namespace openshot; diff --git a/tests/Clip_Tests.cpp b/tests/Clip_Tests.cpp index f72d8b05e..2f2e850c3 100644 --- a/tests/Clip_Tests.cpp +++ b/tests/Clip_Tests.cpp @@ -37,7 +37,7 @@ // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace openshot; diff --git a/tests/Color_Tests.cpp b/tests/Color_Tests.cpp index 82c7d1803..81d6070eb 100644 --- a/tests/Color_Tests.cpp +++ b/tests/Color_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Coordinate_Tests.cpp b/tests/Coordinate_Tests.cpp index a666ce8c0..b57af38fc 100644 --- a/tests/Coordinate_Tests.cpp +++ b/tests/Coordinate_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/DummyReader_Tests.cpp b/tests/DummyReader_Tests.cpp index c72be2d9f..b889391fd 100644 --- a/tests/DummyReader_Tests.cpp +++ b/tests/DummyReader_Tests.cpp @@ -32,7 +32,7 @@ // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/FFmpegReader_Tests.cpp b/tests/FFmpegReader_Tests.cpp index b79820ade..d6a2d6e71 100644 --- a/tests/FFmpegReader_Tests.cpp +++ b/tests/FFmpegReader_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/FFmpegWriter_Tests.cpp b/tests/FFmpegWriter_Tests.cpp index cb75a118a..83764b619 100644 --- a/tests/FFmpegWriter_Tests.cpp +++ b/tests/FFmpegWriter_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Fraction_Tests.cpp b/tests/Fraction_Tests.cpp index 8f4f5f839..b061ba482 100644 --- a/tests/Fraction_Tests.cpp +++ b/tests/Fraction_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 921f3a155..777bdd7bf 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Frame_Tests.cpp b/tests/Frame_Tests.cpp index a92906a3d..8c2ebc407 100644 --- a/tests/Frame_Tests.cpp +++ b/tests/Frame_Tests.cpp @@ -32,7 +32,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" #include diff --git a/tests/ImageWriter_Tests.cpp b/tests/ImageWriter_Tests.cpp index bdf73a07d..a9b9e07df 100644 --- a/tests/ImageWriter_Tests.cpp +++ b/tests/ImageWriter_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/KeyFrame_Tests.cpp b/tests/KeyFrame_Tests.cpp index dbd81500e..840251654 100644 --- a/tests/KeyFrame_Tests.cpp +++ b/tests/KeyFrame_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Point_Tests.cpp b/tests/Point_Tests.cpp index dfe35d2a0..3e63778d8 100644 --- a/tests/Point_Tests.cpp +++ b/tests/Point_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/ReaderBase_Tests.cpp b/tests/ReaderBase_Tests.cpp index 776529d3c..3e68b40dc 100644 --- a/tests/ReaderBase_Tests.cpp +++ b/tests/ReaderBase_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Settings_Tests.cpp b/tests/Settings_Tests.cpp index 65bd66a66..cc7b86d2e 100644 --- a/tests/Settings_Tests.cpp +++ b/tests/Settings_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; diff --git a/tests/Timeline_Tests.cpp b/tests/Timeline_Tests.cpp index 47683b792..536b21334 100644 --- a/tests/Timeline_Tests.cpp +++ b/tests/Timeline_Tests.cpp @@ -31,7 +31,7 @@ #include "UnitTest++.h" // Prevent name clashes with juce::UnitTest #define DONT_SET_USING_JUCE_NAMESPACE 1 -#include "../include/OpenShot.h" +#include "OpenShot.h" using namespace std; using namespace openshot; From 68ed2602b22100b18abf43e2cdef762022286cd8 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:10:33 -0400 Subject: [PATCH 56/86] Examples: Update include paths --- examples/Example.cpp | 4 ++-- examples/ExampleHtml.cpp | 4 ++-- examples/qt-demo/main.cpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/Example.cpp b/examples/Example.cpp index c9e616288..2cd770712 100644 --- a/examples/Example.cpp +++ b/examples/Example.cpp @@ -31,8 +31,8 @@ #include #include #include -#include "../../include/OpenShot.h" -#include "../../include/CrashHandler.h" +#include "OpenShot.h" +#include "CrashHandler.h" using namespace openshot; diff --git a/examples/ExampleHtml.cpp b/examples/ExampleHtml.cpp index 576961bc5..928a05fe8 100644 --- a/examples/ExampleHtml.cpp +++ b/examples/ExampleHtml.cpp @@ -35,8 +35,8 @@ #include #include -#include "../../include/OpenShot.h" -#include "../../include/CrashHandler.h" +#include "OpenShot.h" +#include "CrashHandler.h" using namespace openshot; diff --git a/examples/qt-demo/main.cpp b/examples/qt-demo/main.cpp index 3e5f00bab..8c02795ab 100644 --- a/examples/qt-demo/main.cpp +++ b/examples/qt-demo/main.cpp @@ -28,7 +28,9 @@ * along with OpenShot Library. If not, see . */ -#include "../../../include/Qt/PlayerDemo.h" +#include "Qt/PlayerDemo.h" + +#include int main(int argc, char *argv[]) { From 51650016a7a64156a63fb89cfac103f6873ba4a5 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:11:09 -0400 Subject: [PATCH 57/86] Bindings: Update include paths --- bindings/python/CMakeLists.txt | 4 ++-- bindings/ruby/CMakeLists.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 6d7ce79ea..7bbf46139 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -50,8 +50,8 @@ include_directories(${PYTHON_INCLUDE_PATH}) if (CMAKE_VERSION VERSION_LESS 3.12) ### Include project headers include_directories( - "${PROJECT_SOURCE_DIR}/include" - "${PROJECT_BINARY_DIR}/include") + "${PROJECT_SOURCE_DIR}/src" + "${PROJECT_BINARY_DIR}/src") endif() ### Enable C++ support in SWIG diff --git a/bindings/ruby/CMakeLists.txt b/bindings/ruby/CMakeLists.txt index 2ec16dcf3..cd1061c94 100644 --- a/bindings/ruby/CMakeLists.txt +++ b/bindings/ruby/CMakeLists.txt @@ -66,8 +66,8 @@ include_directories(${RUBY_INCLUDE_DIRS}) if (CMAKE_VERSION VERSION_LESS 3.12) ### Include Ruby header files and project headers include_directories( - "${PROJECT_SOURCE_DIR}/include" - "${PROJECT_BINARY_DIR}/include") + "${PROJECT_SOURCE_DIR}/src" + "${PROJECT_BINARY_DIR}/src") endif() ### Enable C++ in SWIG From ce8512b84e95ed78961c00b4912dcaf2e398ae95 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:11:43 -0400 Subject: [PATCH 58/86] Bindings: Use SWIG shared_ptr for Ruby, also --- bindings/ruby/openshot.i | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i index 2f24d2200..349ce54d6 100644 --- a/bindings/ruby/openshot.i +++ b/bindings/ruby/openshot.i @@ -46,19 +46,15 @@ /* Unhandled STL Exception Handling */ %include -namespace std { - template class shared_ptr { - public: - T *operator->(); - }; -} +/* Include shared pointer code */ +%include /* Mark these classes as shared_ptr classes */ #ifdef USE_IMAGEMAGICK - %template(SPtrImage) std::shared_ptr; + %shared_ptr(Magick::Image) #endif -%template(SPtrAudioBuffer) std::shared_ptr; -%template(SPtrOpenFrame) std::shared_ptr; +%shared_ptr(juce::AudioSampleBuffer) +%shared_ptr(openshot::Frame) %{ /* Ruby and FFmpeg define competing RSHIFT macros, From 600f5594604ae4aa6e0bc99b57268b1d48e1960c Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:12:06 -0400 Subject: [PATCH 59/86] Bindings: Rename MappedMetadata to MetadataMap --- bindings/python/openshot.i | 2 +- bindings/ruby/openshot.i | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/openshot.i b/bindings/python/openshot.i index 53e514c15..e74b64940 100644 --- a/bindings/python/openshot.i +++ b/bindings/python/openshot.i @@ -237,5 +237,5 @@ %template(PointsVector) std::vector; %template(FieldVector) std::vector; %template(MappedFrameVector) std::vector; -%template(MappedMetadata) std::map; +%template(MetadataMap) std::map; %template(AudioDeviceInfoVector) std::vector; diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i index 349ce54d6..5e6f93117 100644 --- a/bindings/ruby/openshot.i +++ b/bindings/ruby/openshot.i @@ -223,5 +223,5 @@ %template(PointsVector) std::vector; %template(FieldVector) std::vector; %template(MappedFrameVector) std::vector; -%template(MappedMetadata) std::map; +%template(MetadataMap) std::map; %template(AudioDeviceInfoVector) std::vector; From 0fe29540b2e19ef883bab8e2235b3e3405ddfb6f Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:28:07 -0400 Subject: [PATCH 60/86] Examples: Add new CMakeLists.txt for directory --- examples/CMakeLists.txt | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 examples/CMakeLists.txt diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 000000000..874751288 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,65 @@ +####################### CMakeLists.txt (libopenshot) ######################### +# @brief CMake build file for libopenshot (used to generate makefiles) +# @author Jonathan Thomas +# @author FeRD (Frank Dana) +# +# @section LICENSE +# +# Copyright (c) 2008-2020 OpenShot Studios, LLC +# . This file is part of +# OpenShot Library (libopenshot), an open-source project dedicated to +# delivering high quality video editing and animation solutions to the +# world. For more information visit . +# +# OpenShot Library (libopenshot) is free software: you can redistribute it +# and/or modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# OpenShot Library (libopenshot) is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenShot Library. If not, see . +################################################################################ + +include(GNUInstallDirs) + +# Dependencies +find_package(Qt5 COMPONENTS Gui REQUIRED) + +############### CLI EXECUTABLES ################ +# Create test executable +add_executable(openshot-example Example.cpp) + +# Define path to test input files +file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH) +target_compile_definitions(openshot-example PRIVATE + -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) + +# Link test executable to the new library +target_link_libraries(openshot-example openshot) + +add_executable(openshot-html-test ExampleHtml.cpp) +target_link_libraries(openshot-html-test openshot Qt5::Gui) + +############### PLAYER EXECUTABLE ################ +# Create test executable +add_executable(openshot-player qt-demo/main.cpp) + +set_target_properties(openshot-player PROPERTIES AUTOMOC ON) + +# Link test executable to the new library +target_link_libraries(openshot-player openshot) + +############### TEST BLACKMAGIC CAPTURE APP ################ +if (BLACKMAGIC_FOUND) + # Create test executable + add_executable(openshot-blackmagic + examples/ExampleBlackmagic.cpp) + + # Link test executable to the new library + target_link_libraries(openshot-blackmagic openshot) +endif() From a2d540aa2cc9c9e8079c3cf5de5eca8aa0131f6e Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:15:12 -0400 Subject: [PATCH 61/86] Root CMakeLists: Adjust paths for tree reorg - Also, add missing ENABLE_COVERAGE option(), tweak option processing --- CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38abe4f30..3533e2361 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,18 +75,14 @@ option(USE_SYSTEM_JSONCPP "Use system installed JsonCpp, if found" ON) option(DISABLE_BUNDLED_JSONCPP "Don't fall back to bundled JsonCpp" OFF) option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF) option(ENABLE_TESTS "Build unit tests (requires UnitTest++)" ON) +option(ENABLE_COVERAGE "Scan test coverage using gcov and report" OFF) option(ENABLE_DOCS "Build API documentation (requires Doxygen)" ON) option(APPIMAGE_BUILD "Build to install in an AppImage (Linux only)" OFF) option(ENABLE_MAGICK "Use ImageMagick, if available" ON) # Legacy commandline override if (DISABLE_TESTS) - if(ENABLE_COVERAGE) - message(WARNING "ENABLE_COVERAGE requires tests, overriding DISABLE_TESTS") - set(ENABLE_TESTS ON) - else() - set(ENABLE_TESTS OFF) - endif() + set(ENABLE_TESTS OFF) endif() if(DEFINED ENABLE_TESTS) @@ -110,7 +106,7 @@ IF (WIN32) ENDIF(WIN32) ############## Code Coverage ######################### -if (DISABLE_TESTS AND ENABLE_COVERAGE) +if (ENABLE_COVERAGE AND NOT ENABLE_TESTS) message(WARNING "ENABLE_COVERAGE requires tests, overriding DISABLE_TESTS") set(DISABLE_TESTS OFF CACHE BOOL "Don't build unit tests" FORCE) endif() @@ -135,13 +131,17 @@ if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "Release") endif() -############## PROCESS src/ DIRECTORIES ############## +### +### Process subdirectories +### add_subdirectory(src) +add_subdirectory(examples) +add_subdirectory(bindings) ########## Configure Version.h header ############## -configure_file(include/OpenShotVersion.h.in include/OpenShotVersion.h @ONLY) +configure_file(src/OpenShotVersion.h.in src/OpenShotVersion.h @ONLY) # We'll want that installed later -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/OpenShotVersion.h +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/src/OpenShotVersion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot) ################### DOCUMENTATION ################### From 10d994dce4ac5e2b626818b0a709f4c5cd78ae61 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:15:35 -0400 Subject: [PATCH 62/86] tests/CmakeLists, Doxyfile.in: Update paths --- Doxyfile.in | 28 ++++++++++++---------------- tests/CMakeLists.txt | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Doxyfile.in b/Doxyfile.in index c47d6e651..343190a17 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -148,7 +148,7 @@ INLINE_INHERITED_MEMB = NO # shortest path that makes the file name unique will be used # The default value is: YES. -FULL_PATH_NAMES = NO +FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand @@ -160,7 +160,7 @@ FULL_PATH_NAMES = NO # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/src" # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -169,7 +169,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = "@PROJECT_SOURCE_DIR@/src" # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -803,9 +803,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/include" \ - "@CMAKE_CURRENT_SOURCE_DIR@/src" \ - "@CMAKE_CURRENT_SOURCE_DIR@/doc" +INPUT = "@PROJECT_SOURCE_DIR@/src" \ + "@PROJECT_SOURCE_DIR@/doc" \ + "@PROJECT_BINARY_DIR@/src/OpenShotVersion.h" # This tag can be used to specify the character encoding of the source files @@ -862,11 +862,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = "*/.*" \ - "*/.*/*" \ - "*/src/examples/*" \ - "*/src/bindings/*" \ - "*.py" +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -883,27 +879,27 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/tests" +EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/examples" # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = "*.cpp" # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. -EXAMPLE_RECURSIVE = NO +EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@" +IMAGE_PATH = "@PROJECT_SOURCE_DIR@" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -2086,7 +2082,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = "*.h" # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 146ffad18..437c6eb05 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,7 +25,7 @@ ################################################################################ # Test media path, used by unit tests for input data -file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/src/examples/" TEST_MEDIA_PATH) +file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH) add_definitions( -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) ################### UNITTEST++ ##################### From 3e9e99207e86f1ec2c198f0c7fb09e35b0c4fccc Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:15:52 -0400 Subject: [PATCH 63/86] Update INSTALL.md instructions for tree reorg --- INSTALL.md | 194 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 78 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f2d0dda77..7cb1ec40e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,72 +2,90 @@ Operating system specific install instructions are located in: -* [doc/INSTALL-LINUX.md][INSTALL-LINUX] -* [doc/INSTALL-MAC.md][INSTALL-MAC] -* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS] +* [doc/INSTALL-LINUX.md][INSTALL-LINUX] +* [doc/INSTALL-MAC.md][INSTALL-MAC] +* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS] ## Getting Started -The best way to get started with libopenshot, is to learn about our build system, obtain all the source code, -install a development IDE and tools, and better understand our dependencies. So, please read through the -following sections, and follow the instructions. And keep in mind, that your computer is likely different -than the one used when writing these instructions. Your file paths and versions of applications might be -slightly different, so keep an eye out for subtle file path differences in the commands you type. +The best way to get started with libopenshot is to learn about our build system, +obtain all the source code, install a development IDE and tools, +and better understand our dependencies. +So, please read through the following sections, and follow the instructions. +And keep in mind, your computer is likely different than the one used when writing these instructions. +Your file paths and versions of applications will be different, +so you should treat the specifics used here as examples and adjust as necessary. +Keep an eye out for subtle file path differences in the commands you type. ## Dependencies The following libraries are required to build libopenshot. Instructions on how to install these dependencies vary for each operating system. -Libraries and Executables have been labeled in the list below to help distinguish between them. +Libraries and executables have been labeled in the list below to help distinguish between them. #### FFmpeg (libavformat, libavcodec, libavutil, libavdevice, libavresample, libswscale) - * http://www.ffmpeg.org/ **(Library)** - * This library is used to decode and encode video, audio, and image files. It is also used to obtain information about media files, such as frame rate, sample rate, aspect ratio, and other common attributes. +* **(Library)** +* This library is used to decode and encode video, audio, and image files. + It is also used to obtain information about media files, + such as frame rate, sample rate, aspect ratio, and other common attributes. #### ImageMagick++ (libMagick++, libMagickWand, libMagickCore) - * http://www.imagemagick.org/script/magick++.php **(Library)** - * This library is **optional**, and used to decode and encode images. +* **(Library)** +* This library is **optional**, and used to decode and encode images. #### OpenShot Audio Library (libopenshot-audio) - * https://github.com/OpenShot/libopenshot-audio/ **(Library)** - * This library is used to mix, resample, host plug-ins, and play audio. It is based on the JUCE project, which is an outstanding audio library used by many different applications +* **(Library)** +* This library is used to mix, resample, host plug-ins, and play audio. + It is based on the JUCE project, + an outstanding audio library used by many different applications. #### Qt 5 (libqt5) - * http://www.qt.io/qt5/ **(Library)** - * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc... +* **(Library)** +* Qt5 is used to display video, store image data, composite images, + apply image effects, and many other utility functions, + such as file system manipulation, high resolution timers, etc. #### ZeroMQ (libzmq) - * http://zeromq.org/ **(Library)** - * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot. +* **(Library)** +* This library is used to communicate between libopenshot and other applications (publisher / subscriber). + Primarily used to send debug data from libopenshot. #### OpenMP (`-fopenmp`) - * http://openmp.org/wp/ **(Compiler Flag)** - * If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with easy methods of using parallel programming techniques to improve performance and take advantage of multi-core processors. +* **(Compiler Flag)** +* If your compiler supports this flag (GCC, Clang, and most other compilers), + it provides libopenshot with implementations of common parallel programming techniques + used to improve performance and take advantage of multi-core processors. #### CMake (`cmake`) - * http://www.cmake.org/ **(Executable)** - * This executable is used to automate the generation of Makefiles, check for dependencies, and is the backbone of libopenshot’s cross-platform build process. +* **(Executable)** +* This executable is used to automate the generation of Makefiles, + check for dependencies, and is the backbone of libopenshot’s cross-platform build process. #### SWIG (`swig`) - * http://www.swig.org/ **(Executable)** - * This executable is used to generate the Python and Ruby bindings for libopenshot. It is a simple and powerful wrapper for C++ libraries, and supports many languages. +* **(Executable)** +* This executable is used to generate the Python and Ruby bindings for libopenshot. + It is a powerful wrapper for C++ libraries, and supports many languages. #### Python 3 (libpython) - * http://www.python.org/ **(Executable and Library)** - * This library is used by swig to create the Python (version 3+) bindings for libopenshot. This is also the official language used by OpenShot Video Editor (a graphical interface to libopenshot). +* **(Executable and Library)** +* This library is used by swig to create the Python (version 3+) bindings for libopenshot. + This is also the official language used by OpenShot Video Editor (a graphical interface to libopenshot). #### Doxygen (doxygen) - * http://www.stack.nl/~dimitri/doxygen/ **(Executable)** - * This executable is used to auto-generate the documentation used by libopenshot. +* **(Executable)** +* This executable is used to auto-generate the documentation used by libopenshot. #### UnitTest++ (libunittest++) - * https://github.com/unittest-cpp/ **(Library)** - * This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code very clean and simple. +* **(Library)** +* This library is used to execute unit tests for libopenshot. + It contains many macros used to keep our unit testing code clean and easy-to-follow. ## Obtaining Source Code -The first step in installing libopenshot is to obtain the most recent source code. The source code is available on [GitHub](https://github.com/OpenShot/libopenshot). Use the following command to obtain the latest libopenshot source code. +The first step in installing libopenshot is to obtain the most recent source code. +The source code is available on [GitHub](https://github.com/OpenShot/libopenshot). +Use the following command to obtain the latest libopenshot source code. ``` git clone https://github.com/OpenShot/libopenshot.git @@ -79,27 +97,36 @@ git clone https://github.com/OpenShot/libopenshot-audio.git The source code is divided up into the following folders. #### `build/` -This folder needs to be manually created, and is used by cmake to store the temporary build files, such as makefiles, as well as the final binaries (library and test executables). +This folder needs to be manually created, +and is used by cmake to store the build system control files and generated output +(such as compiled object files and the result of template-file processing) +as well as the final results of the build (library, tool, and test program binaries). #### `cmake/` This folder contains custom modules not included by default in cmake. -CMake find modules are used to discover dependency libraries on the system, and to incorporate their headers and object files. +CMake find modules are used to discover dependency libraries on the system, +and to incorporate their headers and object files. CMake code modules are used to implement build features such as test coverage scanning. #### `doc/` This folder contains documentation and related files. This includes logos and images required by the doxygen-generated API documentation. -#### `include/` -This folder contains all headers (*.h) used by libopenshot. - #### `src/` -This folder contains all source code (*.cpp) used by libopenshot. +This folder contains all source code (`*.cpp`) and headers (`*.h`) for libopenshot. + +#### `bindings/` +This folder contains language bindings for the libopenshot API. +Current supported languages are Python and Ruby. + +#### `examples/` +This folder contains various pieces of example code written in C++, Ruby, or Python. +It also holds the media files (data files) used in examples and unit tests. #### `tests/` This folder contains all unit test code. Each test file (`_Tests.cpp`) contains the tests for the named class. -We use UnitTest++ macros to keep the test code simple and manageable. +We use UnitTest++ macros to keep the test code uncomplicated and manageable. #### `thirdparty/` This folder contains code not written by the OpenShot team. @@ -113,79 +140,90 @@ locates header files and libraries, and generates a build system in various form We use CMake's Makefile generators to compile libopenshot and libopenshot-audio. CMake uses an out-of-source build concept. -This means that the build system, all temporary files, and all generated products are kept separate from the source code. +This means that the build system, +along with all temporary/intermediate files and generated products output during the build, +is kept strictly separate from the source code. This includes Makefiles, object files, and even the final binaries. -While it is possible to build in-tree, we highly recommend you use a `/build/` sub-folder to compile each library. -This prevents the build process from cluttering up the source -code. -These instructions have only been tested with the GNU compiler suite (including MSYS2/MinGW for Windows), and the Clang compiler (including AppleClang on MacOS). +The source code files and directories are never modified by the build system, +which makes it convenient and safe to re-run, reconfigure, or discard builds as needed. +While it is possible to build in-tree, +we highly recommend you use a `/build/` sub-folder to compile each library. + +These instructions have only been tested with the following compiler stacks: +* The GNU compiler suite (including MSYS2/MinGW for Windows) +* The Clang compiler (including AppleClang on MacOS) +Other compilers, including MSVC, are entirely unsupported. +It may be possible to build libopenshot using other compiler stacks, +but most likely not without modifications to the build system which you would have to make yourself. ## CMake Flags (Optional) -There are many different build flags that can be passed to cmake to adjust how libopenshot is compiled. Some of these flags might be required when compiling on certain OSes, just depending on how your build environment is setup. +There are many different build flags that can be passed to cmake to adjust how libopenshot is compiled. +Some of these flags might be required when compiling on certain OSes, +depending on how your build environment is setup. To add a build flag, follow this general syntax: ```sh -$ cmake -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 .. +$ cmake -B build -S . -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 ... ``` Following are some of the flags you might need to set when generating your build system. ##### Optional behavior: -* `-DENABLE_TESTS=0` (default: `ON`) -* `-DENABLE_COVERAGE=1` (default: `OFF`) -* `-DENABLE_DOCS=0` (default: `ON` if doxygen found) +* `-DENABLE_TESTS=0` (default: `ON`) +* `-DENABLE_COVERAGE=1` (default: `OFF`) +* `-DENABLE_DOCS=0` (default: `ON` if doxygen found) +* `-DENABLE_RUBY=0` (default: `ON` if SWIG and Ruby detected) +* `-DENABLE_PYTHON=0` (default: `ON` if SWIG and Python detected) ##### Compiler configuration: -* `-DCMAKE_BUILD_TYPE=Release`, `-DCMAKE_BUILD_TYPE=Debug` (default: `Debug` if unset) -* `-DCMAKE_CXX_FLAGS="-Wall -Wextra"` (default: CMake builtin defaults for build type) -* `-DCMAKE_CXX_COMPILER=/path/to/g++`, `-DCMAKE_CXX_COMPILER=/path/to/clang++` -* `-DCMAKE_C_COMPILER=/path/to/gcc`, `-DCMAKE_CXX_COMPILER=/path/to/clang` (used by CMake for OS probes) +* `-DCMAKE_BUILD_TYPE=Release`, `-DCMAKE_BUILD_TYPE=Debug` (default: `Release` if unset) +* `-DCMAKE_CXX_FLAGS="-Wall -Wextra"` (default: CMake builtin defaults for build type) +* `-DCMAKE_CXX_COMPILER=/path/to/g++`, `-DCMAKE_CXX_COMPILER=/path/to/clang++` +* `-DCMAKE_C_COMPILER=/path/to/gcc`, `-DCMAKE_CXX_COMPILER=/path/to/clang` (used by CMake for OS probes) ##### Dependency configuration: -* `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/` -* `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered) -* `-DImageMagick_FOUND=0` (default: auto if discovered) +* `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/` +* `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered) +* `-DENABLE_MAGICK=0` (default: auto if discovered) ##### To compile bindings for a specific Python installation: -* `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/` -* `-DPYTHON_LIBRARY=/location/of/libpython*.so` -* `-DPYTHON_FRAMEWORKS=/usr/local/Cellar/python3/3.3.2/Frameworks/Python.framework/` (MacOS only) +* `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/` +* `-DPYTHON_LIBRARY=/location/of/libpython*.so` +* `-DPYTHON_FRAMEWORKS=/usr/local/Cellar/python3/3.3.2/Frameworks/Python.framework/` (MacOS only) ##### Only used when building with ImageMagick enabled: -* `-DMAGICKCORE_HDRI_ENABLE=1` (default `0`) -* `-DMAGICKCORE_QUANTUM_DEPTH=8` (default `16`) +* `-DMAGICKCORE_HDRI_ENABLE=1` (default `0`) +* `-DMAGICKCORE_QUANTUM_DEPTH=8` (default `16`) ## Linux Build Instructions (libopenshot-audio) -To compile libopenshot-audio, we need to go through a few additional steps to manually build and install it. Launch a terminal and enter: +To compile libopenshot-audio, we need to build it from source code and install the results. +Launch a terminal and enter: -``` +```sh cd [libopenshot-audio repo folder] -mkdir build -cd build -cmake ../ -make -make install -./src/openshot-audio-test-sound (This should play a test sound) +cmake -B build -S . +cmake --build build +./build/src/openshot-audio-test-sound (This should play a test sound) +cmake --install build ``` ## Linux Build Instructions (libopenshot) Run the following commands to compile libopenshot: -``` +```sh cd [libopenshot repo directory] -mkdir -p build -cd build -cmake ../ -make -make install +cmake -B build -S . +cmake --build build +cmake --build build --target test +cmake --install build ``` For more detailed instructions, please see: -* [doc/INSTALL-LINUX.md][INSTALL-LINUX] -* [doc/INSTALL-MAC.md][INSTALL-MAC] -* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS] +* [doc/INSTALL-LINUX.md][INSTALL-LINUX] +* [doc/INSTALL-MAC.md][INSTALL-MAC] +* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS] [INSTALL-LINUX]: https://github.com/OpenShot/libopenshot/blob/develop/doc/INSTALL-LINUX.md [INSTALL-MAC]: https://github.com/OpenShot/libopenshot/blob/develop/doc/INSTALL-MAC.md From b0e406be497ec6a083afea38fd0ee8194d4c0260 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 05:17:27 -0400 Subject: [PATCH 64/86] Fix Ruby example script bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ruby: Relax SWIG version for compatibility check - Turns out the Ruby-2.7.0-compatibility commit made it into SWIG 4.0.2 Ruby bindings: Fix all kinds of brokenness - Turns out int64_t function args require stdint.i for auto-conversion. - The imported module name is (still) 'Openshot' — lowercase 's'. - #TIL that accidentally dropping the OUTPUT_NAME config leads to a non-loadable shared library, due to the filename being wrong. --- bindings/python/CMakeLists.txt | 2 +- bindings/ruby/CMakeLists.txt | 14 +++++++++++--- bindings/ruby/openshot.i | 1 + examples/Example.rb | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 7bbf46139..b020592bd 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -54,7 +54,7 @@ if (CMAKE_VERSION VERSION_LESS 3.12) "${PROJECT_BINARY_DIR}/src") endif() -### Enable C++ support in SWIG +### Enable C++ in SWIG set_property(SOURCE openshot.i PROPERTY CPLUSPLUS ON) set_property(SOURCE openshot.i PROPERTY SWIG_MODULE_NAME openshot) diff --git a/bindings/ruby/CMakeLists.txt b/bindings/ruby/CMakeLists.txt index cd1061c94..de47b94fd 100644 --- a/bindings/ruby/CMakeLists.txt +++ b/bindings/ruby/CMakeLists.txt @@ -46,11 +46,11 @@ endif() option(SILENCE_RUBY_VERSION_WARNING "Don't warn about possible SWIG incompatibilities with Ruby 2.7.0+" OFF) -if (${RUBY_VERSION} VERSION_GREATER 2.6.9 AND ${SWIG_VERSION} VERSION_LESS 4.0.3) +if (${RUBY_VERSION} VERSION_GREATER 2.6.9 AND ${SWIG_VERSION} VERSION_LESS 4.0.2) if (NOT ${SILENCE_RUBY_VERSION_WARNING}) message(WARNING "\ Ruby 2.7.0+ detected, building the libopenshot Ruby API bindings \ -requires either SWIG 4.0.3 or an older version patched with this commit: \ +requires either SWIG 4.0.2+ or an older version patched with this commit: \ https://github.com/swig/swig/commit/5542cc228ad10bdc5c91107afb77c808c43bf2a4 \ (Recent Fedora and Ubuntu distro packages of SWIG 4.0.1 have already been \ patched.)") @@ -64,7 +64,7 @@ endif() include_directories(${RUBY_INCLUDE_DIRS}) if (CMAKE_VERSION VERSION_LESS 3.12) - ### Include Ruby header files and project headers + ### Include project headers include_directories( "${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/src") @@ -99,6 +99,14 @@ else() swig_add_library(rbopenshot LANGUAGE ruby SOURCES openshot.i) endif() +### Set name of target (with no prefix, since Ruby does not like that) +# XXX: If this is not done exactly this way, the module builds as +# e.g. rbopenshot.so, but its initializer method will be named +# 'Init_openshot()' (via the module name set in the SWIG .i file). +# Which leads to Ruby barfing when it attempts to load the module. +set_target_properties(${SWIG_MODULE_rbopenshot_REAL_NAME} PROPERTIES + PREFIX "" OUTPUT_NAME "openshot") + ### Link the new Ruby wrapper library with libopenshot target_link_libraries(${SWIG_MODULE_rbopenshot_REAL_NAME} PUBLIC ${RUBY_LIBRARY} openshot) diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i index 5e6f93117..b27510e20 100644 --- a/bindings/ruby/openshot.i +++ b/bindings/ruby/openshot.i @@ -42,6 +42,7 @@ %include "std_list.i" %include "std_vector.i" %include "std_map.i" +%include /* Unhandled STL Exception Handling */ %include diff --git a/examples/Example.rb b/examples/Example.rb index 7d2e4e1aa..5e4c79409 100644 --- a/examples/Example.rb +++ b/examples/Example.rb @@ -1,13 +1,13 @@ # Find and load the ruby libopenshot wrapper library -require "./openshot" +require "openshot" # Create a new FFmpegReader and Open it -r = OpenShot::FFmpegReader.new("myfile.mp4") +r = Openshot::FFmpegReader.new("test.mp4") r.Open() # Get frame 1 f = r.GetFrame(1) # Display the frame -r.Display() +f.Display() From aff469535fea1c93362b8fd4c41f8a3ec74a9a06 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 10:22:34 -0400 Subject: [PATCH 65/86] ImageReader/Writer.h: Goose coverage to 100% --- tests/ImageWriter_Tests.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/ImageWriter_Tests.cpp b/tests/ImageWriter_Tests.cpp index 6d3a2e229..eb7b31e98 100644 --- a/tests/ImageWriter_Tests.cpp +++ b/tests/ImageWriter_Tests.cpp @@ -61,6 +61,8 @@ TEST(Gif) /* WRITER ---------------- */ ImageWriter w("output1.gif"); + CHECK_EQUAL(false, w.IsOpen()); + // Check for exception on write-before-open CHECK_THROW(w.WriteFrame(&r, 500, 504), WriterClosed); @@ -79,7 +81,16 @@ TEST(Gif) // Open up the 5th frame from the newly created GIF ImageReader r1("output1.gif[4]"); + + // Basic Reader state queries + CHECK_EQUAL("ImageReader", r1.Name()); + + CacheMemory* c = r1.GetCache(); + CHECK_EQUAL(true, c == nullptr); + + CHECK_EQUAL(false, r1.IsOpen()); r1.Open(); + CHECK_EQUAL(true, r1.IsOpen()); // Verify various settings CHECK_EQUAL(r.info.width, r1.info.width); From 4bd3af0c7150ec3e1b333783d7f24957ef3897be Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 10:41:53 -0400 Subject: [PATCH 66/86] Rename FindResvg, target is now Resvg::Resvg --- .../{FindRESVG.cmake => FindResvg.cmake} | 56 +++++++++---------- src/CMakeLists.txt | 21 ++++--- 2 files changed, 40 insertions(+), 37 deletions(-) rename cmake/Modules/{FindRESVG.cmake => FindResvg.cmake} (59%) diff --git a/cmake/Modules/FindRESVG.cmake b/cmake/Modules/FindResvg.cmake similarity index 59% rename from cmake/Modules/FindRESVG.cmake rename to cmake/Modules/FindResvg.cmake index 0538eacd5..452a81c8b 100644 --- a/cmake/Modules/FindRESVG.cmake +++ b/cmake/Modules/FindResvg.cmake @@ -1,13 +1,13 @@ # vim: ts=2 sw=2 #[=======================================================================[.rst: -FindRESVG +FindResvg --------- Try to find the shared-library build of resvg, the Rust SVG library IMPORTED targets ^^^^^^^^^^^^^^^^ -This module defines :prop_tgt:`IMPORTED` target ``RESVG::resvg`` when +This module defines :prop_tgt:`IMPORTED` target ``Resvg::Resvg`` when the library and headers are found. Result Variables @@ -17,10 +17,10 @@ This module defines the following variables: :: - RESVG_FOUND - Library and header files found - RESVG_INCLUDE_DIRS - Include directory path - RESVG_LIBRARIES - Link path to the library - RESVG_DEFINITIONS - Compiler switches (currently unused) + Resvg_FOUND - Library and header files found + Resvg_INCLUDE_DIRS - Include directory path + Resvg_LIBRARIES - Link path to the library + Resvg_DEFINITIONS - Compiler switches (currently unused) Backwards compatibility ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ if(DEFINED RESVGDIR AND CMAKE_VERSION VERSION_GREATER 3.4) BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) endif() -find_path(RESVG_INCLUDE_DIRS +find_path(Resvg_INCLUDE_DIRS ResvgQt.h PATHS ${RESVGDIR} @@ -62,7 +62,7 @@ find_path(RESVG_INCLUDE_DIRS resvg/capi/include ) -find_library(RESVG_LIBRARIES +find_library(Resvg_LIBRARIES NAMES resvg PATHS ${RESVGDIR} @@ -77,39 +77,39 @@ find_library(RESVG_LIBRARIES resvg/target/release ) -if (RESVG_INCLUDE_DIRS AND RESVG_LIBRARIES) - set(RESVG_FOUND TRUE) +if (Resvg_INCLUDE_DIRS AND Resvg_LIBRARIES) + set(Resvg_FOUND TRUE) endif() -set(RESVG_LIBRARIES ${RESVG_LIBRARIES} CACHE STRING "The Resvg library link path") -set(RESVG_INCLUDE_DIRS ${RESVG_INCLUDE_DIRS} CACHE STRING "The Resvg include directories") -set(RESVG_DEFINITIONS "" CACHE STRING "The Resvg CFLAGS") +set(Resvg_LIBRARIES ${Resvg_LIBRARIES} CACHE STRING "The Resvg library link path") +set(Resvg_INCLUDE_DIRS ${Resvg_INCLUDE_DIRS} CACHE STRING "The Resvg include directories") +set(Resvg_DEFINITIONS "" CACHE STRING "The Resvg CFLAGS") -mark_as_advanced(RESVG_LIBRARIES RESVG_INCLUDE_DIRS RESVG_DEFINITIONS) +mark_as_advanced(Resvg_LIBRARIES Resvg_INCLUDE_DIRS Resvg_DEFINITIONS) # Give a nice error message if some of the required vars are missing. -find_package_handle_standard_args(RESVG - "Could NOT find RESVG, using Qt SVG parsing instead" - RESVG_LIBRARIES RESVG_INCLUDE_DIRS ) +find_package_handle_standard_args(Resvg + "Could NOT find Resvg, using Qt SVG parsing instead" + Resvg_LIBRARIES Resvg_INCLUDE_DIRS ) # Export target -if(RESVG_FOUND AND NOT TARGET RESVG::resvg) - message(STATUS "Creating IMPORTED target RESVG::resvg") +if(Resvg_FOUND AND NOT TARGET Resvg::Resvg) + message(STATUS "Creating IMPORTED target Resvg::Resvg") if (WIN32) # Windows mis-links SHARED library targets - add_library(RESVG::resvg UNKNOWN IMPORTED) + add_library(Resvg::Resvg UNKNOWN IMPORTED) else() # Linux needs SHARED to link because libresvg has no SONAME - add_library(RESVG::resvg SHARED IMPORTED) - set_property(TARGET RESVG::resvg APPEND PROPERTY + add_library(Resvg::Resvg SHARED IMPORTED) + set_property(TARGET Resvg::Resvg APPEND PROPERTY IMPORTED_NO_SONAME TRUE) endif() - set_property(TARGET RESVG::resvg APPEND PROPERTY - INTERFACE_INCLUDE_DIRECTORIES "${RESVG_INCLUDE_DIRS}") + set_property(TARGET Resvg::Resvg APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${Resvg_INCLUDE_DIRS}") - set_property(TARGET RESVG::resvg APPEND PROPERTY - INTERFACE_COMPILE_DEFINITIONS "${RESVG_DEFINITIONS}") + set_property(TARGET Resvg::Resvg APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS "${Resvg_DEFINITIONS}") - set_property(TARGET RESVG::resvg APPEND PROPERTY - IMPORTED_LOCATION "${RESVG_LIBRARIES}") + set_property(TARGET Resvg::Resvg APPEND PROPERTY + IMPORTED_LOCATION "${Resvg_LIBRARIES}") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16b95f320..0307b987a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -318,22 +318,25 @@ if (TARGET cppzmq) target_link_libraries(openshot PUBLIC cppzmq) endif() -################### RESVG ##################### +### +### Resvg, the Rust SVG library +### + # Migrate some legacy variable names -if(DEFINED RESVGDIR AND NOT DEFINED RESVG_ROOT) - set(RESVG_ROOT ${RESVGDIR}) +if(DEFINED RESVGDIR AND NOT DEFINED Resvg_ROOT) + set(Resvg_ROOT ${RESVGDIR}) endif() -if(DEFINED ENV{RESVGDIR} AND NOT DEFINED RESVG_ROOT) - set(RESVG_ROOT $ENV{RESVGDIR}) +if(DEFINED ENV{RESVGDIR} AND NOT DEFINED Resvg_ROOT) + set(Resvg_ROOT $ENV{RESVGDIR}) endif() # Find resvg library (used for rendering svg files) -find_package(RESVG) +find_package(Resvg) # Include resvg headers (optional SVG library) -if (TARGET RESVG::resvg) - #include_directories(${RESVG_INCLUDE_DIRS}) - target_link_libraries(openshot PUBLIC RESVG::resvg) +if (TARGET Resvg::Resvg) + #include_directories(${Resvg_INCLUDE_DIRS}) + target_link_libraries(openshot PUBLIC Resvg::Resvg) target_compile_definitions(openshot PUBLIC -DUSE_RESVG=1) list(APPEND CMAKE_SWIG_FLAGS -DUSE_RESVG=1) From 1ae52808c7459fd0d8025018eb75544a3f1c2872 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 10:55:59 -0400 Subject: [PATCH 67/86] CMake: Add target for ImageMagick dependency - Parameters assigned to ImageMagick::Magick++ will now follow the 'openshot' shared-library target wherever it's linked, even in build subdirectories that aren't children or siblings of the location where the target was created. - Removed the redundant code duplicating `find_package(ImageMagick)` checks. Multiple dependency scans have only been necessary as a workaround to overcome CMake variables' restricted scope. Targets do not share that limitation. --- cmake/Modules/FindOpenShotAudio.cmake | 17 ++++- src/CMakeLists.txt | 96 ++++++++++++++++----------- tests/CMakeLists.txt | 31 --------- 3 files changed, 71 insertions(+), 73 deletions(-) diff --git a/cmake/Modules/FindOpenShotAudio.cmake b/cmake/Modules/FindOpenShotAudio.cmake index 0683a864a..edb5c3fce 100644 --- a/cmake/Modules/FindOpenShotAudio.cmake +++ b/cmake/Modules/FindOpenShotAudio.cmake @@ -145,15 +145,28 @@ if(OpenShotAudio_FOUND AND NOT TARGET OpenShot::Audio) set_property(TARGET OpenShot::Audio APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${OpenShotAudio_INCLUDE_DIRS}") + # Juce requires either DEBUG or NDEBUG to be defined on MacOS. + # -DNDEBUG is set by cmake for all release configs, so add + # -DDEBUG for debug builds. We'll do this for all OSes, even + # though only MacOS requires it. + # The generator expression translates to: + # CONFIG == "DEBUG" ? "DEBUG" : "" + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS $<$:DEBUG>) + + # For the Ruby bindings + set_property(TARGET OpenShot::Audio APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS HAVE_ISFINITE=1) + if(WIN32) set_property(TARGET OpenShot::Audio APPEND PROPERTY - INTERFACE_COMPILE_DEFINITIONS -DIGNORE_JUCE_HYPOT=1) + INTERFACE_COMPILE_DEFINITIONS IGNORE_JUCE_HYPOT=1) set_property(TARGET OpenShot::Audio APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -include cmath) elseif(APPLE) # Prevent compiling with __cxx11 set_property(TARGET OpenShot::Audio APPEND PROPERTY - INTERFACE_COMPILE_DEFINITIONS -D_GLIBCXX_USE_CXX11_ABI=0) + INTERFACE_COMPILE_DEFINITIONS _GLIBCXX_USE_CXX11_ABI=0) list(APPEND framework_deps "-framework Carbon" "-framework Cocoa" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0307b987a..776c63015 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,36 +43,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "Debug") endif() - -################ IMAGE MAGICK ################## -# Set the Quantum Depth that ImageMagick was built with (default to 16 bits) -if(ENABLE_MAGICK) - IF (MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) - ELSE (MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) - ENDIF (MAGICKCORE_QUANTUM_DEPTH) - IF (MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) - ELSE (MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) - ENDIF (MAGICKCORE_HDRI_ENABLE) - - # Find the ImageMagick++ library - find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) - if (ImageMagick_FOUND) - # Include ImageMagick++ headers (needed for compile) - include_directories(${ImageMagick_INCLUDE_DIRS}) - - # define a global var (used in the C++) - add_definitions( -DUSE_IMAGEMAGICK=1 ) - list(APPEND CMAKE_SWIG_FLAGS -DUSE_IMAGEMAGICK=1) - - set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE) - mark_as_advanced(HAVE_IMAGEMAGICK) - endif() -endif() - ############### PROFILING ################# #set(PROFILER "/usr/lib/libprofiler.so.0.3.2") #set(PROFILER "/usr/lib/libtcmalloc.so.4") @@ -185,13 +155,6 @@ set_target_properties(openshot PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" ) -# Add optional ImageMagic-dependent sources -if(ImageMagick_FOUND) - target_sources(openshot PRIVATE - ImageReader.cpp - ImageWriter.cpp - TextReader.cpp) -endif() # BlackMagic related files if(BLACKMAGIC_FOUND) @@ -217,6 +180,62 @@ target_include_directories(openshot find_package(OpenShotAudio 0.2.0 REQUIRED) target_link_libraries(openshot PUBLIC OpenShot::Audio) +### +### ImageMagick +### + +# Find the ImageMagick++ library +find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) + +if(ImageMagick_FOUND) + if(NOT TARGET ImageMagick::Magick++ AND NOT TARGET Magick++_TARGET) + add_library(Magick++_TARGET INTERFACE) + + # Include ImageMagick++ headers (needed for compile) + set_property(TARGET Magick++_TARGET APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${ImageMagick_INCLUDE_DIRS}) + + # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) + if(NOT DEFINED MAGICKCORE_QUANTUM_DEPTH) + set(MAGICKCORE_QUANTUM_DEPTH 16) + endif() + if(NOT DEFINED MAGICKCORE_HDRI_ENABLE) + set(MAGICKCORE_HDRI_ENABLE 0) + endif() + + set_property(TARGET Magick++_TARGET APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + MAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH}) + set_property(TARGET Magick++_TARGET APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + MAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE}) + + target_link_libraries(Magick++_TARGET INTERFACE + ${ImageMagick_LIBRARIES}) + + # Alias to our namespaced name + add_library(ImageMagick::Magick++ ALIAS Magick++_TARGET) + + endif() + + # Add optional ImageMagic-dependent sources + target_sources(openshot PRIVATE + ImageReader.cpp + ImageWriter.cpp + TextReader.cpp) + + # define a preprocessor macro (used in the C++ source) + target_compile_definitions(openshot PUBLIC USE_IMAGEMAGICK=1) + list(APPEND CMAKE_SWIG_FLAGS -DUSE_IMAGEMAGICK=1) + + # Link with ImageMagick library + target_link_libraries(openshot PUBLIC ImageMagick::Magick++) + + set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE) + mark_as_advanced(HAVE_IMAGEMAGICK) +endif() + + ################### JSONCPP ##################### # Include jsoncpp headers (needed for JSON parsing) if (USE_SYSTEM_JSONCPP) @@ -366,9 +385,6 @@ if(DEFINED PROFILER) target_link_libraries(openshot PUBLIC ${PROFILER}) endif() -if(ImageMagick_FOUND) - target_link_libraries(openshot PUBLIC ${ImageMagick_LIBRARIES}) -endif() if(BLACKMAGIC_FOUND) target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR}) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 437c6eb05..73a812b5c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,37 +44,6 @@ set_package_properties(UnitTest++ PROPERTIES TYPE RECOMMENDED PURPOSE "Unit testing framework") -################ IMAGE MAGICK ################## -if(ENABLE_MAGICK) - # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) - if(MAGICKCORE_QUANTUM_DEPTH) - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) - else() - add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) - endif() - - if(MAGICKCORE_HDRI_ENABLE) - add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) - else() - add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) - endif() - - if(OPENSHOT_IMAGEMAGICK_COMPATIBILITY) - add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=${OPENSHOT_IMAGEMAGICK_COMPATIBILITY} ) - else() - add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=0 ) - endif() - - # Find the ImageMagick++ library - find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) - if(ImageMagick_FOUND) - # Include ImageMagick++ headers (needed for compile) - include_directories(${ImageMagick_INCLUDE_DIRS}) - - # define a global var (used in the C++) - add_definitions( -DUSE_IMAGEMAGICK=1 ) - endif() -endif() ################# BLACKMAGIC DECKLINK ################### if(ENABLE_BLACKMAGIC) From d910949189d370e9de4072fdb8183fcd3b68bb5e Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 11:04:28 -0400 Subject: [PATCH 68/86] CMake: Code and Find module tweaks - FindOpenShotAudio takes over -DDEBUG, -DHAVE_ISFINITE logic (now set on OpenShot::Audio target when appropriate) - Tweaks to BlackMagic dependency discovery - Reverse the test-disabling logic, `DISABLE_TESTS FALSE` cache variable changed to `ENABLE_TESTS TRUE` --- CMakeLists.txt | 22 +++++++++++--------- bindings/ruby/CMakeLists.txt | 3 --- src/CMakeLists.txt | 39 +++++++++++++----------------------- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3533e2361..ec608257d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,8 +107,8 @@ ENDIF(WIN32) ############## Code Coverage ######################### if (ENABLE_COVERAGE AND NOT ENABLE_TESTS) - message(WARNING "ENABLE_COVERAGE requires tests, overriding DISABLE_TESTS") - set(DISABLE_TESTS OFF CACHE BOOL "Don't build unit tests" FORCE) + message(WARNING "ENABLE_COVERAGE requires unit tests, forcing ENABLE_TESTS") + set(ENABLE_TESTS ON CACHE BOOL "Don't build unit tests" FORCE) endif() if (ENABLE_COVERAGE) @@ -121,11 +121,6 @@ if (ENABLE_COVERAGE) endif() add_feature_info("Coverage" ENABLE_COVERAGE "analyze test coverage and generate report") -# Juce requires either DEBUG or NDEBUG to be defined on MacOS. -# -DNDEBUG is set by cmake for all release configs, so add -# -DDEBUG for debug builds. We'll do this for all OSes, even -# though only MacOS requires it. -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") # Make sure we've picked some build type, default to release if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "Release") @@ -138,12 +133,17 @@ add_subdirectory(src) add_subdirectory(examples) add_subdirectory(bindings) -########## Configure Version.h header ############## +### +### Configure Version.h header +### +# (Note: This comes after the subdirectories, because it needs variables +# set during the dependency discovery in src/CMakeLists.txt) configure_file(src/OpenShotVersion.h.in src/OpenShotVersion.h @ONLY) # We'll want that installed later install(FILES ${CMAKE_CURRENT_BINARY_DIR}/src/OpenShotVersion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot) + ################### DOCUMENTATION ################### # Find Doxygen (used for documentation) set(DOCS_ENABLED FALSE) # Only set true if Doxygen is found and configured @@ -180,8 +180,10 @@ if (ENABLE_COVERAGE) NAME coverage LCOV_ARGS "--no-external" EXECUTABLE openshot-test - DEPENDENCIES openshot-test) - message("Generate coverage report with 'make coverage'") + DEPENDENCIES openshot-test + EXCLUDE "bindings" "examples" "${CMAKE_CURRENT_BINARY_DIR}/bindings" + ) + message("Generate coverage report with 'make coverage'") endif() ########### PRINT FEATURE SUMMARY ############## diff --git a/bindings/ruby/CMakeLists.txt b/bindings/ruby/CMakeLists.txt index de47b94fd..72d1a9857 100644 --- a/bindings/ruby/CMakeLists.txt +++ b/bindings/ruby/CMakeLists.txt @@ -74,9 +74,6 @@ endif() set_property(SOURCE openshot.i PROPERTY CPLUSPLUS ON) set_property(SOURCE openshot.i PROPERTY SWIG_MODULE_NAME openshot) -### Unbreak std::isfinite() -add_definitions(-DHAVE_ISFINITE=1) - ### Suppress a ton of warnings in the generated SWIG C++ code set(SWIG_CXX_FLAGS "-Wno-unused-variable -Wno-unused-function \ -Wno-deprecated-copy -Wno-class-memaccess -Wno-cast-function-type \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 776c63015..f8bd44363 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,15 +34,6 @@ if (POLICY CMP0057) cmake_policy(SET CMP0057 NEW) endif() -# Juce requires either DEBUG or NDEBUG to be defined on MacOS. -# -DNDEBUG is set by cmake for all release configs, so add -# -DDEBUG for debug builds. We'll do this for all OSes, even -# though only MacOS requires it. -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -# Make sure we've picked some build type, default to debug -if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_BUILD_TYPE "Debug") -endif() ############### PROFILING ################# #set(PROFILER "/usr/lib/libprofiler.so.0.3.2") #set(PROFILER "/usr/lib/libtcmalloc.so.4") @@ -155,16 +146,6 @@ set_target_properties(openshot PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" ) - -# BlackMagic related files -if(BLACKMAGIC_FOUND) - target_sources(openshot PRIVATE - DecklinkInput.cpp - DecklinkReader.cpp - DecklinkOutput.cpp - DecklinkWriter.cpp) -endif() - # Location of our includes, both internally and when installed target_include_directories(openshot PRIVATE @@ -320,7 +301,10 @@ endif() target_link_libraries(openshot PUBLIC OpenMP::OpenMP_CXX) -################### ZEROMQ ##################### +### +### ZeroMQ +### + # Find ZeroMQ library (used for socket communication & logging) find_package(ZeroMQ REQUIRED) # Creates libzmq target @@ -370,9 +354,19 @@ if (ENABLE_BLACKMAGIC) find_package(BlackMagic) if (BLACKMAGIC_FOUND) + # BlackMagic related files + target_sources(openshot PRIVATE + DecklinkInput.cpp + DecklinkReader.cpp + DecklinkOutput.cpp + DecklinkWriter.cpp) + # Include Blackmagic headers (needed for compile) target_include_directories(openshot PRIVATE ${BLACKMAGIC_INCLUDE_DIR}) + # Link libopenshot with BlackMagic libs + target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR}) + # define a preprocessor macro (used in the C++) target_compile_definitions(openshot PUBLIC -DUSE_BLACKMAGIC=1) list(APPEND CMAKE_SWIG_FLAGS -DUSE_BLACKMAGIC=1) @@ -385,11 +379,6 @@ if(DEFINED PROFILER) target_link_libraries(openshot PUBLIC ${PROFILER}) endif() - -if(BLACKMAGIC_FOUND) - target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR}) -endif() - if(WIN32) # Required for exception handling on Windows target_link_libraries(openshot PUBLIC "imagehlp" "dbghelp" ) From 46902b1641b37b82106a19239f260fdbfaa3bc46 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 11:04:41 -0400 Subject: [PATCH 69/86] Codecov: Update excludes for new repo layout --- codecov.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/codecov.yml b/codecov.yml index ee151daaf..e00103e78 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,10 +7,11 @@ coverage: base: pr # Only post a status to pull requests informational: true # Don't block PRs based on coverage stats (yet?) ignore: - - "/src/examples" - - "/src/Qt/demo" - - "/thirdparty/jsoncpp/*.cpp" - - "/thirdparty/jsoncpp/json/*.h" + - "/examples" + - "/bindings" + - "/thirdparty/jsoncpp" - "/doc" - "/cmake" - "/*.md" + - "bindings" + - "src/openshot_autogen" From 3854fe9c0e2efd83c66098a788b0fedf1c1ae6a5 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sun, 18 Oct 2020 11:14:20 -0400 Subject: [PATCH 70/86] FindOpenShotAudio: Fix linking --- cmake/Modules/FindOpenShotAudio.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindOpenShotAudio.cmake b/cmake/Modules/FindOpenShotAudio.cmake index edb5c3fce..4716112e3 100644 --- a/cmake/Modules/FindOpenShotAudio.cmake +++ b/cmake/Modules/FindOpenShotAudio.cmake @@ -180,7 +180,7 @@ if(OpenShotAudio_FOUND AND NOT TARGET OpenShot::Audio) "-lobjc" "-framework Accelerate" ) - target_link_libraries(OpenShot::Audio PRIVATE ${framework_deps}) + target_link_libraries(OpenShot::Audio INTERFACE ${framework_deps}) endif() set_property(TARGET OpenShot::Audio APPEND PROPERTY From b741eff7f2cb2ca6b9d563e632e56b0dd2c3f1c3 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 19 Oct 2020 04:33:58 -0400 Subject: [PATCH 71/86] INSTALL.md: Fix Markdown style --- INSTALL.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 7cb1ec40e..b1ddc7961 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -24,8 +24,8 @@ Instructions on how to install these dependencies vary for each operating system Libraries and executables have been labeled in the list below to help distinguish between them. #### FFmpeg (libavformat, libavcodec, libavutil, libavdevice, libavresample, libswscale) - * **(Library)** + * This library is used to decode and encode video, audio, and image files. It is also used to obtain information about media files, such as frame rate, sample rate, aspect ratio, and other common attributes. @@ -36,39 +36,46 @@ Libraries and executables have been labeled in the list below to help distinguis #### OpenShot Audio Library (libopenshot-audio) * **(Library)** + * This library is used to mix, resample, host plug-ins, and play audio. It is based on the JUCE project, an outstanding audio library used by many different applications. #### Qt 5 (libqt5) * **(Library)** + * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc. #### ZeroMQ (libzmq) * **(Library)** + * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot. #### OpenMP (`-fopenmp`) * **(Compiler Flag)** + * If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with implementations of common parallel programming techniques used to improve performance and take advantage of multi-core processors. #### CMake (`cmake`) * **(Executable)** + * This executable is used to automate the generation of Makefiles, check for dependencies, and is the backbone of libopenshot’s cross-platform build process. #### SWIG (`swig`) * **(Executable)** + * This executable is used to generate the Python and Ruby bindings for libopenshot. It is a powerful wrapper for C++ libraries, and supports many languages. #### Python 3 (libpython) * **(Executable and Library)** + * This library is used by swig to create the Python (version 3+) bindings for libopenshot. This is also the official language used by OpenShot Video Editor (a graphical interface to libopenshot). @@ -78,6 +85,7 @@ Libraries and executables have been labeled in the list below to help distinguis #### UnitTest++ (libunittest++) * **(Library)** + * This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code clean and easy-to-follow. @@ -87,7 +95,7 @@ The first step in installing libopenshot is to obtain the most recent source cod The source code is available on [GitHub](https://github.com/OpenShot/libopenshot). Use the following command to obtain the latest libopenshot source code. -``` +```sh git clone https://github.com/OpenShot/libopenshot.git git clone https://github.com/OpenShot/libopenshot-audio.git ``` @@ -152,11 +160,12 @@ we highly recommend you use a `/build/` sub-folder to compile each library. These instructions have only been tested with the following compiler stacks: * The GNU compiler suite (including MSYS2/MinGW for Windows) * The Clang compiler (including AppleClang on MacOS) + Other compilers, including MSVC, are entirely unsupported. It may be possible to build libopenshot using other compiler stacks, but most likely not without modifications to the build system which you would have to make yourself. -## CMake Flags (Optional) +### CMake Flags (Optional) There are many different build flags that can be passed to cmake to adjust how libopenshot is compiled. Some of these flags might be required when compiling on certain OSes, depending on how your build environment is setup. @@ -164,35 +173,35 @@ depending on how your build environment is setup. To add a build flag, follow this general syntax: ```sh -$ cmake -B build -S . -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 ... +cmake -B build -S . -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 ... ``` Following are some of the flags you might need to set when generating your build system. -##### Optional behavior: +#### Optional behaviors of the build system * `-DENABLE_TESTS=0` (default: `ON`) * `-DENABLE_COVERAGE=1` (default: `OFF`) * `-DENABLE_DOCS=0` (default: `ON` if doxygen found) * `-DENABLE_RUBY=0` (default: `ON` if SWIG and Ruby detected) * `-DENABLE_PYTHON=0` (default: `ON` if SWIG and Python detected) -##### Compiler configuration: +#### Options to configure the compiler * `-DCMAKE_BUILD_TYPE=Release`, `-DCMAKE_BUILD_TYPE=Debug` (default: `Release` if unset) * `-DCMAKE_CXX_FLAGS="-Wall -Wextra"` (default: CMake builtin defaults for build type) * `-DCMAKE_CXX_COMPILER=/path/to/g++`, `-DCMAKE_CXX_COMPILER=/path/to/clang++` * `-DCMAKE_C_COMPILER=/path/to/gcc`, `-DCMAKE_CXX_COMPILER=/path/to/clang` (used by CMake for OS probes) -##### Dependency configuration: +#### Options to configure dependencies * `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/` * `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered) * `-DENABLE_MAGICK=0` (default: auto if discovered) -##### To compile bindings for a specific Python installation: +#### Options to compile bindings for a specific Python installation * `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/` * `-DPYTHON_LIBRARY=/location/of/libpython*.so` * `-DPYTHON_FRAMEWORKS=/usr/local/Cellar/python3/3.3.2/Frameworks/Python.framework/` (MacOS only) -##### Only used when building with ImageMagick enabled: +#### Options only relevant when building with ImageMagick * `-DMAGICKCORE_HDRI_ENABLE=1` (default `0`) * `-DMAGICKCORE_QUANTUM_DEPTH=8` (default `16`) From 000c9d6d4c4b0cc5cd7ba924507152f3fe73f989 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 13:12:42 -0500 Subject: [PATCH 72/86] - Refactoring all Timeline drawing code into the Clip class - Making Clip a proper Reader (so it can be used directly, instead of a Timeline) --- include/Clip.h | 21 +++- src/Clip.cpp | 299 ++++++++++++++++++++++++++++++++++++++++++++++- src/Timeline.cpp | 261 ++--------------------------------------- 3 files changed, 323 insertions(+), 258 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index 0c98526a0..a609cb85c 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -92,7 +92,7 @@ namespace openshot { * c2.alpha.AddPoint(384, 1.0); // Animate the alpha to visible (between frame #360 and frame #384) * @endcode */ - class Clip : public openshot::ClipBase { + class Clip : public openshot::ClipBase, public openshot::ReaderBase { protected: /// Section lock for multiple threads juce::CriticalSection getFrameCriticalSection; @@ -100,6 +100,7 @@ namespace openshot { private: bool waveform; ///< Should a waveform be used instead of the clip's image std::list effects; /// Is Reader opened // Audio resampler (if time mapping) openshot::AudioResampler *resampler; @@ -117,6 +118,9 @@ namespace openshot { /// Apply effects to the source frame (if any) std::shared_ptr apply_effects(std::shared_ptr frame); + /// Apply keyframes to the source frame (if any) + std::shared_ptr apply_keyframes(std::shared_ptr frame); + /// Get file extension std::string get_file_extension(std::string path); @@ -132,6 +136,9 @@ namespace openshot { /// Update default rotation from reader void init_reader_rotation(); + /// Compare 2 floating point numbers + bool isEqual(double a, double b); + /// Sort effects by order void sort_effects(); @@ -159,6 +166,18 @@ namespace openshot { /// Destructor virtual ~Clip(); + + /// Get the cache object used by this reader (always returns NULL for this object) + CacheMemory* GetCache() override { return NULL; }; + + /// Determine if reader is open or closed + bool IsOpen() override { return is_open; }; + + /// Return the type name of the class + std::string Name() override { return "Clip"; }; + + + /// @brief Add an effect to the clip /// @param effect Add an effect to the clip. An effect can modify the audio or video of an openshot::Frame. void AddEffect(openshot::EffectBase* effect); diff --git a/src/Clip.cpp b/src/Clip.cpp index 1f7382828..8e1435477 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -134,14 +134,14 @@ void Clip::init_reader_rotation() { } // Default Constructor for a clip -Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL) +Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); } // Constructor with reader -Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL) +Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); @@ -158,7 +158,7 @@ Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), alloca } // Constructor with filepath -Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL) +Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false) { // Init all default settings init_settings(); @@ -262,6 +262,10 @@ void Clip::Open() { // Open the reader reader->Open(); + is_open = true; + + // Copy Reader info to Clip + info = reader->info; // Set some clip properties from the file reader if (end == 0.0) @@ -275,6 +279,7 @@ void Clip::Open() // Close the internal reader void Clip::Close() { + is_open = false; if (reader) { ZmqLogger::Instance()->AppendDebugMethod("Clip::Close"); @@ -311,6 +316,10 @@ float Clip::End() const // Get an openshot::Frame object for a specific frame number of this reader. std::shared_ptr Clip::GetFrame(int64_t requested_frame) { + // Check for open reader (or throw exception) + if (!is_open) + throw ReaderClosed("The Clip is closed. Call Open() before calling this method", "N/A"); + if (reader) { // Adjust out of bounds frame number @@ -363,6 +372,9 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Apply effects to the frame (if any) apply_effects(frame); + // Apply keyframe / transforms + apply_keyframes(frame); + // Return processed 'frame' return frame; } @@ -1057,3 +1069,284 @@ std::shared_ptr Clip::apply_effects(std::shared_ptr frame) // Return modified frame return frame; } + +// Compare 2 floating point numbers for equality +bool Clip::isEqual(double a, double b) +{ + return fabs(a - b) < 0.000001; +} + + +// Apply keyframes to the source frame (if any) +std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) +{ + // Get actual frame image data + std::shared_ptr source_image = frame->GetImage(); + + /* REPLACE IMAGE WITH WAVEFORM IMAGE (IF NEEDED) */ + if (Waveform()) + { + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Generate Waveform Image)", "frame->number", frame->number, "Waveform()", Waveform()); + + // Get the color of the waveform + int red = wave_color.red.GetInt(frame->number); + int green = wave_color.green.GetInt(frame->number); + int blue = wave_color.blue.GetInt(frame->number); + int alpha = wave_color.alpha.GetInt(frame->number); + + // Generate Waveform Dynamically (the size of the timeline) + source_image = frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); + frame->AddImage(std::shared_ptr(source_image)); + } + + /* ALPHA & OPACITY */ + if (alpha.GetValue(frame->number) != 1.0) + { + float alpha_value = alpha.GetValue(frame->number); + + // Get source image's pixels + unsigned char *pixels = (unsigned char *) source_image->bits(); + + // Loop through pixels + for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) + { + // Apply alpha to pixel + pixels[byte_index + 3] *= alpha_value; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); + } + + /* RESIZE SOURCE IMAGE - based on scale type */ + QSize source_size = source_image->size(); + switch (scale) + { + case (SCALE_FIT): { + // keep aspect ratio + source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_STRETCH): { + // ignore aspect ratio + source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_CROP): { + QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); + QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); + + // respect aspect ratio + if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) + source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); + else + source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + case (SCALE_NONE): { + // Calculate ratio of source size to project size + // Even with no scaling, previews need to be adjusted correctly + // (otherwise NONE scaling draws the frame image outside of the preview) + float source_width_ratio = source_size.width() / float(Settings::Instance()->MAX_WIDTH); + float source_height_ratio = source_size.height() / float(Settings::Instance()->MAX_HEIGHT); + source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + break; + } + } + + float crop_x_value = crop_x.GetValue(frame->number); + float crop_y_value = crop_y.GetValue(frame->number); + float crop_w_value = crop_width.GetValue(frame->number); + float crop_h_value = crop_height.GetValue(frame->number); + switch(crop_gravity) + { + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; + case (GRAVITY_TOP): + crop_x_value += 0.5; + break; + case (GRAVITY_TOP_RIGHT): + crop_x_value += 1.0; + break; + case (GRAVITY_LEFT): + crop_y_value += 0.5; + break; + case (GRAVITY_CENTER): + crop_x_value += 0.5; + crop_y_value += 0.5; + break; + case (GRAVITY_RIGHT): + crop_x_value += 1.0; + crop_y_value += 0.5; + break; + case (GRAVITY_BOTTOM_LEFT): + crop_y_value += 1.0; + break; + case (GRAVITY_BOTTOM): + crop_x_value += 0.5; + crop_y_value += 1.0; + break; + case (GRAVITY_BOTTOM_RIGHT): + crop_x_value += 1.0; + crop_y_value += 1.0; + break; + } + + /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ + float x = 0.0; // left + float y = 0.0; // top + + // Adjust size for scale x and scale y + float sx = scale_x.GetValue(frame->number); // percentage X scale + float sy = scale_y.GetValue(frame->number); // percentage Y scale + float scaled_source_width = source_size.width() * sx; + float scaled_source_height = source_size.height() * sy; + + switch (gravity) + { + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; + case (GRAVITY_TOP): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + break; + case (GRAVITY_TOP_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + break; + case (GRAVITY_LEFT): + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_CENTER): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + break; + case (GRAVITY_BOTTOM_LEFT): + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + case (GRAVITY_BOTTOM): + x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + case (GRAVITY_BOTTOM_RIGHT): + x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + break; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); + + /* LOCATION, ROTATION, AND SCALE */ + float r = rotation.GetValue(frame->number); // rotate in degrees + x += (Settings::Instance()->MAX_WIDTH * location_x.GetValue(frame->number)); // move in percentage of final width + y += (Settings::Instance()->MAX_HEIGHT * location_y.GetValue(frame->number)); // move in percentage of final height + float shear_x_value = shear_x.GetValue(frame->number); + float shear_y_value = shear_y.GetValue(frame->number); + float origin_x_value = origin_x.GetValue(frame->number); + float origin_y_value = origin_y.GetValue(frame->number); + + bool transformed = false; + QTransform transform; + + // Transform source image (if needed) + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); + + if (!isEqual(x, 0) || !isEqual(y, 0)) { + // TRANSLATE/MOVE CLIP + transform.translate(x, y); + transformed = true; + } + + if (!isEqual(r, 0) || !isEqual(shear_x_value, 0) || !isEqual(shear_y_value, 0)) { + // ROTATE CLIP (around origin_x, origin_y) + float origin_x_offset = (scaled_source_width * origin_x_value); + float origin_y_offset = (scaled_source_height * origin_y_value); + transform.translate(origin_x_offset, origin_y_offset); + transform.rotate(r); + transform.shear(shear_x_value, shear_y_value); + transform.translate(-origin_x_offset,-origin_y_offset); + transformed = true; + } + + // SCALE CLIP (if needed) + float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx; + float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy; + + if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) { + transform.scale(source_width_scale, source_height_scale); + transformed = true; + } + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); + + /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ + std::shared_ptr new_image; + new_image = std::shared_ptr(new QImage(*source_image)); + new_image->fill(QColor(QString::fromStdString("#00000000"))); + + // Load timeline's new frame image into a QPainter + QPainter painter(new_image.get()); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); + + // Apply transform (translate, rotate, scale)... if any + if (transformed) + painter.setTransform(transform); + + // Composite a new layer onto the image + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(0, 0, *source_image, crop_x_value * source_image->width(), crop_y_value * source_image->height(), crop_w_value * source_image->width(), crop_h_value * source_image->height()); + + // Draw frame #'s on top of image (if needed) + if (display != FRAME_DISPLAY_NONE) { + std::stringstream frame_number_str; + switch (display) + { + case (FRAME_DISPLAY_NONE): + // This is only here to prevent unused-enum warnings + break; + + case (FRAME_DISPLAY_CLIP): + frame_number_str << frame->number; + break; + + case (FRAME_DISPLAY_TIMELINE): + frame_number_str << "N/A"; + break; + + case (FRAME_DISPLAY_BOTH): + frame_number_str << "N/A" << " (" << frame->number << ")"; + break; + } + + // Draw frame number on top of image + painter.setPen(QColor("#ffffff")); + painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + } + + painter.end(); + + // Add new QImage to frame + frame->AddImage(new_image); + + // Return modified frame + return frame; +} diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 9ccf9a84e..42009a22f 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -482,25 +482,6 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer", "new_frame->number", new_frame->number, "clip_frame_number", clip_frame_number, "timeline_frame_number", timeline_frame_number); - /* REPLACE IMAGE WITH WAVEFORM IMAGE (IF NEEDED) */ - if (source_clip->Waveform()) - { - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Generate Waveform Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); - - // Get the color of the waveform - int red = source_clip->wave_color.red.GetInt(clip_frame_number); - int green = source_clip->wave_color.green.GetInt(clip_frame_number); - int blue = source_clip->wave_color.blue.GetInt(clip_frame_number); - int alpha = source_clip->wave_color.alpha.GetInt(clip_frame_number); - - // Generate Waveform Dynamically (the size of the timeline) - std::shared_ptr source_image; - #pragma omp critical (T_addLayer) - source_image = source_frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); - source_frame->AddImage(std::shared_ptr(source_image)); - } - /* Apply effects to the source frame (if any). If multiple clips are overlapping, only process the * effects on the top clip. */ if (is_top_clip && source_frame) { @@ -571,7 +552,6 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in else // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (No Audio Copied - Wrong # of Channels)", "source_clip->Reader()->info.has_audio", source_clip->Reader()->info.has_audio, "source_frame->GetAudioChannelsCount()", source_frame->GetAudioChannelsCount(), "info.channels", info.channels, "clip_frame_number", clip_frame_number, "timeline_frame_number", timeline_frame_number); - } // Skip out if video was disabled or only an audio frame (no visualisation in use) @@ -586,253 +566,26 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Get actual frame image data source_image = source_frame->GetImage(); - /* ALPHA & OPACITY */ - if (source_clip->alpha.GetValue(clip_frame_number) != 1.0) - { - float alpha = source_clip->alpha.GetValue(clip_frame_number); - - // Get source image's pixels - unsigned char *pixels = (unsigned char *) source_image->bits(); - - // Loop through pixels - for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) - { - // Apply alpha to pixel - pixels[byte_index + 3] *= alpha; - } - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Set Alpha & Opacity)", "alpha", alpha, "source_frame->number", source_frame->number, "clip_frame_number", clip_frame_number); - } - - /* RESIZE SOURCE IMAGE - based on scale type */ - QSize source_size = source_image->size(); - switch (source_clip->scale) - { - case (SCALE_FIT): { - // keep aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_FIT)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_STRETCH): { - // ignore aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_STRETCH)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_CROP): { - QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); - QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); - - // respect aspect ratio - if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) - source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); - else - source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_CROP)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - case (SCALE_NONE): { - // Calculate ratio of source size to project size - // Even with no scaling, previews need to be adjusted correctly - // (otherwise NONE scaling draws the frame image outside of the preview) - float source_width_ratio = source_size.width() / float(info.width); - float source_height_ratio = source_size.height() / float(info.height); - source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Scale: SCALE_NONE)", "source_frame->number", source_frame->number, "source_width", source_size.width(), "source_height", source_size.height()); - break; - } - } - - float crop_x = source_clip->crop_x.GetValue(clip_frame_number); - float crop_y = source_clip->crop_y.GetValue(clip_frame_number); - float crop_w = source_clip->crop_width.GetValue(clip_frame_number); - float crop_h = source_clip->crop_height.GetValue(clip_frame_number); - switch(source_clip->crop_gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - crop_x += 0.5; - break; - case (GRAVITY_TOP_RIGHT): - crop_x += 1.0; - break; - case (GRAVITY_LEFT): - crop_y += 0.5; - break; - case (GRAVITY_CENTER): - crop_x += 0.5; - crop_y += 0.5; - break; - case (GRAVITY_RIGHT): - crop_x += 1.0; - crop_y += 0.5; - break; - case (GRAVITY_BOTTOM_LEFT): - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM): - crop_x += 0.5; - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM_RIGHT): - crop_x += 1.0; - crop_y += 1.0; - break; - } - - - /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ - float x = 0.0; // left - float y = 0.0; // top - - // Adjust size for scale x and scale y - float sx = source_clip->scale_x.GetValue(clip_frame_number); // percentage X scale - float sy = source_clip->scale_y.GetValue(clip_frame_number); // percentage Y scale - float scaled_source_width = source_size.width() * sx; - float scaled_source_height = source_size.height() * sy; - - switch (source_clip->gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - break; - case (GRAVITY_TOP_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - break; - case (GRAVITY_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_CENTER): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center - break; - case (GRAVITY_BOTTOM_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - case (GRAVITY_BOTTOM): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - case (GRAVITY_BOTTOM_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom - break; - } - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Gravity)", "source_frame->number", source_frame->number, "source_clip->gravity", source_clip->gravity, "info.width", info.width, "scaled_source_width", scaled_source_width, "info.height", info.height, "scaled_source_height", scaled_source_height); - - /* LOCATION, ROTATION, AND SCALE */ - float r = source_clip->rotation.GetValue(clip_frame_number); // rotate in degrees - x += (Settings::Instance()->MAX_WIDTH * source_clip->location_x.GetValue(clip_frame_number)); // move in percentage of final width - y += (Settings::Instance()->MAX_HEIGHT * source_clip->location_y.GetValue(clip_frame_number)); // move in percentage of final height - float shear_x = source_clip->shear_x.GetValue(clip_frame_number); - float shear_y = source_clip->shear_y.GetValue(clip_frame_number); - float origin_x = source_clip->origin_x.GetValue(clip_frame_number); - float origin_y = source_clip->origin_y.GetValue(clip_frame_number); - - bool transformed = false; - QTransform transform; - - // Transform source image (if needed) - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Build QTransform - if needed)", "source_frame->number", source_frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); - - if (!isEqual(x, 0) || !isEqual(y, 0)) { - // TRANSLATE/MOVE CLIP - transform.translate(x, y); - transformed = true; - } - - if (!isEqual(r, 0) || !isEqual(shear_x, 0) || !isEqual(shear_y, 0)) { - // ROTATE CLIP (around origin_x, origin_y) - float origin_x_value = (scaled_source_width * origin_x); - float origin_y_value = (scaled_source_height * origin_y); - transform.translate(origin_x_value, origin_y_value); - transform.rotate(r); - transform.shear(shear_x, shear_y); - transform.translate(-origin_x_value,-origin_y_value); - transformed = true; - } - - // SCALE CLIP (if needed) - float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx; - float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy; - - if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) { - transform.scale(source_width_scale, source_height_scale); - transformed = true; - } - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; - #pragma omp critical (T_addLayer) - new_image = new_frame->GetImage(); + new_image = new_frame->GetImage(); // Load timeline's new frame image into a QPainter QPainter painter(new_image.get()); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); - - // Apply transform (translate, rotate, scale)... if any - if (transformed) - painter.setTransform(transform); // Composite a new layer onto the image painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, crop_x * source_image->width(), crop_y * source_image->height(), crop_w * source_image->width(), crop_h * source_image->height()); - - // Draw frame #'s on top of image (if needed) - if (source_clip->display != FRAME_DISPLAY_NONE) { - std::stringstream frame_number_str; - switch (source_clip->display) - { - case (FRAME_DISPLAY_NONE): - // This is only here to prevent unused-enum warnings - break; - - case (FRAME_DISPLAY_CLIP): - frame_number_str << clip_frame_number; - break; - - case (FRAME_DISPLAY_TIMELINE): - frame_number_str << timeline_frame_number; - break; - - case (FRAME_DISPLAY_BOTH): - frame_number_str << timeline_frame_number << " (" << clip_frame_number << ")"; - break; - } - - // Draw frame number on top of image - painter.setPen(QColor("#ffffff")); - painter.drawText(20, 20, QString(frame_number_str.str().c_str())); - } - + painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); painter.end(); + // Add new QImage to frame + new_frame->AddImage(new_image); + // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); } // Update the list of 'opened' clips From eb328f119020b189baebc87fbddff1d8930d73cf Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 17:05:50 -0500 Subject: [PATCH 73/86] Refactored the Settings::Instance()->MAX_WIDTH and Settings::Instance()->MAX_HEIGHT out of the Cilp class. GetFrame() now has an overload which specifies the width, height, and samples needed. Otherwise, it returns the Clip image based on the source reader (width, height, num samples). --- include/Clip.h | 16 +++++++- src/Clip.cpp | 96 +++++++++++++++++++++++++++++++----------------- src/Timeline.cpp | 6 ++- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index a609cb85c..fc8c828e0 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -119,7 +119,7 @@ namespace openshot { std::shared_ptr apply_effects(std::shared_ptr frame); /// Apply keyframes to the source frame (if any) - std::shared_ptr apply_keyframes(std::shared_ptr frame); + std::shared_ptr apply_keyframes(std::shared_ptr frame, int width, int height); /// Get file extension std::string get_file_extension(std::string path); @@ -191,12 +191,24 @@ namespace openshot { /// Look up an effect by ID openshot::EffectBase* GetEffect(const std::string& id); - /// @brief Get an openshot::Frame object for a specific frame number of this timeline. + /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number + /// of samples match the source reader. /// /// @returns The requested frame (containing the image) /// @param requested_frame The frame number that is requested std::shared_ptr GetFrame(int64_t requested_frame); + /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number + /// of samples can be customized to match the Timeline, or any custom output. Extra samples will be moved to the + /// next Frame. Missing samples will be moved from the next Frame. + /// + /// @returns The requested frame (containing the image) + /// @param requested_frame The frame number that is requested + /// @param width The width of the image requested + /// @param height The height of the image requested + /// @param samples The number of samples requested + std::shared_ptr GetFrame(int64_t requested_frame, int width, int height, int samples); + /// Open the internal reader void Open(); diff --git a/src/Clip.cpp b/src/Clip.cpp index 8e1435477..4aead66d0 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -325,6 +325,33 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Adjust out of bounds frame number requested_frame = adjust_frame_number_minimum(requested_frame); + // Is a time map detected + int64_t new_frame_number = requested_frame; + int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); + if (time.GetLength() > 1) + new_frame_number = time_mapped_number; + + // Get the # of audio samples from the time mapped Frame instance + std::shared_ptr time_mapped_original_frame = GetOrCreateFrame(new_frame_number); + return GetFrame(requested_frame, reader->info.width, reader->info.height, time_mapped_original_frame->GetAudioSamplesCount()); + } + else + // Throw error if reader not initialized + throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method."); +} + +// Get an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int height, int samples) +{ + // Check for open reader (or throw exception) + if (!is_open) + throw ReaderClosed("The Clip is closed. Call Open() before calling this method", "N/A"); + + if (reader) + { + // Adjust out of bounds frame number + requested_frame = adjust_frame_number_minimum(requested_frame); + // Adjust has_video and has_audio overrides int enabled_audio = has_audio.GetInt(requested_frame); if (enabled_audio == -1 && reader && reader->info.has_audio) @@ -367,13 +394,17 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) frame->AddAudio(true, channel, 0, original_frame->GetAudioSamples(channel), original_frame->GetAudioSamplesCount(), 1.0); // Get time mapped frame number (used to increase speed, change direction, etc...) + // TODO: Handle variable # of samples, since this resamples audio for different speeds (only when time curve is set) get_time_mapped_frame(frame, requested_frame); + // Adjust # of samples to match requested (the interaction with time curves will make this tricky) + // TODO: Implement move samples to/from next frame + // Apply effects to the frame (if any) apply_effects(frame); // Apply keyframe / transforms - apply_keyframes(frame); + apply_keyframes(frame, width, height); // Return processed 'frame' return frame; @@ -646,13 +677,9 @@ int64_t Clip::adjust_frame_number_minimum(int64_t frame_number) std::shared_ptr Clip::GetOrCreateFrame(int64_t number) { std::shared_ptr new_frame; - - // Init some basic properties about this frame - int samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels); - try { // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number, "samples_in_frame", samples_in_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number); // Attempt to get a frame (but this could fail if a reader has just been closed) new_frame = reader->GetFrame(number); @@ -669,14 +696,17 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number) // ... } + // Estimate # of samples needed for this frame + int estimated_samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels); + // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "samples_in_frame", samples_in_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "estimated_samples_in_frame", estimated_samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", samples_in_frame, reader->info.channels); + new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); new_frame->SampleRate(reader->info.sample_rate); new_frame->ChannelsLayout(reader->info.channel_layout); - new_frame->AddAudioSilence(samples_in_frame); + new_frame->AddAudioSilence(estimated_samples_in_frame); return new_frame; } @@ -1078,7 +1108,7 @@ bool Clip::isEqual(double a, double b) // Apply keyframes to the source frame (if any) -std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) +std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int width, int height) { // Get actual frame image data std::shared_ptr source_image = frame->GetImage(); @@ -1096,7 +1126,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) int alpha = wave_color.alpha.GetInt(frame->number); // Generate Waveform Dynamically (the size of the timeline) - source_image = frame->GetWaveform(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, red, green, blue, alpha); + source_image = frame->GetWaveform(width, height, red, green, blue, alpha); frame->AddImage(std::shared_ptr(source_image)); } @@ -1125,7 +1155,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) { case (SCALE_FIT): { // keep aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::KeepAspectRatio); + source_size.scale(width, height, Qt::KeepAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); @@ -1133,18 +1163,18 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) } case (SCALE_STRETCH): { // ignore aspect ratio - source_size.scale(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, Qt::IgnoreAspectRatio); + source_size.scale(width, height, Qt::IgnoreAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_CROP): { - QSize width_size(Settings::Instance()->MAX_WIDTH, round(Settings::Instance()->MAX_WIDTH / (float(source_size.width()) / float(source_size.height())))); - QSize height_size(round(Settings::Instance()->MAX_HEIGHT / (float(source_size.height()) / float(source_size.width()))), Settings::Instance()->MAX_HEIGHT); + QSize width_size(width, round(width / (float(source_size.width()) / float(source_size.height())))); + QSize height_size(round(height / (float(source_size.height()) / float(source_size.width()))), height); // respect aspect ratio - if (width_size.width() >= Settings::Instance()->MAX_WIDTH && width_size.height() >= Settings::Instance()->MAX_HEIGHT) + if (width_size.width() >= width && width_size.height() >= height) source_size.scale(width_size.width(), width_size.height(), Qt::KeepAspectRatio); else source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); @@ -1157,9 +1187,9 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) // Calculate ratio of source size to project size // Even with no scaling, previews need to be adjusted correctly // (otherwise NONE scaling draws the frame image outside of the preview) - float source_width_ratio = source_size.width() / float(Settings::Instance()->MAX_WIDTH); - float source_height_ratio = source_size.height() / float(Settings::Instance()->MAX_HEIGHT); - source_size.scale(Settings::Instance()->MAX_WIDTH * source_width_ratio, Settings::Instance()->MAX_HEIGHT * source_height_ratio, Qt::KeepAspectRatio); + float source_width_ratio = source_size.width() / float(width); + float source_height_ratio = source_size.height() / float(height); + source_size.scale(width * source_width_ratio, height * source_height_ratio, Qt::KeepAspectRatio); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); @@ -1222,32 +1252,32 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) // This is only here to prevent unused-enum warnings break; case (GRAVITY_TOP): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center + x = (width - scaled_source_width) / 2.0; // center break; case (GRAVITY_TOP_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right + x = width - scaled_source_width; // right break; case (GRAVITY_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_CENTER): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + x = (width - scaled_source_width) / 2.0; // center + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height) / 2.0; // center + x = width - scaled_source_width; // right + y = (height - scaled_source_height) / 2.0; // center break; case (GRAVITY_BOTTOM_LEFT): - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + y = (height - scaled_source_height); // bottom break; case (GRAVITY_BOTTOM): - x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + x = (width - scaled_source_width) / 2.0; // center + y = (height - scaled_source_height); // bottom break; case (GRAVITY_BOTTOM_RIGHT): - x = Settings::Instance()->MAX_WIDTH - scaled_source_width; // right - y = (Settings::Instance()->MAX_HEIGHT - scaled_source_height); // bottom + x = width - scaled_source_width; // right + y = (height - scaled_source_height); // bottom break; } @@ -1256,8 +1286,8 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame) /* LOCATION, ROTATION, AND SCALE */ float r = rotation.GetValue(frame->number); // rotate in degrees - x += (Settings::Instance()->MAX_WIDTH * location_x.GetValue(frame->number)); // move in percentage of final width - y += (Settings::Instance()->MAX_HEIGHT * location_y.GetValue(frame->number)); // move in percentage of final height + x += (width * location_x.GetValue(frame->number)); // move in percentage of final width + y += (height * location_y.GetValue(frame->number)); // move in percentage of final height float shear_x_value = shear_x.GetValue(frame->number); float shear_y_value = shear_y.GetValue(frame->number); float origin_x_value = origin_x.GetValue(frame->number); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 42009a22f..cf3f0cee0 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -441,7 +441,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number)); + new_frame = std::shared_ptr(clip->GetFrame(number, info.width, info.height, samples_in_frame)); // Return real frame return new_frame; @@ -737,8 +737,10 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Get clip frame # long clip_start_frame = (clip->Start() * info.fps.ToDouble()) + 1; long clip_frame_number = frame_number - clip_start_position + clip_start_frame; + int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); + // Cache clip object - clip->GetFrame(clip_frame_number); + clip->GetFrame(clip_frame_number, info.width, info.height, samples_in_frame); } } } From 6da4e8fded5e31e0923ea41a32dc128586c44ad3 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 26 Aug 2020 22:47:31 -0500 Subject: [PATCH 74/86] Fixed a bug with cropping logic on Clip (disabled it temporarily). I need to replace the Crop functionality with a more robust cropping tool. Also, updated Timeline to use the MaxWidth/MaxHeight settings when calling the clip (since those are set when the screen is resized). --- include/Clip.h | 9 ++++++--- src/Clip.cpp | 39 +++++++++++++++++++++++++++++---------- src/Timeline.cpp | 4 ++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index fc8c828e0..fe4effab3 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -116,10 +116,10 @@ namespace openshot { int64_t adjust_frame_number_minimum(int64_t frame_number); /// Apply effects to the source frame (if any) - std::shared_ptr apply_effects(std::shared_ptr frame); + void apply_effects(std::shared_ptr frame); /// Apply keyframes to the source frame (if any) - std::shared_ptr apply_keyframes(std::shared_ptr frame, int width, int height); + void apply_keyframes(std::shared_ptr frame, int width, int height); /// Get file extension std::string get_file_extension(std::string path); @@ -146,6 +146,9 @@ namespace openshot { void reverse_buffer(juce::AudioSampleBuffer* buffer); public: + /// Final cache object used to hold final frames + CacheMemory final_cache; + openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent openshot::AnchorType anchor; ///< The anchor determines what parent a clip should snap to @@ -168,7 +171,7 @@ namespace openshot { /// Get the cache object used by this reader (always returns NULL for this object) - CacheMemory* GetCache() override { return NULL; }; + CacheMemory* GetCache() override { return &final_cache; }; /// Determine if reader is open or closed bool IsOpen() override { return is_open; }; diff --git a/src/Clip.cpp b/src/Clip.cpp index 4aead66d0..0a38c6b4b 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -107,6 +107,9 @@ void Clip::init_settings() // Init audio and video overrides has_audio = Keyframe(-1.0); has_video = Keyframe(-1.0); + + // Initialize Clip cache + final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); } // Init reader's rotation (if any) @@ -352,6 +355,16 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he // Adjust out of bounds frame number requested_frame = adjust_frame_number_minimum(requested_frame); + // Check the cache for this frame + std::shared_ptr cached_frame = final_cache.GetFrame(requested_frame); + if (cached_frame) { + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", requested_frame); + + // Return the cached frame + return cached_frame; + } + // Adjust has_video and has_audio overrides int enabled_audio = has_audio.GetInt(requested_frame); if (enabled_audio == -1 && reader && reader->info.has_audio) @@ -406,6 +419,9 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he // Apply keyframe / transforms apply_keyframes(frame, width, height); + // Cache frame + final_cache.Add(frame); + // Return processed 'frame' return frame; } @@ -889,6 +905,9 @@ void Clip::SetJsonValue(const Json::Value root) { // Set parent data ClipBase::SetJsonValue(root); + // Clear cache + final_cache.Clear(); + // Set data from Json (if key is found) if (!root["gravity"].isNull()) gravity = (GravityType) root["gravity"].asInt(); @@ -1077,16 +1096,22 @@ void Clip::AddEffect(EffectBase* effect) // Sort effects sort_effects(); + + // Clear cache + final_cache.Clear(); } // Remove an effect from the clip void Clip::RemoveEffect(EffectBase* effect) { effects.remove(effect); + + // Clear cache + final_cache.Clear(); } // Apply effects to the source frame (if any) -std::shared_ptr Clip::apply_effects(std::shared_ptr frame) +void Clip::apply_effects(std::shared_ptr frame) { // Find Effects at this position and layer for (auto effect : effects) @@ -1095,9 +1120,6 @@ std::shared_ptr Clip::apply_effects(std::shared_ptr frame) frame = effect->GetFrame(frame, frame->number); } // end effect loop - - // Return modified frame - return frame; } // Compare 2 floating point numbers for equality @@ -1108,7 +1130,7 @@ bool Clip::isEqual(double a, double b) // Apply keyframes to the source frame (if any) -std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int width, int height) +void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) { // Get actual frame image data std::shared_ptr source_image = frame->GetImage(); @@ -1330,7 +1352,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; - new_image = std::shared_ptr(new QImage(*source_image)); + new_image = std::shared_ptr(new QImage(QSize(width, height), source_image->format())); new_image->fill(QColor(QString::fromStdString("#00000000"))); // Load timeline's new frame image into a QPainter @@ -1343,7 +1365,7 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w // Composite a new layer onto the image painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, crop_x_value * source_image->width(), crop_y_value * source_image->height(), crop_w_value * source_image->width(), crop_h_value * source_image->height()); + painter.drawImage(0, 0, *source_image); // Draw frame #'s on top of image (if needed) if (display != FRAME_DISPLAY_NONE) { @@ -1376,7 +1398,4 @@ std::shared_ptr Clip::apply_keyframes(std::shared_ptr frame, int w // Add new QImage to frame frame->AddImage(new_image); - - // Return modified frame - return frame; } diff --git a/src/Timeline.cpp b/src/Timeline.cpp index cf3f0cee0..ac36be063 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -441,7 +441,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number, info.width, info.height, samples_in_frame)); + new_frame = std::shared_ptr(clip->GetFrame(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame)); // Return real frame return new_frame; @@ -740,7 +740,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Cache clip object - clip->GetFrame(clip_frame_number, info.width, info.height, samples_in_frame); + clip->GetFrame(clip_frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame); } } } From 8387b124ad8a7b371f2a09dc6732ef5e2095b03c Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 4 Oct 2020 16:59:21 -0500 Subject: [PATCH 75/86] Large refactor of Timeline, TimelineBase, ClipBase, and Clip, to allow a Clip access to the parent timeline instance (if available), and thus, certain properties (preview size, timeline FPS, etc...). This allows for a simpler rendering of Clip keyframes (during the Clip::GetFrame method), and a simpler Timeline class, that can change the preview window size dynamically and no longer requires a Singleton Settings class. - Also removed "crop" from Clip class, as it was never implmeneted correctly, and we have a fully functional "crop" effect when needed - Added caching to Clip class, to optimize previewing of cached frames (much faster than previous) --- include/Clip.h | 35 ++--- include/ClipBase.h | 26 ++++ include/EffectBase.h | 21 ++- include/OpenShot.h | 1 + include/ReaderBase.h | 6 +- include/Timeline.h | 4 +- include/TimelineBase.h | 47 +++++++ include/effects/Bars.h | 20 ++- include/effects/Blur.h | 20 ++- include/effects/Brightness.h | 20 ++- include/effects/ChromaKey.h | 20 ++- include/effects/ColorShift.h | 20 ++- include/effects/Crop.h | 20 ++- include/effects/Deinterlace.h | 20 ++- include/effects/Hue.h | 20 ++- include/effects/Mask.h | 20 ++- include/effects/Negate.h | 20 ++- include/effects/Pixelate.h | 20 ++- include/effects/Saturation.h | 20 ++- include/effects/Shift.h | 20 ++- include/effects/Wave.h | 20 ++- src/CMakeLists.txt | 1 + src/ChunkReader.cpp | 2 - src/ChunkWriter.cpp | 8 -- src/Clip.cpp | 231 +++++++++++++-------------------- src/EffectBase.cpp | 10 ++ src/FFmpegReader.cpp | 13 +- src/Qt/PlayerDemo.cpp | 5 - src/QtImageReader.cpp | 13 +- src/ReaderBase.cpp | 12 +- src/Settings.cpp | 2 - src/Timeline.cpp | 81 +++++++----- src/TimelineBase.cpp | 33 +++++ src/bindings/python/openshot.i | 2 + src/bindings/ruby/openshot.i | 2 + 35 files changed, 509 insertions(+), 326 deletions(-) create mode 100644 include/TimelineBase.h create mode 100644 src/TimelineBase.cpp diff --git a/include/Clip.h b/include/Clip.h index fe4effab3..24d4c599a 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -42,6 +42,7 @@ #include "Effects.h" #include "EffectInfo.h" #include "Fraction.h" +#include "Frame.h" #include "KeyFrame.h" #include "ReaderBase.h" #include "JuceHeader.h" @@ -146,9 +147,6 @@ namespace openshot { void reverse_buffer(juce::AudioSampleBuffer* buffer); public: - /// Final cache object used to hold final frames - CacheMemory final_cache; - openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent openshot::AnchorType anchor; ///< The anchor determines what parent a clip should snap to @@ -169,9 +167,8 @@ namespace openshot { /// Destructor virtual ~Clip(); - - /// Get the cache object used by this reader (always returns NULL for this object) - CacheMemory* GetCache() override { return &final_cache; }; + /// Get the cache object used by this clip + CacheMemory* GetCache() { return &cache; }; /// Determine if reader is open or closed bool IsOpen() override { return is_open; }; @@ -197,20 +194,21 @@ namespace openshot { /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number /// of samples match the source reader. /// - /// @returns The requested frame (containing the image) - /// @param requested_frame The frame number that is requested - std::shared_ptr GetFrame(int64_t requested_frame); + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number); /// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number /// of samples can be customized to match the Timeline, or any custom output. Extra samples will be moved to the /// next Frame. Missing samples will be moved from the next Frame. /// - /// @returns The requested frame (containing the image) - /// @param requested_frame The frame number that is requested - /// @param width The width of the image requested - /// @param height The height of the image requested - /// @param samples The number of samples requested - std::shared_ptr GetFrame(int64_t requested_frame, int width, int height, int samples); + /// A new openshot::Frame objects is returned, based on a copy from the source image, with all keyframes and clip effects + /// rendered. + /// + /// @returns The modified openshot::Frame object + /// @param frame This is ignored on Clip, due to caching optimizations. This frame instance is clobbered with the source frame. + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Open the internal reader void Open(); @@ -265,13 +263,6 @@ namespace openshot { /// Curve representing the color of the audio wave form openshot::Color wave_color; - // Crop settings and curves - openshot::GravityType crop_gravity; ///< Cropping needs to have a gravity to determine what side we are cropping - openshot::Keyframe crop_width; ///< Curve representing width in percent (0.0=0%, 1.0=100%) - openshot::Keyframe crop_height; ///< Curve representing height in percent (0.0=0%, 1.0=100%) - openshot::Keyframe crop_x; ///< Curve representing X offset in percent (-1.0=-100%, 0.0=0%, 1.0=100%) - openshot::Keyframe crop_y; ///< Curve representing Y offset in percent (-1.0=-100%, 0.0=0%, 1.0=100%) - // Perspective curves openshot::Keyframe perspective_c1_x; ///< Curves representing X for coordinate 1 openshot::Keyframe perspective_c1_y; ///< Curves representing Y for coordinate 1 diff --git a/include/ClipBase.h b/include/ClipBase.h index 1f7f55c47..e335c501b 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -33,10 +33,13 @@ #include #include +#include "CacheMemory.h" #include "Exceptions.h" +#include "Frame.h" #include "Point.h" #include "KeyFrame.h" #include "Json.h" +#include "TimelineBase.h" namespace openshot { @@ -54,6 +57,7 @@ namespace openshot { float start; ///< The position in seconds to start playing (used to trim the beginning of a clip) float end; ///< The position in seconds to end playing (used to trim the ending of a clip) std::string previous_properties; ///< This string contains the previous JSON properties + openshot::TimelineBase* timeline; ///< Pointer to the parent timeline instance (if any) /// Generate JSON for a property Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const; @@ -62,6 +66,7 @@ namespace openshot { Json::Value add_property_choice_json(std::string name, int value, int selected_value) const; public: + CacheMemory cache; /// Constructor for the base clip ClipBase() { }; @@ -72,6 +77,25 @@ namespace openshot { bool operator> ( ClipBase& a) { return (Position() > a.Position()); } bool operator>= ( ClipBase& a) { return (Position() >= a.Position()); } + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + virtual std::shared_ptr GetFrame(int64_t frame_number) = 0; + + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// modified openshot::Frame object + /// + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. + /// + /// @returns The modified openshot::Frame object + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + virtual std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) = 0; + /// Get basic properties std::string Id() const { return id; } ///< Get the Id of this clip object float Position() const { return position; } ///< Get position on timeline (in seconds) @@ -79,6 +103,7 @@ namespace openshot { float Start() const { return start; } ///< Get start position (in seconds) of clip (trim start of video) float End() const { return end; } ///< Get end position (in seconds) of clip (trim end of video) float Duration() const { return end - start; } ///< Get the length of this clip (in seconds) + openshot::TimelineBase* ParentTimeline() { return timeline; } ///< Get the associated Timeline pointer (if any) /// Set basic properties void Id(std::string value) { id = value; } ///> Set the Id of this clip object @@ -86,6 +111,7 @@ namespace openshot { void Layer(int value) { layer = value; } ///< Set layer of clip on timeline (lower number is covered by higher numbers) void Start(float value) { start = value; } ///< Set start position (in seconds) of clip (trim start of video) void End(float value) { end = value; } ///< Set end position (in seconds) of clip (trim end of video) + void ParentTimeline(openshot::TimelineBase* new_timeline) { timeline = new_timeline; } ///< Set associated Timeline pointer /// Get and Set JSON methods virtual std::string Json() const = 0; ///< Generate JSON string of this object diff --git a/include/EffectBase.h b/include/EffectBase.h index 1f967a021..353e18177 100644 --- a/include/EffectBase.h +++ b/include/EffectBase.h @@ -67,6 +67,10 @@ namespace openshot { private: int order; ///< The order to evaluate this effect. Effects are processed in this order (when more than one overlap). + + protected: + openshot::ClipBase* clip; ///< Pointer to the parent clip instance (if any) + public: /// Information about the current effect @@ -78,21 +82,16 @@ namespace openshot /// Constrain a color value from 0 to 255 int constrain(int color_value); - /// @brief This method is required for all derived classes of EffectBase, and returns a - /// modified openshot::Frame object - /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). - /// - /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - virtual std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) = 0; - /// Initialize the values of the EffectInfo struct. It is important for derived classes to call /// this method, or the EffectInfo struct values will not be initialized. void InitEffectInfo(); + /// Parent clip object of this effect (which can be unparented and NULL) + openshot::ClipBase* ParentClip(); + + /// Set parent clip object of this effect + void ParentClip(openshot::ClipBase* new_clip); + /// Get and Set JSON methods virtual std::string Json() const = 0; ///< Generate JSON string of this object virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object diff --git a/include/OpenShot.h b/include/OpenShot.h index 5273ff0d3..56f847d17 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -138,6 +138,7 @@ #include "QtHtmlReader.h" #include "QtImageReader.h" #include "QtTextReader.h" +#include "TimelineBase.h" #include "Timeline.h" #include "Settings.h" diff --git a/include/ReaderBase.h b/include/ReaderBase.h index c997b76dc..6d68f7299 100644 --- a/include/ReaderBase.h +++ b/include/ReaderBase.h @@ -98,9 +98,9 @@ namespace openshot { protected: /// Section lock for multiple threads - juce::CriticalSection getFrameCriticalSection; - juce::CriticalSection processingCriticalSection; - openshot::ClipBase* parent; + juce::CriticalSection getFrameCriticalSection; + juce::CriticalSection processingCriticalSection; + openshot::ClipBase* clip; ///< Pointer to the parent clip instance (if any) public: diff --git a/include/Timeline.h b/include/Timeline.h index d5b01c0bc..227183428 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -54,6 +54,8 @@ #include "OpenMPUtilities.h" #include "ReaderBase.h" #include "Settings.h" +#include "TimelineBase.h" + namespace openshot { @@ -161,7 +163,7 @@ namespace openshot { * t.Close(); * @endcode */ - class Timeline : public openshot::ReaderBase { + class Timeline : public openshot::TimelineBase, public openshot::ReaderBase { private: bool is_open; /// + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_TIMELINE_BASE_H +#define OPENSHOT_TIMELINE_BASE_H + + +namespace openshot { + /** + * @brief This class represents a timeline (used for building generic timeline implementations) + */ + class TimelineBase { + + public: + int preview_width; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + int preview_height; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + }; +} + +#endif diff --git a/include/effects/Bars.h b/include/effects/Bars.h index 7c92255aa..a781a5205 100644 --- a/include/effects/Bars.h +++ b/include/effects/Bars.h @@ -77,16 +77,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) Bars(Color color, Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Blur.h b/include/effects/Blur.h index 60a0cd085..a3211e76e 100644 --- a/include/effects/Blur.h +++ b/include/effects/Blur.h @@ -89,16 +89,24 @@ namespace openshot /// @param new_iterations The curve to adjust the # of iterations (between 1 and 100) Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h index 5f25b94a0..5e36671f0 100644 --- a/include/effects/Brightness.h +++ b/include/effects/Brightness.h @@ -77,16 +77,24 @@ namespace openshot /// @param new_contrast The curve to adjust the contrast (3 is typical, 20 is a lot, 100 is max. 0 is invalid) Brightness(Keyframe new_brightness, Keyframe new_contrast); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/ChromaKey.h b/include/effects/ChromaKey.h index fcc8c3b1f..a59e582dc 100644 --- a/include/effects/ChromaKey.h +++ b/include/effects/ChromaKey.h @@ -74,16 +74,24 @@ namespace openshot /// @param fuzz The fuzz factor (or threshold) ChromaKey(Color color, Keyframe fuzz); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/ColorShift.h b/include/effects/ColorShift.h index 4ef56dc68..b6c596a9d 100644 --- a/include/effects/ColorShift.h +++ b/include/effects/ColorShift.h @@ -81,16 +81,24 @@ namespace openshot /// @param alpha_y The curve to adjust the Alpha y shift (between -1 and 1, percentage) ColorShift(Keyframe red_x, Keyframe red_y, Keyframe green_x, Keyframe green_y, Keyframe blue_x, Keyframe blue_y, Keyframe alpha_x, Keyframe alpha_y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Crop.h b/include/effects/Crop.h index f5ba07f24..ccdba3f1f 100644 --- a/include/effects/Crop.h +++ b/include/effects/Crop.h @@ -76,16 +76,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) Crop(Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Deinterlace.h b/include/effects/Deinterlace.h index 97c778538..83a9f2b2a 100644 --- a/include/effects/Deinterlace.h +++ b/include/effects/Deinterlace.h @@ -70,16 +70,24 @@ namespace openshot /// Default constructor Deinterlace(bool isOdd); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Hue.h b/include/effects/Hue.h index 833bf087b..e561bbf77 100644 --- a/include/effects/Hue.h +++ b/include/effects/Hue.h @@ -67,16 +67,24 @@ namespace openshot /// @param hue The curve to adjust the hue shift (between 0 and 1) Hue(Keyframe hue); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Mask.h b/include/effects/Mask.h index 8156b8436..910f1308d 100644 --- a/include/effects/Mask.h +++ b/include/effects/Mask.h @@ -89,16 +89,24 @@ namespace openshot /// @param mask_contrast The curve to adjust the contrast of the wipe's mask (3 is typical, 20 is a lot, 0 is invalid) Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Negate.h b/include/effects/Negate.h index c691a86c1..cab98f0a8 100644 --- a/include/effects/Negate.h +++ b/include/effects/Negate.h @@ -58,16 +58,24 @@ namespace openshot /// Default constructor Negate(); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Pixelate.h b/include/effects/Pixelate.h index 793f4d46f..8090847f0 100644 --- a/include/effects/Pixelate.h +++ b/include/effects/Pixelate.h @@ -75,16 +75,24 @@ namespace openshot /// @param bottom The curve to adjust the bottom margin size (between 0 and 1) Pixelate(Keyframe pixelization, Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index cc6976315..b07a17001 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -80,16 +80,24 @@ namespace openshot /// @param saturation_B The curve to adjust blue saturation of the frame's image (0.0 = greyscale, 1.0 = normal, 2.0 = double saturation) Saturation(Keyframe saturation, Keyframe saturation_R, Keyframe saturation_G, Keyframe saturation_B); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Shift.h b/include/effects/Shift.h index 7a7efbea4..b2c3242d5 100644 --- a/include/effects/Shift.h +++ b/include/effects/Shift.h @@ -70,16 +70,24 @@ namespace openshot /// @param y The curve to adjust the y shift (between -1 and 1, percentage) Shift(Keyframe x, Keyframe y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/include/effects/Wave.h b/include/effects/Wave.h index d4759c24e..3b922eb3d 100644 --- a/include/effects/Wave.h +++ b/include/effects/Wave.h @@ -76,16 +76,24 @@ namespace openshot /// @param speed_y The curve to adjust the vertical speed (0 to 10) Wave(Keyframe wavelength, Keyframe amplitude, Keyframe multiplier, Keyframe shift_x, Keyframe speed_y); - /// @brief This method is required for all derived classes of EffectBase, and returns a + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a /// modified openshot::Frame object /// - /// The frame object is passed into this method, and a frame_number is passed in which - /// tells the effect which settings to use from its keyframes (starting at 1). + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. /// /// @returns The modified openshot::Frame object - /// @param frame The frame object that needs the effect applied to it - /// @param frame_number The frame number (starting at 1) of the effect on the timeline. - std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04121c225..8cc2b6f28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -178,6 +178,7 @@ set(OPENSHOT_SOURCES QtPlayer.cpp QtTextReader.cpp Settings.cpp + TimelineBase.cpp Timeline.cpp) # Video effects diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp index c194ce33a..fb45e8f6d 100644 --- a/src/ChunkReader.cpp +++ b/src/ChunkReader.cpp @@ -222,7 +222,6 @@ std::shared_ptr ChunkReader::GetFrame(int64_t requested_frame) // Close existing reader (if needed) if (local_reader) { - std::cout << "Close READER" << std::endl; // Close and delete old reader local_reader->Close(); delete local_reader; @@ -230,7 +229,6 @@ std::shared_ptr ChunkReader::GetFrame(int64_t requested_frame) try { - std::cout << "Load READER: " << chunk_video_path << std::endl; // Load new FFmpegReader local_reader = new FFmpegReader(chunk_video_path); local_reader->Open(); // open reader diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp index c7752cd57..8a8e559c7 100644 --- a/src/ChunkWriter.cpp +++ b/src/ChunkWriter.cpp @@ -159,10 +159,6 @@ void ChunkWriter::WriteFrame(std::shared_ptr frame) // Write the frames once it reaches the correct chunk size if (frame_count % chunk_size == 0 && frame_count >= chunk_size) { - std::cout << "Done with chunk" << std::endl; - std::cout << "frame_count: " << frame_count << std::endl; - std::cout << "chunk_size: " << chunk_size << std::endl; - // Pad an additional 12 frames for (int z = 0; z<12; z++) { @@ -231,10 +227,6 @@ void ChunkWriter::Close() // Write the frames once it reaches the correct chunk size if (is_writing) { - std::cout << "Final chunk" << std::endl; - std::cout << "frame_count: " << frame_count << std::endl; - std::cout << "chunk_size: " << chunk_size << std::endl; - // Pad an additional 12 frames for (int z = 0; z<12; z++) { diff --git a/src/Clip.cpp b/src/Clip.cpp index 0a38c6b4b..fbea9e705 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -79,13 +79,6 @@ void Clip::init_settings() // Init audio waveform color wave_color = Color((unsigned char)0, (unsigned char)123, (unsigned char)255, (unsigned char)255); - // Init crop settings - crop_gravity = GRAVITY_TOP_LEFT; - crop_width = Keyframe(1.0); - crop_height = Keyframe(1.0); - crop_x = Keyframe(0.0); - crop_y = Keyframe(0.0); - // Init shear and perspective curves shear_x = Keyframe(0.0); shear_y = Keyframe(0.0); @@ -109,7 +102,7 @@ void Clip::init_settings() has_video = Keyframe(-1.0); // Initialize Clip cache - final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); } // Init reader's rotation (if any) @@ -177,7 +170,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Open common video format - reader = new FFmpegReader(path); + reader = new openshot::FFmpegReader(path); } catch(...) { } } @@ -186,7 +179,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Open common video format - reader = new Timeline(path, true); + reader = new openshot::Timeline(path, true); } catch(...) { } } @@ -198,13 +191,13 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N try { // Try an image reader - reader = new QtImageReader(path); + reader = new openshot::QtImageReader(path); } catch(...) { try { // Try a video reader - reader = new FFmpegReader(path); + reader = new openshot::FFmpegReader(path); } catch(...) { } } @@ -316,8 +309,8 @@ float Clip::End() const return end; } -// Get an openshot::Frame object for a specific frame number of this reader. -std::shared_ptr Clip::GetFrame(int64_t requested_frame) +// Create an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr Clip::GetFrame(int64_t frame_number) { // Check for open reader (or throw exception) if (!is_open) @@ -326,25 +319,19 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) if (reader) { // Adjust out of bounds frame number - requested_frame = adjust_frame_number_minimum(requested_frame); - - // Is a time map detected - int64_t new_frame_number = requested_frame; - int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); - if (time.GetLength() > 1) - new_frame_number = time_mapped_number; + frame_number = adjust_frame_number_minimum(frame_number); - // Get the # of audio samples from the time mapped Frame instance - std::shared_ptr time_mapped_original_frame = GetOrCreateFrame(new_frame_number); - return GetFrame(requested_frame, reader->info.width, reader->info.height, time_mapped_original_frame->GetAudioSamplesCount()); + // Get the original frame and pass it to GetFrame overload + std::shared_ptr original_frame = GetOrCreateFrame(frame_number); + return GetFrame(original_frame, frame_number); } else // Throw error if reader not initialized throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method."); } -// Get an openshot::Frame object for a specific frame number of this reader. -std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int height, int samples) +// Use an existing openshot::Frame object and draw this Clip's frame onto it +std::shared_ptr Clip::GetFrame(std::shared_ptr frame, int64_t frame_number) { // Check for open reader (or throw exception) if (!is_open) @@ -353,33 +340,33 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he if (reader) { // Adjust out of bounds frame number - requested_frame = adjust_frame_number_minimum(requested_frame); + frame_number = adjust_frame_number_minimum(frame_number); // Check the cache for this frame - std::shared_ptr cached_frame = final_cache.GetFrame(requested_frame); + std::shared_ptr cached_frame = cache.GetFrame(frame_number); if (cached_frame) { // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", requested_frame); + ZmqLogger::Instance()->AppendDebugMethod("Clip::GetFrame", "returned cached frame", frame_number); // Return the cached frame return cached_frame; } // Adjust has_video and has_audio overrides - int enabled_audio = has_audio.GetInt(requested_frame); + int enabled_audio = has_audio.GetInt(frame_number); if (enabled_audio == -1 && reader && reader->info.has_audio) enabled_audio = 1; else if (enabled_audio == -1 && reader && !reader->info.has_audio) enabled_audio = 0; - int enabled_video = has_video.GetInt(requested_frame); + int enabled_video = has_video.GetInt(frame_number); if (enabled_video == -1 && reader && reader->info.has_video) enabled_video = 1; else if (enabled_video == -1 && reader && !reader->info.has_audio) enabled_video = 0; // Is a time map detected - int64_t new_frame_number = requested_frame; - int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(requested_frame)); + int64_t new_frame_number = frame_number; + int64_t time_mapped_number = adjust_frame_number_minimum(time.GetLong(frame_number)); if (time.GetLength() > 1) new_frame_number = time_mapped_number; @@ -387,16 +374,6 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he std::shared_ptr original_frame; original_frame = GetOrCreateFrame(new_frame_number); - // Create a new frame - auto frame = std::make_shared( - new_frame_number, 1, 1, "#000000", - original_frame->GetAudioSamplesCount(), - original_frame->GetAudioChannelsCount()); - { - frame->SampleRate(original_frame->SampleRate()); - frame->ChannelsLayout(original_frame->ChannelsLayout()); - } - // Copy the image from the odd field if (enabled_video) frame->AddImage(std::make_shared(*original_frame->GetImage())); @@ -416,11 +393,24 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame, int width, int he // Apply effects to the frame (if any) apply_effects(frame); + // Determine size of image (from Timeline or Reader) + int width = 0; + int height = 0; + if (timeline) { + // Use timeline size (if available) + width = timeline->preview_width; + height = timeline->preview_height; + } else { + // Fallback to clip size + width = reader->info.width; + height = reader->info.height; + } + // Apply keyframe / transforms apply_keyframes(frame, width, height); // Cache frame - final_cache.Add(frame); + cache.Add(frame); // Return processed 'frame' return frame; @@ -692,17 +682,26 @@ int64_t Clip::adjust_frame_number_minimum(int64_t frame_number) // Get or generate a blank frame std::shared_ptr Clip::GetOrCreateFrame(int64_t number) { - std::shared_ptr new_frame; try { // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number); // Attempt to get a frame (but this could fail if a reader has just been closed) - new_frame = reader->GetFrame(number); + std::shared_ptr reader_frame = reader->GetFrame(number); // Return real frame - if (new_frame) - return new_frame; + if (reader_frame) { + // Create a new copy of reader frame + // This allows a clip to modify the pixels and audio of this frame without + // changing the underlying reader's frame data + //std::shared_ptr reader_copy(new Frame(number, 1, 1, "#000000", reader_frame->GetAudioSamplesCount(), reader_frame->GetAudioChannelsCount())); + std::shared_ptr reader_copy(new Frame(*reader_frame.get())); + { + reader_copy->SampleRate(reader_frame->SampleRate()); + reader_copy->ChannelsLayout(reader_frame->ChannelsLayout()); + } + return reader_copy; + } } catch (const ReaderClosed & e) { // ... @@ -719,7 +718,7 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number) ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (create blank)", "number", number, "estimated_samples_in_frame", estimated_samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); + std::shared_ptr new_frame = std::make_shared(number, reader->info.width, reader->info.height, "#000000", estimated_samples_in_frame, reader->info.channels); new_frame->SampleRate(reader->info.sample_rate); new_frame->ChannelsLayout(reader->info.channel_layout); new_frame->AddAudioSilence(estimated_samples_in_frame); @@ -808,11 +807,6 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) const { root["has_video"]["choices"].append(add_property_choice_json("Off", 0, has_video.GetValue(requested_frame))); root["has_video"]["choices"].append(add_property_choice_json("On", 1, has_video.GetValue(requested_frame))); - root["crop_x"] = add_property_json("Crop X", crop_x.GetValue(requested_frame), "float", "", &crop_x, -1.0, 1.0, false, requested_frame); - root["crop_y"] = add_property_json("Crop Y", crop_y.GetValue(requested_frame), "float", "", &crop_y, -1.0, 1.0, false, requested_frame); - root["crop_width"] = add_property_json("Crop Width", crop_width.GetValue(requested_frame), "float", "", &crop_width, 0.0, 1.0, false, requested_frame); - root["crop_height"] = add_property_json("Crop Height", crop_height.GetValue(requested_frame), "float", "", &crop_height, 0.0, 1.0, false, requested_frame); - root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", &wave_color.red, 0, 255, false, requested_frame); root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", &wave_color.red, 0, 255, false, requested_frame); root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", &wave_color.blue, 0, 255, false, requested_frame); @@ -843,10 +837,6 @@ Json::Value Clip::JsonValue() const { root["time"] = time.JsonValue(); root["volume"] = volume.JsonValue(); root["wave_color"] = wave_color.JsonValue(); - root["crop_width"] = crop_width.JsonValue(); - root["crop_height"] = crop_height.JsonValue(); - root["crop_x"] = crop_x.JsonValue(); - root["crop_y"] = crop_y.JsonValue(); root["shear_x"] = shear_x.JsonValue(); root["shear_y"] = shear_y.JsonValue(); root["origin_x"] = origin_x.JsonValue(); @@ -906,7 +896,7 @@ void Clip::SetJsonValue(const Json::Value root) { ClipBase::SetJsonValue(root); // Clear cache - final_cache.Clear(); + cache.Clear(); // Set data from Json (if key is found) if (!root["gravity"].isNull()) @@ -939,14 +929,6 @@ void Clip::SetJsonValue(const Json::Value root) { volume.SetJsonValue(root["volume"]); if (!root["wave_color"].isNull()) wave_color.SetJsonValue(root["wave_color"]); - if (!root["crop_width"].isNull()) - crop_width.SetJsonValue(root["crop_width"]); - if (!root["crop_height"].isNull()) - crop_height.SetJsonValue(root["crop_height"]); - if (!root["crop_x"].isNull()) - crop_x.SetJsonValue(root["crop_x"]); - if (!root["crop_y"].isNull()) - crop_y.SetJsonValue(root["crop_y"]); if (!root["shear_x"].isNull()) shear_x.SetJsonValue(root["shear_x"]); if (!root["shear_y"].isNull()) @@ -1025,13 +1007,13 @@ void Clip::SetJsonValue(const Json::Value root) { if (type == "FFmpegReader") { // Create new reader - reader = new FFmpegReader(root["reader"]["path"].asString(), false); + reader = new openshot::FFmpegReader(root["reader"]["path"].asString(), false); reader->SetJsonValue(root["reader"]); } else if (type == "QtImageReader") { // Create new reader - reader = new QtImageReader(root["reader"]["path"].asString(), false); + reader = new openshot::QtImageReader(root["reader"]["path"].asString(), false); reader->SetJsonValue(root["reader"]); #ifdef USE_IMAGEMAGICK @@ -1051,20 +1033,20 @@ void Clip::SetJsonValue(const Json::Value root) { } else if (type == "ChunkReader") { // Create new reader - reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt()); + reader = new openshot::ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt()); reader->SetJsonValue(root["reader"]); } else if (type == "DummyReader") { // Create new reader - reader = new DummyReader(); + reader = new openshot::DummyReader(); reader->SetJsonValue(root["reader"]); } else if (type == "Timeline") { // Create new reader (always load from file again) // This prevents FrameMappers from being loaded on accident - reader = new Timeline(root["reader"]["path"].asString(), true); + reader = new openshot::Timeline(root["reader"]["path"].asString(), true); } // mark as managed reader and set parent @@ -1091,6 +1073,9 @@ void Clip::sort_effects() // Add an effect to the clip void Clip::AddEffect(EffectBase* effect) { + // Set parent clip pointer + effect->ParentClip(this); + // Add effect to list effects.push_back(effect); @@ -1098,7 +1083,7 @@ void Clip::AddEffect(EffectBase* effect) sort_effects(); // Clear cache - final_cache.Clear(); + cache.Clear(); } // Remove an effect from the clip @@ -1107,7 +1092,7 @@ void Clip::RemoveEffect(EffectBase* effect) effects.remove(effect); // Clear cache - final_cache.Clear(); + cache.Clear(); } // Apply effects to the source frame (if any) @@ -1180,7 +1165,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width, height, Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_FIT)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_STRETCH): { @@ -1188,7 +1173,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width, height, Qt::IgnoreAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_STRETCH)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_CROP): { @@ -1202,7 +1187,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(height_size.width(), height_size.height(), Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_CROP)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } case (SCALE_NONE): { @@ -1214,50 +1199,11 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) source_size.scale(width * source_width_ratio, height * source_height_ratio, Qt::KeepAspectRatio); // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Scale: SCALE_NONE)", "frame->number", frame->number, "source_width", source_size.width(), "source_height", source_size.height()); break; } } - float crop_x_value = crop_x.GetValue(frame->number); - float crop_y_value = crop_y.GetValue(frame->number); - float crop_w_value = crop_width.GetValue(frame->number); - float crop_h_value = crop_height.GetValue(frame->number); - switch(crop_gravity) - { - case (GRAVITY_TOP_LEFT): - // This is only here to prevent unused-enum warnings - break; - case (GRAVITY_TOP): - crop_x_value += 0.5; - break; - case (GRAVITY_TOP_RIGHT): - crop_x_value += 1.0; - break; - case (GRAVITY_LEFT): - crop_y_value += 0.5; - break; - case (GRAVITY_CENTER): - crop_x_value += 0.5; - crop_y_value += 0.5; - break; - case (GRAVITY_RIGHT): - crop_x_value += 1.0; - crop_y_value += 0.5; - break; - case (GRAVITY_BOTTOM_LEFT): - crop_y_value += 1.0; - break; - case (GRAVITY_BOTTOM): - crop_x_value += 0.5; - crop_y_value += 1.0; - break; - case (GRAVITY_BOTTOM_RIGHT): - crop_x_value += 1.0; - crop_y_value += 1.0; - break; - } - /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ float x = 0.0; // left float y = 0.0; // top @@ -1304,7 +1250,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) } // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height); /* LOCATION, ROTATION, AND SCALE */ float r = rotation.GetValue(frame->number); // rotate in degrees @@ -1319,7 +1265,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) QTransform transform; // Transform source image (if needed) - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy); if (!isEqual(x, 0) || !isEqual(y, 0)) { // TRANSLATE/MOVE CLIP @@ -1348,7 +1294,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) } // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Clip::add_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Transform: Composite Image Layer: Prepare)", "frame->number", frame->number, "transformed", transformed); /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ std::shared_ptr new_image; @@ -1367,31 +1313,34 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.drawImage(0, 0, *source_image); - // Draw frame #'s on top of image (if needed) - if (display != FRAME_DISPLAY_NONE) { - std::stringstream frame_number_str; - switch (display) - { - case (FRAME_DISPLAY_NONE): - // This is only here to prevent unused-enum warnings - break; + if (timeline) { + Timeline *t = (Timeline *) timeline; - case (FRAME_DISPLAY_CLIP): - frame_number_str << frame->number; - break; + // Draw frame #'s on top of image (if needed) + if (display != FRAME_DISPLAY_NONE) { + std::stringstream frame_number_str; + switch (display) { + case (FRAME_DISPLAY_NONE): + // This is only here to prevent unused-enum warnings + break; - case (FRAME_DISPLAY_TIMELINE): - frame_number_str << "N/A"; - break; + case (FRAME_DISPLAY_CLIP): + frame_number_str << frame->number; + break; - case (FRAME_DISPLAY_BOTH): - frame_number_str << "N/A" << " (" << frame->number << ")"; - break; - } + case (FRAME_DISPLAY_TIMELINE): + frame_number_str << (position * t->info.fps.ToFloat()) + frame->number; + break; - // Draw frame number on top of image - painter.setPen(QColor("#ffffff")); - painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + case (FRAME_DISPLAY_BOTH): + frame_number_str << (position * t->info.fps.ToFloat()) + frame->number << " (" << frame->number << ")"; + break; + } + + // Draw frame number on top of image + painter.setPen(QColor("#ffffff")); + painter.drawText(20, 20, QString(frame_number_str.str().c_str())); + } } painter.end(); diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index 05ed97c2a..fcf006451 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -138,3 +138,13 @@ Json::Value EffectBase::JsonInfo() const { // return JsonValue return root; } + +/// Parent clip object of this reader (which can be unparented and NULL) +openshot::ClipBase* EffectBase::ParentClip() { + return clip; +} + +/// Set parent clip object of this reader +void EffectBase::ParentClip(openshot::ClipBase* new_clip) { + clip = new_clip; +} \ No newline at end of file diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 5b7b7ddd4..dcdad2c0d 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1268,15 +1268,16 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { // without losing quality. NOTE: We cannot go smaller than the timeline itself, or the add_layer timeline // method will scale it back to timeline size before scaling it smaller again. This needs to be fixed in // the future. - int max_width = openshot::Settings::Instance()->MAX_WIDTH; - if (max_width <= 0) - max_width = info.width; - int max_height = openshot::Settings::Instance()->MAX_HEIGHT; - if (max_height <= 0) - max_height = info.height; + int max_width = info.width; + int max_height = info.height; Clip *parent = (Clip *) GetParentClip(); if (parent) { + if (parent->ParentTimeline()) { + // Set max width/height based on parent clip's timeline (if attached to a timeline) + max_width = parent->ParentTimeline()->preview_width; + max_height = parent->ParentTimeline()->preview_height; + } if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) { // Best fit or Stretch scaling (based on max timeline size * scaling keyframes) float max_scale_x = parent->scale_x.GetMaxPoint().co.Y; diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp index e5f0e11d0..54813b377 100644 --- a/src/Qt/PlayerDemo.cpp +++ b/src/Qt/PlayerDemo.cpp @@ -105,7 +105,6 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) } else if (event->key() == Qt::Key_J) { - std::cout << "BACKWARD" << player->Speed() - 1 << std::endl; if (player->Speed() - 1 != 0) player->Speed(player->Speed() - 1); else @@ -115,7 +114,6 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) player->Play(); } else if (event->key() == Qt::Key_L) { - std::cout << "FORWARD" << player->Speed() + 1 << std::endl; if (player->Speed() + 1 != 0) player->Speed(player->Speed() + 1); else @@ -126,19 +124,16 @@ void PlayerDemo::keyPressEvent(QKeyEvent *event) } else if (event->key() == Qt::Key_Left) { - std::cout << "FRAME STEP -1" << std::endl; if (player->Speed() != 0) player->Speed(0); player->Seek(player->Position() - 1); } else if (event->key() == Qt::Key_Right) { - std::cout << "FRAME STEP +1" << std::endl; if (player->Speed() != 0) player->Speed(0); player->Seek(player->Position() + 1); } else if (event->key() == Qt::Key_Escape) { - std::cout << "QUIT PLAYER" << std::endl; QWidget *pWin = QApplication::activeWindow(); pWin->hide(); diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 78c2b009a..86040bd4d 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -175,15 +175,16 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) // without losing quality. NOTE: We cannot go smaller than the timeline itself, or the add_layer timeline // method will scale it back to timeline size before scaling it smaller again. This needs to be fixed in // the future. - int max_width = Settings::Instance()->MAX_WIDTH; - if (max_width <= 0) - max_width = info.width; - int max_height = Settings::Instance()->MAX_HEIGHT; - if (max_height <= 0) - max_height = info.height; + int max_width = info.width; + int max_height = info.height; Clip* parent = (Clip*) GetParentClip(); if (parent) { + if (parent->ParentTimeline()) { + // Set max width/height based on parent clip's timeline (if attached to a timeline) + max_width = parent->ParentTimeline()->preview_width; + max_height = parent->ParentTimeline()->preview_height; + } if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) { // Best fit or Stretch scaling (based on max timeline size * scaling keyframes) float max_scale_x = parent->scale_x.GetMaxPoint().co.Y; diff --git a/src/ReaderBase.cpp b/src/ReaderBase.cpp index 6799d95ec..653756c51 100644 --- a/src/ReaderBase.cpp +++ b/src/ReaderBase.cpp @@ -63,7 +63,7 @@ ReaderBase::ReaderBase() info.audio_timebase = Fraction(); // Init parent clip - parent = NULL; + clip = NULL; } // Display file information @@ -249,3 +249,13 @@ void ReaderBase::SetJsonValue(const Json::Value root) { } } } + +/// Parent clip object of this reader (which can be unparented and NULL) +openshot::ClipBase* ReaderBase::ParentClip() { + return clip; +} + +/// Set parent clip object of this reader +void ReaderBase::ParentClip(openshot::ClipBase* new_clip) { + clip = new_clip; +} diff --git a/src/Settings.cpp b/src/Settings.cpp index e48fd981c..d946d2277 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -45,8 +45,6 @@ Settings *Settings::Instance() m_pInstance = new Settings; m_pInstance->HARDWARE_DECODER = 0; m_pInstance->HIGH_QUALITY_SCALING = false; - m_pInstance->MAX_WIDTH = 0; - m_pInstance->MAX_HEIGHT = 0; m_pInstance->WAIT_FOR_VIDEO_PROCESSING_TASK = false; m_pInstance->OMP_THREADS = 12; m_pInstance->FF_THREADS = 8; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index ac36be063..afa85f99f 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -52,6 +52,8 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha // Init FileInfo struct (clear all values) info.width = width; info.height = height; + preview_width = info.width; + preview_height = info.height; info.fps = fps; info.sample_rate = sample_rate; info.channels = channels; @@ -241,6 +243,9 @@ Timeline::~Timeline() { // Add an openshot::Clip to the timeline void Timeline::AddClip(Clip* clip) { + // Assign timeline to clip + clip->ParentTimeline(this); + // All clips should be converted to the frame rate of this timeline if (auto_map_clips) // Apply framemapper (or update existing framemapper) @@ -256,6 +261,9 @@ void Timeline::AddClip(Clip* clip) // Add an effect to the timeline void Timeline::AddEffect(EffectBase* effect) { + // Assign timeline to effect + effect->ParentTimeline(this); + // Add effect to list effects.push_back(effect); @@ -441,7 +449,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) // Attempt to get a frame (but this could fail if a reader has just been closed) #pragma omp critical (T_GetOtCreateFrame) - new_frame = std::shared_ptr(clip->GetFrame(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame)); + new_frame = std::shared_ptr(clip->GetFrame(number)); // Return real frame return new_frame; @@ -458,7 +466,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(Clip* clip, int64_t number) ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetOrCreateFrame (create blank)", "number", number, "samples_in_frame", samples_in_frame); // Create blank frame - new_frame = std::make_shared(number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, "#000000", samples_in_frame, info.channels); + new_frame = std::make_shared(number, preview_width, preview_height, "#000000", samples_in_frame, info.channels); #pragma omp critical (T_GetOtCreateFrame) { new_frame->SampleRate(info.sample_rate); @@ -560,29 +568,8 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Skip the rest of the image processing for performance reasons return; - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Get Source Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); - - // Get actual frame image data - source_image = source_frame->GetImage(); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); - - /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ - std::shared_ptr new_image; - new_image = new_frame->GetImage(); - - // Load timeline's new frame image into a QPainter - QPainter painter(new_image.get()); - - // Composite a new layer onto the image - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); - painter.end(); - // Add new QImage to frame - new_frame->AddImage(new_image); + new_frame->AddImage(source_frame->GetImage()); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); @@ -737,10 +724,9 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Get clip frame # long clip_start_frame = (clip->Start() * info.fps.ToDouble()) + 1; long clip_frame_number = frame_number - clip_start_position + clip_start_frame; - int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Cache clip object - clip->GetFrame(clip_frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, samples_in_frame); + clip->GetFrame(clip_frame_number); } } } @@ -758,7 +744,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels); // Create blank frame (which will become the requested frame) - std::shared_ptr new_frame(std::make_shared(frame_number, Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, "#000000", samples_in_frame, info.channels)); + std::shared_ptr new_frame(std::make_shared(frame_number, preview_width, preview_height, "#000000", samples_in_frame, info.channels)); #pragma omp critical (T_GetFrame) { new_frame->AddAudioSilence(samples_in_frame); @@ -772,7 +758,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Add Background Color to 1st layer (if animated or not black) if ((color.red.GetCount() > 1 || color.green.GetCount() > 1 || color.blue.GetCount() > 1) || (color.red.GetValue(frame_number) != 0.0 || color.green.GetValue(frame_number) != 0.0 || color.blue.GetValue(frame_number) != 0.0)) - new_frame->AddColor(Settings::Instance()->MAX_WIDTH, Settings::Instance()->MAX_HEIGHT, color.GetColorHex(frame_number)); + new_frame->AddColor(preview_width, preview_height, color.GetColorHex(frame_number)); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size()); @@ -1036,6 +1022,10 @@ void Timeline::SetJsonValue(const Json::Value root) { info.video_length = info.fps.ToFloat() * info.duration; } + // Update preview settings + preview_width = info.width; + preview_height = info.height; + // Re-open if needed if (was_open) Open(); @@ -1264,6 +1254,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef // Add Effect to Timeline AddEffect(e); + + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) e->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } } } else if (change_type == "update") { @@ -1276,6 +1272,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef int64_t old_ending_frame = ((existing_effect->Position() + existing_effect->Duration()) * info.fps.ToDouble()) + 1; final_cache->Remove(old_starting_frame - 8, old_ending_frame + 8); + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) existing_effect->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } + // Update effect properties from JSON existing_effect->SetJsonValue(change["value"]); } @@ -1290,6 +1292,12 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef int64_t old_ending_frame = ((existing_effect->Position() + existing_effect->Duration()) * info.fps.ToDouble()) + 1; final_cache->Remove(old_starting_frame - 8, old_ending_frame + 8); + // Clear cache on parent clip (if any) + Clip* parent_clip = (Clip*) existing_effect->ParentClip(); + if (parent_clip && parent_clip->GetCache()) { + parent_clip->GetCache()->Clear(); + } + // Remove effect from timeline RemoveEffect(existing_effect); } @@ -1308,7 +1316,7 @@ void Timeline::apply_json_to_timeline(Json::Value change) { sub_key = change["key"][(uint)1].asString(); // Clear entire cache - final_cache->Clear(); + ClearAllCache(); // Determine type of change operation if (change_type == "insert" || change_type == "update") { @@ -1332,12 +1340,16 @@ void Timeline::apply_json_to_timeline(Json::Value change) { info.duration = change["value"].asDouble(); info.video_length = info.fps.ToFloat() * info.duration; } - else if (root_key == "width") + else if (root_key == "width") { // Set width info.width = change["value"].asInt(); - else if (root_key == "height") + preview_width = info.width; + } + else if (root_key == "height") { // Set height info.height = change["value"].asInt(); + preview_height = info.height; + } else if (root_key == "fps" && sub_key == "" && change["value"].isObject()) { // Set fps fraction if (!change["value"]["num"].isNull()) @@ -1429,6 +1441,7 @@ void Timeline::ClearAllCache() { for (auto clip : clips) { // Clear cache on clip + clip->GetCache()->Clear(); clip->Reader()->GetCache()->Clear(); // Clear nested Reader (if any) @@ -1451,7 +1464,7 @@ void Timeline::SetMaxSize(int width, int height) { // Scale QSize up to proposed size display_ratio_size.scale(proposed_size, Qt::KeepAspectRatio); - // Set max size - Settings::Instance()->MAX_WIDTH = display_ratio_size.width(); - Settings::Instance()->MAX_HEIGHT = display_ratio_size.height(); + // Update preview settings + preview_width = display_ratio_size.width(); + preview_height = display_ratio_size.height(); } diff --git a/src/TimelineBase.cpp b/src/TimelineBase.cpp new file mode 100644 index 000000000..f75e1ddb9 --- /dev/null +++ b/src/TimelineBase.cpp @@ -0,0 +1,33 @@ +/** + * @file + * @brief Source file for Timeline class + * @author Jonathan Thomas + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/TimelineBase.h" + +using namespace openshot; diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index 53e514c15..b5be39c4c 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -92,6 +92,7 @@ #include "KeyFrame.h" #include "RendererBase.h" #include "Settings.h" +#include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" #include "AudioDeviceInfo.h" @@ -203,6 +204,7 @@ %include "KeyFrame.h" %include "RendererBase.h" %include "Settings.h" +%include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" %include "AudioDeviceInfo.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 2f24d2200..d36990dd5 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -103,6 +103,7 @@ namespace std { #include "KeyFrame.h" #include "RendererBase.h" #include "Settings.h" +#include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" #include "AudioDeviceInfo.h" @@ -192,6 +193,7 @@ namespace std { %include "KeyFrame.h" %include "RendererBase.h" %include "Settings.h" +%include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" %include "AudioDeviceInfo.h" From b3ad76ddf73b63c1d59932d4c2fab7d722d27d85 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 5 Oct 2020 23:08:31 -0500 Subject: [PATCH 76/86] Fixing some regressions on image merging --- include/ClipBase.h | 10 +++++++++- include/TimelineBase.h | 3 +++ src/Timeline.cpp | 23 ++++++++++++++++++++++- src/TimelineBase.cpp | 8 ++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/ClipBase.h b/include/ClipBase.h index e335c501b..11d2271fa 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -69,7 +69,15 @@ namespace openshot { CacheMemory cache; /// Constructor for the base clip - ClipBase() { }; + ClipBase() { + // Initialize values + position = 0.0; + layer = 0; + start = 0.0; + end = 0.0; + previous_properties = ""; + timeline = NULL; + }; // Compare a clip using the Position() property bool operator< ( ClipBase& a) { return (Position() < a.Position()); } diff --git a/include/TimelineBase.h b/include/TimelineBase.h index d065a6de1..af6a65a20 100644 --- a/include/TimelineBase.h +++ b/include/TimelineBase.h @@ -41,6 +41,9 @@ namespace openshot { public: int preview_width; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. int preview_height; ///< Optional preview width of timeline image. If your preview window is smaller than the timeline, it's recommended to set this. + + /// Constructor for the base timeline + TimelineBase(); }; } diff --git a/src/Timeline.cpp b/src/Timeline.cpp index afa85f99f..86fc55ff9 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -568,8 +568,29 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Skip the rest of the image processing for performance reasons return; + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Get Source Image)", "source_frame->number", source_frame->number, "source_clip->Waveform()", source_clip->Waveform(), "clip_frame_number", clip_frame_number); + + // Get actual frame image data + source_image = source_frame->GetImage(); + + // Debug output + ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Prepare)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width(), "source_image->width()", source_image->width()); + + /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ + std::shared_ptr new_image; + new_image = new_frame->GetImage(); + + // Load timeline's new frame image into a QPainter + QPainter painter(new_image.get()); + + // Composite a new layer onto the image + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(0, 0, *source_image, 0, 0, source_image->width(), source_image->height()); + painter.end(); + // Add new QImage to frame - new_frame->AddImage(source_frame->GetImage()); + new_frame->AddImage(new_image); // Debug output ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Transform: Composite Image Layer: Completed)", "source_frame->number", source_frame->number, "new_frame->GetImage()->width()", new_frame->GetImage()->width()); diff --git a/src/TimelineBase.cpp b/src/TimelineBase.cpp index f75e1ddb9..cc59a2433 100644 --- a/src/TimelineBase.cpp +++ b/src/TimelineBase.cpp @@ -31,3 +31,11 @@ #include "../include/TimelineBase.h" using namespace openshot; + +/// Constructor for the base timeline +TimelineBase::TimelineBase() +{ + // Init preview size (default) + preview_width = 1920; + preview_height = 1080; +} From 8d36b1275411d350a5416fc910d32b6018f6a283 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Thu, 8 Oct 2020 14:44:01 -0500 Subject: [PATCH 77/86] Initialize parent clip variable --- src/EffectBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index fcf006451..fb7b22697 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -41,6 +41,7 @@ void EffectBase::InitEffectInfo() Start(0.0); End(0.0); Order(0); + ParentClip(NULL); info.has_video = false; info.has_audio = false; From b936ea837c2ccacf427486ebf4a5c5199dcdf0d2 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 14:55:25 -0500 Subject: [PATCH 78/86] Initializing Clip info struct, and fixing clip cache settings --- include/Clip.h | 15 +++++++++------ src/Clip.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index 24d4c599a..ef3b712f2 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -98,6 +98,15 @@ namespace openshot { /// Section lock for multiple threads juce::CriticalSection getFrameCriticalSection; + /// Init default settings for a clip + void init_settings(); + + /// Init reader info details + void init_reader_settings(); + + /// Update default rotation from reader + void init_reader_rotation(); + private: bool waveform; ///< Should a waveform be used instead of the clip's image std::list effects; /// frame, int64_t frame_number); - /// Init default settings for a clip - void init_settings(); - - /// Update default rotation from reader - void init_reader_rotation(); - /// Compare 2 floating point numbers bool isEqual(double a, double b); diff --git a/src/Clip.cpp b/src/Clip.cpp index fbea9e705..044543230 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -69,9 +69,6 @@ void Clip::init_settings() // Init alpha alpha = Keyframe(1.0); - // Init rotation - init_reader_rotation(); - // Init time & volume time = Keyframe(1.0); volume = Keyframe(1.0); @@ -101,8 +98,22 @@ void Clip::init_settings() has_audio = Keyframe(-1.0); has_video = Keyframe(-1.0); - // Initialize Clip cache - cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + // Init reader info struct and cache size + init_reader_settings(); +} + +// Init reader info details +void Clip::init_reader_settings() { + if (reader) { + // Init rotation (if any) + init_reader_rotation(); + + // Initialize info struct + info = reader->info; + + // Initialize Clip cache + cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + } } // Init reader's rotation (if any) @@ -208,8 +219,8 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N End(reader->info.duration); reader->SetParentClip(this); allocated_reader = reader; - init_reader_rotation(); - } + // Init reader info struct and cache size + init_reader_settings(); } } // Destructor @@ -237,8 +248,8 @@ void Clip::Reader(ReaderBase* new_reader) // set parent reader->SetParentClip(this); - // Init rotation (if any) - init_reader_rotation(); + // Init reader info struct and cache size + init_reader_settings(); } /// Get the current reader From a4109419ac22161b553cb7c6ce4bf6596123efde Mon Sep 17 00:00:00 2001 From: Brenno Date: Sat, 10 Oct 2020 17:01:24 -0300 Subject: [PATCH 79/86] Implemented position remapper inside FrameMapper to fix audio noise when exporting to different fps The FrameMapper class now receives the updated clip position and returns the correct amount of samples for a given frame number --- include/FrameMapper.h | 8 +- src/FrameMapper.cpp | 39 +++++-- src/Timeline.cpp | 10 +- tests/FrameMapper_Tests.cpp | 197 ++++++++++++++++++++++++++++++++++-- 4 files changed, 229 insertions(+), 25 deletions(-) diff --git a/include/FrameMapper.h b/include/FrameMapper.h index e78401a9b..85c933d25 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -147,6 +147,9 @@ namespace openshot bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping SWRCONTEXT *avr; // Audio resampling context object + float position; + float start; + // Internal methods used by init void AddField(int64_t frame); void AddField(Field field); @@ -166,13 +169,13 @@ namespace openshot std::vector frames; // List of all frames /// Default constructor for openshot::FrameMapper class - FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); + FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); /// Destructor virtual ~FrameMapper(); /// Change frame rate or audio mapping details - void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); + void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); /// Close the openshot::FrameMapper and internal reader void Close() override; @@ -218,6 +221,7 @@ namespace openshot /// Resample audio and map channels (if needed) void ResampleMappedAudio(std::shared_ptr frame, int64_t original_frame_number); + int64_t ConvPositon(int64_t clip_frame_number); }; } diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index 8eff6e702..977302915 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -33,7 +33,7 @@ using namespace std; using namespace openshot; -FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) : +FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) : reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL) { // Set the original frame rate from the reader @@ -52,6 +52,8 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe info.width = reader->info.width; info.height = reader->info.height; + position = clipPosition; + start = clipStart; // Used to toggle odd / even fields field_toggle = true; @@ -257,12 +259,12 @@ void FrameMapper::Init() // the original sample rate. int64_t end_samples_frame = start_samples_frame; int end_samples_position = start_samples_position; - int remaining_samples = Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels); + int remaining_samples = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels); while (remaining_samples > 0) { // get original samples - int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position; + int original_samples = Frame::GetSamplesPerFrame(ConvPositon(end_samples_frame), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; // Enough samples if (original_samples >= remaining_samples) @@ -282,12 +284,12 @@ void FrameMapper::Init() // Create the sample mapping struct - SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels)}; + SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels)}; // Reset the audio variables start_samples_frame = end_samples_frame; start_samples_position = end_samples_position + 1; - if (start_samples_position >= Frame::GetSamplesPerFrame(start_samples_frame, original, reader->info.sample_rate, reader->info.channels)) + if (start_samples_position >= Frame::GetSamplesPerFrame(ConvPositon(start_samples_frame), original, reader->info.sample_rate, reader->info.channels)) { start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one) start_samples_position = 0; // reset to 0, since we wrapped @@ -354,7 +356,7 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number) std::shared_ptr new_frame; // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now) - int samples_in_frame = Frame::GetSamplesPerFrame(number, target, reader->info.sample_rate, reader->info.channels); + int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(number), target, reader->info.sample_rate, reader->info.channels); try { // Debug output @@ -427,7 +429,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) // Get # of channels in the actual frame int channels_in_frame = mapped_frame->GetAudioChannelsCount(); - int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, target, mapped_frame->SampleRate(), channels_in_frame); + int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, mapped_frame->SampleRate(), channels_in_frame); // Determine if mapped frame is identical to source frame // including audio sample distribution according to mapped.Samples, @@ -689,6 +691,7 @@ Json::Value FrameMapper::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties root["type"] = "FrameMapper"; + root["position"] = position; // return JsonValue return root; @@ -717,6 +720,10 @@ void FrameMapper::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); + if(!root["position"].isNull()){ + position = root["position"].asDouble(); + } + // Re-Open path, and re-init everything (if needed) if (reader) { @@ -726,7 +733,7 @@ void FrameMapper::SetJsonValue(const Json::Value root) { } // Change frame rate or audio mapping details -void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) +void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) { ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ChangeMapping", "target_fps.num", target_fps.num, "target_fps.den", target_fps.den, "target_pulldown", target_pulldown, "target_sample_rate", target_sample_rate, "target_channels", target_channels, "target_channel_layout", target_channel_layout); @@ -745,6 +752,9 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow info.channels = target_channels; info.channel_layout = target_channel_layout; + position = clipPosition; + start = clipStart; + // Clear cache final_cache.Clear(); @@ -828,7 +838,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig } // Update total samples & input frame size (due to bigger or smaller data types) - total_frame_samples = Frame::GetSamplesPerFrame(frame->number, target, info.sample_rate, info.channels); + total_frame_samples = Frame::GetSamplesPerFrame(ConvPositon(frame->number), target, info.sample_rate, info.channels); ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ResampleMappedAudio (adjust # of samples)", "total_frame_samples", total_frame_samples, "info.sample_rate", info.sample_rate, "sample_rate_in_frame", sample_rate_in_frame, "info.channels", info.channels, "channels_in_frame", channels_in_frame, "original_frame_number", original_frame_number); @@ -937,3 +947,14 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig delete[] resampled_samples; resampled_samples = NULL; } + +int64_t FrameMapper::ConvPositon(int64_t clip_frame_number){ + + int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1; + int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1; + + int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame; + + ///std::cout << "Conv Position " << round(position * info.fps.ToDouble()) << " position: " << position << " info::fps: " << info.fps.ToDouble() << std::endl; + return frame_number; +} \ No newline at end of file diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 86fc55ff9..b635b4995 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -360,14 +360,14 @@ void Timeline::apply_mapper_to_clip(Clip* clip) } else { // Create a new FrameMapper to wrap the current reader - FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); + FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); allocated_frame_mappers.insert(mapper); clip_reader = (ReaderBase*) mapper; } // Update the mapping FrameMapper* clip_mapped_reader = (FrameMapper*) clip_reader; - clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); + clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); // Update clip reader clip->Reader(clip_reader); @@ -546,11 +546,12 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in // Currently, the ResampleContext sometimes leaves behind a few samples for the next call, and the // number of samples returned is variable... and does not match the number expected. // This is a crude solution at best. =) - if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()) + if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()){ // Force timeline frame to match the source frame #pragma omp critical (T_addLayer) - new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout); + new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout); + } // Copy audio samples (and set initial volume). Mix samples with existing audio samples. The gains are added together, to // be sure to set the gain's correctly, so the sum does not exceed 1.0 (of audio distortion will happen). #pragma omp critical (T_addLayer) @@ -682,6 +683,7 @@ bool Timeline::isEqual(double a, double b) // Get an openshot::Frame object for a specific frame number of this reader. std::shared_ptr Timeline::GetFrame(int64_t requested_frame) { + // Adjust out of bounds frame number if (requested_frame < 1) requested_frame = 1; diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 921f3a155..02634c373 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -42,7 +42,7 @@ TEST(FrameMapper_Get_Valid_Frame) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping between 24 fps and 29.97 fps using classic pulldown - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); try { @@ -63,7 +63,7 @@ TEST(FrameMapper_Invalid_Frame_Too_Small) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); // Check invalid frame number CHECK_THROW(mapping.GetMappedFrame(0), OutOfBoundsFrame); @@ -76,7 +76,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Classic) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); @@ -93,7 +93,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Advanced) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -113,7 +113,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_None) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -130,7 +130,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Classic) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -150,7 +150,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Advanced) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -170,7 +170,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_None) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -189,7 +189,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) FFmpegReader r(path.str()); // Map to 30 fps, 3 channels surround, 44100 sample rate - FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND); + FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND, 0.0, 0.0); map.Open(); // Check details @@ -199,7 +199,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) CHECK_EQUAL(1470, map.GetFrame(50)->GetAudioSamplesCount()); // Change mapping data - map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO); + map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO, 0.0, 0.0); // Check details CHECK_EQUAL(1, map.GetFrame(1)->GetAudioChannelsCount()); @@ -210,3 +210,180 @@ TEST(FrameMapper_resample_audio_48000_to_41000) // Close mapper map.Close(); } + +TEST(FrameMapper_AudioSample_Distribution) +{ + + CacheMemory cache; + int OFFSET = 0; + float AMPLITUDE = 0.2; + double ANGLE = 0.0; + int NUM_SAMPLES = 100; + std::cout << "Starting Resample Test" << std::endl; + + for (int64_t frame_number = 1; frame_number <= 90; 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)); + + // Create test samples with sin wave (predictable values) + float *audio_buffer = new float[sample_count * 2]; + + for (int sample_number = 0; sample_number < sample_count; sample_number++) + { + // Calculate sin wave + // TODO: I'm using abs(), because calling AddAudio only seems to be adding the positive values and it's bizarre + float sample_value = float(AMPLITUDE * sin(ANGLE) + OFFSET); + audio_buffer[sample_number] = sample_value;//abs(sample_value); + ANGLE += (2 * M_PI) / NUM_SAMPLES; + + // Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source, + f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1 + f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2 + + // Add test frame to dummy reader + 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.info.has_audio = true; + r.Open(); // Open the reader + + // Map to 24 fps, which should create a variable # of samples per frame + ///FrameMapper map(&r, Fraction(24, 1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO); + //map.info.has_audio = true; + //map.Open(); + + Timeline t1(1920, 1080, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); + + Clip c1; + c1.Reader(&r); + c1.Layer(1); + c1.Position(0.0); + c1.Start(0.0); + c1.End(10.0); + + // Create 2nd map to 24 fps, which should create a variable # of samples per frame + + //FrameMapper map2(&r, Fraction(24, 1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO); + + //map2.info.has_audio = true; + //map2.Open(); + + Clip c2; + c2.Reader(&r); + c2.Layer(2); + + // Position 1 frame into the video, this should mis-align the audio and create situations + // which overlapping Frame instances have different # of samples for the Timeline. + // TODO: Moving to 0.0 position, to simplify this test for now + + + c2.Position(0.041666667 * 14); + c2.Start(1.0); + c2.End(10.0); + + // Add clips + + t1.AddClip(&c1); + t1.AddClip(&c2); + + std::string json_val = t1.Json(); + + std::cout << json_val << std::endl; + + //t1.SetJson(t1.Json()); + t1.Open(); + + FFmpegWriter w("output-resample.mp4"); + + // Set options + w.SetAudioOptions("aac", 44100, 192000); + w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); + + // Open writer + w.Open(); + + + w.WriteFrame(&t1, 5, 50); + + //for (int64_t frame_number = 1; frame_number <= 90; frame_number++){ + // w.WriteFrame(t1.GetFrame(frame_number)); + //} + + // Close writer & reader + w.Close(); + + //map.Close(); + //map2.Close(); + + t1.Close(); + + // Clean up + cache.Clear(); + + r.Close(); + +} + + +/* +TEST(FrameMapperVideoEdition){ + + stringstream path; + path << TEST_MEDIA_PATH << "baseline.mkv"; + FFmpegReader r(path.str()); + r.Open(); + + Clip c1; + c1.Reader(&r); + c1.Layer(1); + c1.Position(0.0); + c1.Start(0.0); + c1.End(45.0); + + Clip c2; + c2.Reader(&r); + c2.Layer(1); + c2.Position(30.0); + c2.Start(0.0); + c2.End(45.0); + + Timeline t1(1280, 720, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); + t1.AddClip(&c1); + t1.AddClip(&c2); + + t1.Open(); + + + FFmpegWriter w("simple-edit.mp4"); + + // Set options + w.SetAudioOptions("aac", 44100, 192000); + w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); + + // Open writer + w.Open(); + + + w.WriteFrame(&t1, 1, t1.GetMaxFrame()); + + // Close writer & reader + w.Close(); + + //map.Close(); + //map2.Close(); + + t1.Close(); + + + r.Close(); + + + +}*/ \ No newline at end of file From ab4916247b93145e1c0b8d8e3a5af36221085d87 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 17:08:27 -0500 Subject: [PATCH 80/86] Replacing audio fix implementation with ParentClip(), to access clip start and position (if any) --- include/FrameMapper.h | 12 +++--- src/FrameMapper.cpp | 85 +++++++++++++++++++++---------------- src/Timeline.cpp | 4 +- tests/FrameMapper_Tests.cpp | 85 ++++++------------------------------- 4 files changed, 67 insertions(+), 119 deletions(-) diff --git a/include/FrameMapper.h b/include/FrameMapper.h index 85c933d25..7f6c9c165 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -147,9 +147,6 @@ namespace openshot bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping SWRCONTEXT *avr; // Audio resampling context object - float position; - float start; - // Internal methods used by init void AddField(int64_t frame); void AddField(Field field); @@ -157,6 +154,9 @@ namespace openshot // Get Frame or Generate Blank Frame std::shared_ptr GetOrCreateFrame(int64_t number); + /// Adjust frame number for Clip position and start (which can result in a different number) + int64_t AdjustFrameNumber(int64_t clip_frame_number, float position, float start); + // Use the original and target frame rates and a pull-down technique to create // a mapping between the original fields and frames or a video to a new frame rate. // This might repeat or skip fields and frames of the original video, depending on @@ -169,13 +169,13 @@ namespace openshot std::vector frames; // List of all frames /// Default constructor for openshot::FrameMapper class - FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); + FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); /// Destructor virtual ~FrameMapper(); /// Change frame rate or audio mapping details - void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart); + void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout); /// Close the openshot::FrameMapper and internal reader void Close() override; @@ -220,8 +220,6 @@ namespace openshot /// Resample audio and map channels (if needed) void ResampleMappedAudio(std::shared_ptr frame, int64_t original_frame_number); - - int64_t ConvPositon(int64_t clip_frame_number); }; } diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index 977302915..d43cbcdfa 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -29,11 +29,12 @@ */ #include "../include/FrameMapper.h" +#include "../include/Clip.h" using namespace std; using namespace openshot; -FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) : +FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) : reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL) { // Set the original frame rate from the reader @@ -52,8 +53,6 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe info.width = reader->info.width; info.height = reader->info.height; - position = clipPosition; - start = clipStart; // Used to toggle odd / even fields field_toggle = true; @@ -118,6 +117,15 @@ void FrameMapper::Init() // Clear cache final_cache.Clear(); + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Some framerates are handled special, and some use a generic Keyframe curve to // map the framerates. These are the special framerates: if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) && @@ -259,12 +267,12 @@ void FrameMapper::Init() // the original sample rate. int64_t end_samples_frame = start_samples_frame; int end_samples_position = start_samples_position; - int remaining_samples = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels); + int remaining_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels); while (remaining_samples > 0) { // get original samples - int original_samples = Frame::GetSamplesPerFrame(ConvPositon(end_samples_frame), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; + int original_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(end_samples_frame, clipPosition, clipStart), original, reader->info.sample_rate, reader->info.channels) - end_samples_position; // Enough samples if (original_samples >= remaining_samples) @@ -284,12 +292,12 @@ void FrameMapper::Init() // Create the sample mapping struct - SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels)}; + SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels)}; // Reset the audio variables start_samples_frame = end_samples_frame; start_samples_position = end_samples_position + 1; - if (start_samples_position >= Frame::GetSamplesPerFrame(ConvPositon(start_samples_frame), original, reader->info.sample_rate, reader->info.channels)) + if (start_samples_position >= Frame::GetSamplesPerFrame(AdjustFrameNumber(start_samples_frame, clipPosition, clipStart), original, reader->info.sample_rate, reader->info.channels)) { start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one) start_samples_position = 0; // reset to 0, since we wrapped @@ -355,8 +363,17 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number) { std::shared_ptr new_frame; + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now) - int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(number), target, reader->info.sample_rate, reader->info.channels); + int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(number, clipPosition, clipStart), target, reader->info.sample_rate, reader->info.channels); try { // Debug output @@ -406,6 +423,15 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) final_frame = final_cache.GetFrame(requested_frame); if (final_frame) return final_frame; + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Minimum number of frames to process (for performance reasons) // Dialing this down to 1 for now, as it seems to improve performance, and reduce export crashes int minimum_frames = 1; @@ -429,7 +455,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame) // Get # of channels in the actual frame int channels_in_frame = mapped_frame->GetAudioChannelsCount(); - int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, mapped_frame->SampleRate(), channels_in_frame); + int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number, clipPosition, clipStart), target, mapped_frame->SampleRate(), channels_in_frame); // Determine if mapped frame is identical to source frame // including audio sample distribution according to mapped.Samples, @@ -598,21 +624,6 @@ void FrameMapper::PrintMapping() // Recalculate mappings Init(); - // Get the difference (in frames) between the original and target frame rates - float difference = target.ToInt() - original.ToInt(); - - int field_interval = 0; - int frame_interval = 0; - - if (difference != 0) - { - // Find the number (i.e. interval) of fields that need to be skipped or repeated - field_interval = round(fabs(original.ToInt() / difference)); - - // Get frame interval (2 fields per frame) - frame_interval = field_interval * 2.0f; - } - // Loop through frame mappings for (float map = 1; map <= frames.size(); map++) { @@ -623,7 +634,6 @@ void FrameMapper::PrintMapping() } - // Determine if reader is open or closed bool FrameMapper::IsOpen() { if (reader) @@ -632,7 +642,6 @@ bool FrameMapper::IsOpen() { return false; } - // Open the internal reader void FrameMapper::Open() { @@ -691,7 +700,6 @@ Json::Value FrameMapper::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties root["type"] = "FrameMapper"; - root["position"] = position; // return JsonValue return root; @@ -720,10 +728,6 @@ void FrameMapper::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); - if(!root["position"].isNull()){ - position = root["position"].asDouble(); - } - // Re-Open path, and re-init everything (if needed) if (reader) { @@ -733,7 +737,7 @@ void FrameMapper::SetJsonValue(const Json::Value root) { } // Change frame rate or audio mapping details -void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) +void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) { ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ChangeMapping", "target_fps.num", target_fps.num, "target_fps.den", target_fps.den, "target_pulldown", target_pulldown, "target_sample_rate", target_sample_rate, "target_channels", target_channels, "target_channel_layout", target_channel_layout); @@ -752,9 +756,6 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow info.channels = target_channels; info.channel_layout = target_channel_layout; - position = clipPosition; - start = clipStart; - // Clear cache final_cache.Clear(); @@ -777,6 +778,15 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig // Recalculate mappings Init(); + // Get clip position from parent clip (if any) + float clipPosition = 0.0; + float clipStart = 0.0; + Clip *parent = (Clip *) ParentClip(); + if (parent) { + clipPosition = parent->Position(); + clipStart = parent->Start(); + } + // Init audio buffers / variables int total_frame_samples = 0; int channels_in_frame = frame->GetAudioChannelsCount(); @@ -838,7 +848,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig } // Update total samples & input frame size (due to bigger or smaller data types) - total_frame_samples = Frame::GetSamplesPerFrame(ConvPositon(frame->number), target, info.sample_rate, info.channels); + total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number, clipPosition, clipStart), target, info.sample_rate, info.channels); ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ResampleMappedAudio (adjust # of samples)", "total_frame_samples", total_frame_samples, "info.sample_rate", info.sample_rate, "sample_rate_in_frame", sample_rate_in_frame, "info.channels", info.channels, "channels_in_frame", channels_in_frame, "original_frame_number", original_frame_number); @@ -948,7 +958,8 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig resampled_samples = NULL; } -int64_t FrameMapper::ConvPositon(int64_t clip_frame_number){ +// Adjust frame number for Clip position and start (which can result in a different number) +int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number, float position, float start) { int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1; int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index b635b4995..f7f018e7c 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -360,14 +360,14 @@ void Timeline::apply_mapper_to_clip(Clip* clip) } else { // Create a new FrameMapper to wrap the current reader - FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); + FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); allocated_frame_mappers.insert(mapper); clip_reader = (ReaderBase*) mapper; } // Update the mapping FrameMapper* clip_mapped_reader = (FrameMapper*) clip_reader; - clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start()); + clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout); // Update clip reader clip->Reader(clip_reader); diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 02634c373..54e76e64c 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -42,7 +42,7 @@ TEST(FrameMapper_Get_Valid_Frame) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping between 24 fps and 29.97 fps using classic pulldown - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); try { @@ -63,7 +63,7 @@ TEST(FrameMapper_Invalid_Frame_Too_Small) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 29.97 fps - FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30000, 1001), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); // Check invalid frame number CHECK_THROW(mapping.GetMappedFrame(0), OutOfBoundsFrame); @@ -76,7 +76,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Classic) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); @@ -93,7 +93,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_Advanced) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -113,7 +113,7 @@ TEST(FrameMapper_24_fps_to_30_fps_Pulldown_None) DummyReader r(Fraction(24,1), 720, 480, 22000, 2, 5.0); // Create mapping 24 fps and 30 fps - FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(30, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -130,7 +130,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Classic) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_CLASSIC, 22000, 2, LAYOUT_STEREO); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -150,7 +150,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_Advanced) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_ADVANCED, 22000, 2, LAYOUT_STEREO); MappedFrame frame2 = mapping.GetMappedFrame(2); MappedFrame frame3 = mapping.GetMappedFrame(3); MappedFrame frame4 = mapping.GetMappedFrame(4); @@ -170,7 +170,7 @@ TEST(FrameMapper_30_fps_to_24_fps_Pulldown_None) DummyReader r(Fraction(30, 1), 720, 480, 22000, 2, 5.0); // Create mapping between 30 fps and 24 fps - FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO, 0.0, 0.0); + FrameMapper mapping(&r, Fraction(24, 1), PULLDOWN_NONE, 22000, 2, LAYOUT_STEREO); MappedFrame frame4 = mapping.GetMappedFrame(4); MappedFrame frame5 = mapping.GetMappedFrame(5); @@ -189,7 +189,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) FFmpegReader r(path.str()); // Map to 30 fps, 3 channels surround, 44100 sample rate - FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND, 0.0, 0.0); + FrameMapper map(&r, Fraction(30,1), PULLDOWN_NONE, 44100, 3, LAYOUT_SURROUND); map.Open(); // Check details @@ -199,7 +199,7 @@ TEST(FrameMapper_resample_audio_48000_to_41000) CHECK_EQUAL(1470, map.GetFrame(50)->GetAudioSamplesCount()); // Change mapping data - map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO, 0.0, 0.0); + map.ChangeMapping(Fraction(25,1), PULLDOWN_NONE, 22050, 1, LAYOUT_MONO); // Check details CHECK_EQUAL(1, map.GetFrame(1)->GetAudioChannelsCount()); @@ -213,13 +213,12 @@ TEST(FrameMapper_resample_audio_48000_to_41000) TEST(FrameMapper_AudioSample_Distribution) { - CacheMemory cache; int OFFSET = 0; float AMPLITUDE = 0.2; double ANGLE = 0.0; int NUM_SAMPLES = 100; - std::cout << "Starting Resample Test" << std::endl; + //std::cout << "Starting Resample Test" << std::endl; for (int64_t frame_number = 1; frame_number <= 90; frame_number++) { @@ -283,7 +282,6 @@ TEST(FrameMapper_AudioSample_Distribution) // which overlapping Frame instances have different # of samples for the Timeline. // TODO: Moving to 0.0 position, to simplify this test for now - c2.Position(0.041666667 * 14); c2.Start(1.0); c2.End(10.0); @@ -295,7 +293,7 @@ TEST(FrameMapper_AudioSample_Distribution) std::string json_val = t1.Json(); - std::cout << json_val << std::endl; + //std::cout << json_val << std::endl; //t1.SetJson(t1.Json()); t1.Open(); @@ -309,7 +307,6 @@ TEST(FrameMapper_AudioSample_Distribution) // Open writer w.Open(); - w.WriteFrame(&t1, 5, 50); //for (int64_t frame_number = 1; frame_number <= 90; frame_number++){ @@ -328,62 +325,4 @@ TEST(FrameMapper_AudioSample_Distribution) cache.Clear(); r.Close(); - } - - -/* -TEST(FrameMapperVideoEdition){ - - stringstream path; - path << TEST_MEDIA_PATH << "baseline.mkv"; - FFmpegReader r(path.str()); - r.Open(); - - Clip c1; - c1.Reader(&r); - c1.Layer(1); - c1.Position(0.0); - c1.Start(0.0); - c1.End(45.0); - - Clip c2; - c2.Reader(&r); - c2.Layer(1); - c2.Position(30.0); - c2.Start(0.0); - c2.End(45.0); - - Timeline t1(1280, 720, Fraction(24, 1), 44100, 2, LAYOUT_STEREO); - t1.AddClip(&c1); - t1.AddClip(&c2); - - t1.Open(); - - - FFmpegWriter w("simple-edit.mp4"); - - // Set options - w.SetAudioOptions("aac", 44100, 192000); - w.SetVideoOptions("libx264", 1280, 720, Fraction(24,1), 5000000); - - // Open writer - w.Open(); - - - w.WriteFrame(&t1, 1, t1.GetMaxFrame()); - - // Close writer & reader - w.Close(); - - //map.Close(); - //map2.Close(); - - t1.Close(); - - - r.Close(); - - - -}*/ \ No newline at end of file From 096c2c409dfb9bd6aceb104402910e80a1b2bedb Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 13 Oct 2020 18:18:10 -0500 Subject: [PATCH 81/86] Converting RGB8888 to ARGB32_Premultiplied (for performance reasons) --- src/CacheDisk.cpp | 2 +- src/FFmpegReader.cpp | 4 ++-- src/Frame.cpp | 26 ++++++++++++-------------- src/QtHtmlReader.cpp | 2 +- src/QtImageReader.cpp | 7 ------- src/QtTextReader.cpp | 2 +- src/effects/Bars.cpp | 2 +- src/effects/Crop.cpp | 2 +- src/effects/Deinterlace.cpp | 2 +- 9 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index b3fd119fd..c8eb5d8d6 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -235,7 +235,7 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number) image->load(frame_path); // Set pixel formatimage-> - image = std::make_shared(image->convertToFormat(QImage::Format_RGBA8888)); + image = std::make_shared(image->convertToFormat(QImage::Format_ARGB32_Premultiplied)); // Create frame object auto frame = std::make_shared(); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index dcdad2c0d..2e8260c5b 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1342,7 +1342,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { scale_mode = SWS_BICUBIC; } SwsContext *img_convert_ctx = sws_getContext(info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, - height, PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL); + height, AV_PIX_FMT_RGB32, scale_mode, NULL, NULL, NULL); // Resize / Convert to RGB sws_scale(img_convert_ctx, my_frame->data, my_frame->linesize, 0, @@ -1352,7 +1352,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { std::shared_ptr f = CreateFrame(current_frame); // Add Image data to frame - f->AddImage(width, height, 4, QImage::Format_RGBA8888, buffer); + f->AddImage(width, height, 4, QImage::Format_ARGB32_Premultiplied, buffer); // Update working cache working_cache.Add(f); diff --git a/src/Frame.cpp b/src/Frame.cpp index 8be002466..5c233aa27 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -221,8 +221,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G } // Create blank image - wave_image = std::make_shared( - total_width, total_height, QImage::Format_RGBA8888); + wave_image = std::make_shared(total_width, total_height, QImage::Format_ARGB32_Premultiplied); wave_image->fill(QColor(0,0,0,0)); // Load QPainter with wave_image device @@ -253,7 +252,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G else { // No audio samples present - wave_image = std::make_shared(width, height, QImage::Format_RGBA8888); + wave_image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); wave_image->fill(QColor(QString::fromStdString("#000000"))); } @@ -614,7 +613,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Create blank thumbnail image & fill background color auto thumbnail = std::make_shared( - new_width, new_height, QImage::Format_RGBA8888); + new_width, new_height, QImage::Format_ARGB32_Premultiplied); thumbnail->fill(QColor(QString::fromStdString(background_color))); // Create painter @@ -676,7 +675,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Set pixel format overlay = std::make_shared( - overlay->convertToFormat(QImage::Format_RGBA8888)); + overlay->convertToFormat(QImage::Format_ARGB32_Premultiplied)); // Resize to fit overlay = std::make_shared(overlay->scaled( @@ -696,7 +695,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Set pixel format mask = std::make_shared( - mask->convertToFormat(QImage::Format_RGBA8888)); + mask->convertToFormat(QImage::Format_ARGB32_Premultiplied)); // Resize to fit mask = std::make_shared(mask->scaled( @@ -753,7 +752,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - image = std::make_shared(new_width, new_height, QImage::Format_RGBA8888); + image = std::make_shared(new_width, new_height, QImage::Format_ARGB32_Premultiplied); // Fill with solid color image->fill(QColor(QString::fromStdString(color))); @@ -805,9 +804,9 @@ void Frame::AddImage(std::shared_ptr new_image) { image = new_image; - // Always convert to RGBA8888 (if different) - if (image->format() != QImage::Format_RGBA8888) - *image = image->convertToFormat(QImage::Format_RGBA8888); + // Always convert to Format_ARGB32_Premultiplied (if different) + if (image->format() != QImage::Format_ARGB32_Premultiplied) + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); // Update height and width width = image->width(); @@ -836,9 +835,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) if (image == new_image || image->size() != new_image->size()) { ret = true; } - else if (new_image->format() != image->format()) { - new_image = std::make_shared( - new_image->convertToFormat(image->format())); + else if (new_image->format() != QImage::Format_ARGB32_Premultiplied) { + new_image = std::make_shared(new_image->convertToFormat(QImage::Format_ARGB32_Premultiplied)); } } if (ret) { @@ -979,7 +977,7 @@ void Frame::AddMagickImage(std::shared_ptr new_image) // Create QImage of frame data image = std::make_shared( - qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, + qbuffer, width, height, width * BPP, QImage::Format_ARGB32_Premultiplied, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer); // Update height and width diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 4925d3a88..4da756ffb 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -62,7 +62,7 @@ void QtHtmlReader::Open() if (!is_open) { // create image - image = std::make_shared(width, height, QImage::Format_RGBA8888); + image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); image->fill(QColor(background_color.c_str())); //start painting diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 86040bd4d..b22ef22b8 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -98,10 +98,6 @@ void QtImageReader::Open() throw InvalidFile("File could not be opened.", path.toStdString()); } - // Convert to proper format - image = std::make_shared( - image->convertToFormat(QImage::Format_RGBA8888)); - // Update image properties info.has_audio = false; info.has_video = true; @@ -255,9 +251,6 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } - cached_image = std::make_shared( - cached_image->convertToFormat(QImage::Format_RGBA8888)); - // Set max size (to later determine if max_size is changed) max_size.setWidth(max_width); max_size.setHeight(max_height); diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index bd157ebd2..0bb1d7418 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -67,7 +67,7 @@ void QtTextReader::Open() if (!is_open) { // create image - image = std::make_shared(width, height, QImage::Format_RGBA8888); + image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); image->fill(QColor(background_color.c_str())); QPainter painter; diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index e653b7dda..d0dd8dbff 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -69,7 +69,7 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram // Get bar color (and create small color image) auto tempColor = std::make_shared( - frame_image->width(), 1, QImage::Format_RGBA8888); + frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied); tempColor->fill(QColor(QString::fromStdString(color.GetColorHex(frame_number)))); // Get current keyframe values diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index f0e0aa953..aa587a02a 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -69,7 +69,7 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram // Get transparent color (and create small transparent image) auto tempColor = std::make_shared( - frame_image->width(), 1, QImage::Format_RGBA8888); + frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied); tempColor->fill(QColor(QString::fromStdString("transparent"))); // Get current keyframe values diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 984336aa0..16c104c02 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -73,7 +73,7 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 const unsigned char* pixels = image->bits(); // Create a smaller, new image - QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_RGBA8888); + QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_ARGB32_Premultiplied); const unsigned char* deinterlaced_pixels = deinterlaced_image.bits(); // Loop through the scanlines of the image (even or odd) From 8e0c1b89fa3cd3141af50281ef8438c51d4a4b28 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 14 Oct 2020 03:06:30 -0500 Subject: [PATCH 82/86] Replacing ARGB32_Premultiplied with Format_RGBA8888_Premultiplied, which still seems to benefit from performance, but keeps the byte order the same as before. win win --- src/CacheDisk.cpp | 2 +- src/FFmpegReader.cpp | 4 ++-- src/Frame.cpp | 24 ++++++++++++------------ src/QtHtmlReader.cpp | 2 +- src/QtImageReader.cpp | 4 ++-- src/QtTextReader.cpp | 2 +- src/effects/Bars.cpp | 2 +- src/effects/Crop.cpp | 2 +- src/effects/Deinterlace.cpp | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index c8eb5d8d6..23985ea69 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -235,7 +235,7 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number) image->load(frame_path); // Set pixel formatimage-> - image = std::make_shared(image->convertToFormat(QImage::Format_ARGB32_Premultiplied)); + image = std::make_shared(image->convertToFormat(QImage::Format_RGBA8888_Premultiplied)); // Create frame object auto frame = std::make_shared(); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 2e8260c5b..2fedd9558 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1342,7 +1342,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { scale_mode = SWS_BICUBIC; } SwsContext *img_convert_ctx = sws_getContext(info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, - height, AV_PIX_FMT_RGB32, scale_mode, NULL, NULL, NULL); + height, PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL); // Resize / Convert to RGB sws_scale(img_convert_ctx, my_frame->data, my_frame->linesize, 0, @@ -1352,7 +1352,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) { std::shared_ptr f = CreateFrame(current_frame); // Add Image data to frame - f->AddImage(width, height, 4, QImage::Format_ARGB32_Premultiplied, buffer); + f->AddImage(width, height, 4, QImage::Format_RGBA8888_Premultiplied, buffer); // Update working cache working_cache.Add(f); diff --git a/src/Frame.cpp b/src/Frame.cpp index 5c233aa27..103338278 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -221,7 +221,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G } // Create blank image - wave_image = std::make_shared(total_width, total_height, QImage::Format_ARGB32_Premultiplied); + wave_image = std::make_shared(total_width, total_height, QImage::Format_RGBA8888_Premultiplied); wave_image->fill(QColor(0,0,0,0)); // Load QPainter with wave_image device @@ -252,7 +252,7 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G else { // No audio samples present - wave_image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); + wave_image = std::make_shared(width, height, QImage::Format_RGBA8888_Premultiplied); wave_image->fill(QColor(QString::fromStdString("#000000"))); } @@ -613,7 +613,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Create blank thumbnail image & fill background color auto thumbnail = std::make_shared( - new_width, new_height, QImage::Format_ARGB32_Premultiplied); + new_width, new_height, QImage::Format_RGBA8888_Premultiplied); thumbnail->fill(QColor(QString::fromStdString(background_color))); // Create painter @@ -675,7 +675,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Set pixel format overlay = std::make_shared( - overlay->convertToFormat(QImage::Format_ARGB32_Premultiplied)); + overlay->convertToFormat(QImage::Format_RGBA8888_Premultiplied)); // Resize to fit overlay = std::make_shared(overlay->scaled( @@ -695,7 +695,7 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri // Set pixel format mask = std::make_shared( - mask->convertToFormat(QImage::Format_ARGB32_Premultiplied)); + mask->convertToFormat(QImage::Format_RGBA8888_Premultiplied)); // Resize to fit mask = std::make_shared(mask->scaled( @@ -752,7 +752,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color) const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - image = std::make_shared(new_width, new_height, QImage::Format_ARGB32_Premultiplied); + image = std::make_shared(new_width, new_height, QImage::Format_RGBA8888_Premultiplied); // Fill with solid color image->fill(QColor(QString::fromStdString(color))); @@ -804,9 +804,9 @@ void Frame::AddImage(std::shared_ptr new_image) { image = new_image; - // Always convert to Format_ARGB32_Premultiplied (if different) - if (image->format() != QImage::Format_ARGB32_Premultiplied) - *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + // Always convert to Format_RGBA8888_Premultiplied (if different) + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); // Update height and width width = image->width(); @@ -835,8 +835,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) if (image == new_image || image->size() != new_image->size()) { ret = true; } - else if (new_image->format() != QImage::Format_ARGB32_Premultiplied) { - new_image = std::make_shared(new_image->convertToFormat(QImage::Format_ARGB32_Premultiplied)); + else if (new_image->format() != QImage::Format_RGBA8888_Premultiplied) { + new_image = std::make_shared(new_image->convertToFormat(QImage::Format_RGBA8888_Premultiplied)); } } if (ret) { @@ -977,7 +977,7 @@ void Frame::AddMagickImage(std::shared_ptr new_image) // Create QImage of frame data image = std::make_shared( - qbuffer, width, height, width * BPP, QImage::Format_ARGB32_Premultiplied, + qbuffer, width, height, width * BPP, QImage::Format_RGBA8888_Premultiplied, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer); // Update height and width diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index 4da756ffb..a93fdd219 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -62,7 +62,7 @@ void QtHtmlReader::Open() if (!is_open) { // create image - image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); + image = std::make_shared(width, height, QImage::Format_RGBA8888_Premultiplied); image->fill(QColor(background_color.c_str())); //start painting diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index b22ef22b8..6ec600c46 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -76,7 +76,7 @@ void QtImageReader::Open() if (renderer.isValid()) { image = std::make_shared( - renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied); + renderer.defaultSize(), QImage::Format_RGBA8888_Premultiplied); image->fill(Qt::transparent); QPainter p(image.get()); @@ -232,7 +232,7 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) // Create empty QImage cached_image = std::make_shared( QSize(svg_size.width(), svg_size.height()), - QImage::Format_ARGB32_Premultiplied); + QImage::Format_RGBA8888_Premultiplied); cached_image->fill(Qt::transparent); // Render SVG into QImage diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index 0bb1d7418..cbab42df9 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -67,7 +67,7 @@ void QtTextReader::Open() if (!is_open) { // create image - image = std::make_shared(width, height, QImage::Format_ARGB32_Premultiplied); + image = std::make_shared(width, height, QImage::Format_RGBA8888_Premultiplied); image->fill(QColor(background_color.c_str())); QPainter painter; diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index d0dd8dbff..42898054f 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -69,7 +69,7 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram // Get bar color (and create small color image) auto tempColor = std::make_shared( - frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied); + frame_image->width(), 1, QImage::Format_RGBA8888_Premultiplied); tempColor->fill(QColor(QString::fromStdString(color.GetColorHex(frame_number)))); // Get current keyframe values diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index aa587a02a..6813cf809 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -69,7 +69,7 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram // Get transparent color (and create small transparent image) auto tempColor = std::make_shared( - frame_image->width(), 1, QImage::Format_ARGB32_Premultiplied); + frame_image->width(), 1, QImage::Format_RGBA8888_Premultiplied); tempColor->fill(QColor(QString::fromStdString("transparent"))); // Get current keyframe values diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 16c104c02..71373080b 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -73,7 +73,7 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 const unsigned char* pixels = image->bits(); // Create a smaller, new image - QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_ARGB32_Premultiplied); + QImage deinterlaced_image(image->width(), image->height() / 2, QImage::Format_RGBA8888_Premultiplied); const unsigned char* deinterlaced_pixels = deinterlaced_image.bits(); // Loop through the scanlines of the image (even or odd) From d3d604cc08c9bb92a480af9a2aeb5c1e56d9947b Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 14 Oct 2020 14:19:26 -0500 Subject: [PATCH 83/86] Fix alpha and mask effects, so they correctly multiply the alpha to all colors (since we have switched to a premulitplied alpha format) --- src/Clip.cpp | 6 +++++- src/effects/Mask.cpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index 044543230..0e61d01fc 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1159,7 +1159,11 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) // Loop through pixels for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) { - // Apply alpha to pixel + // Apply alpha to pixel values (since we use a premultiplied value, we must + // multiply the alpha with all colors). + pixels[byte_index + 0] *= alpha_value; + pixels[byte_index + 1] *= alpha_value; + pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index f270e4109..d0e86cd16 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -118,6 +118,7 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram R = mask_pixels[byte_index]; G = mask_pixels[byte_index + 1]; B = mask_pixels[byte_index + 2]; + A = mask_pixels[byte_index + 3]; // Get the average luminosity gray_value = qGray(R, G, B); @@ -132,16 +133,23 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram // Constrain the value from 0 to 255 gray_value = constrain(gray_value); + // Calculate the % change in alpha + float alpha_percent = float(constrain(A - gray_value)) / 255.0; + // Set the alpha channel to the gray value if (replace_image) { - // Replace frame pixels with gray value + // Replace frame pixels with gray value (including alpha channel) pixels[byte_index + 0] = gray_value; pixels[byte_index + 1] = gray_value; pixels[byte_index + 2] = gray_value; + pixels[byte_index + 3] = gray_value; } else { - // Set alpha channel - A = pixels[byte_index + 3]; - pixels[byte_index + 3] = constrain(A - gray_value); + // Mulitply new alpha value with all the colors (since we are using a premultiplied + // alpha format) + pixels[byte_index + 0] *= alpha_percent; + pixels[byte_index + 1] *= alpha_percent; + pixels[byte_index + 2] *= alpha_percent; + pixels[byte_index + 3] *= alpha_percent; } } From 2f4aba450c1baca7c59dad43f34f2b43c6c4c7b2 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 18:04:10 -0500 Subject: [PATCH 84/86] Fixing some cpp_test complaints --- src/Clip.cpp | 3 ++- tests/FrameMapper_Tests.cpp | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index 0e61d01fc..b75ec689d 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1154,7 +1154,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) float alpha_value = alpha.GetValue(frame->number); // Get source image's pixels - unsigned char *pixels = (unsigned char *) source_image->bits(); + unsigned char *pixels = source_image->bits(); // Loop through pixels for (int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4) @@ -1166,6 +1166,7 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } + pixels = NULL; // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); diff --git a/tests/FrameMapper_Tests.cpp b/tests/FrameMapper_Tests.cpp index 54e76e64c..9caa2a18c 100644 --- a/tests/FrameMapper_Tests.cpp +++ b/tests/FrameMapper_Tests.cpp @@ -287,13 +287,8 @@ TEST(FrameMapper_AudioSample_Distribution) c2.End(10.0); // Add clips - t1.AddClip(&c1); t1.AddClip(&c2); - - std::string json_val = t1.Json(); - - //std::cout << json_val << std::endl; //t1.SetJson(t1.Json()); t1.Open(); From d2521bcbff73cc4ed25a2e3d34830b13872e0fde Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2020 18:22:42 -0500 Subject: [PATCH 85/86] Fixing some additional cpp_test complaints --- src/Clip.cpp | 1 - src/Timeline.cpp | 2 +- src/effects/Mask.cpp | 18 ++++++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index b75ec689d..7a8740568 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -1166,7 +1166,6 @@ void Clip::apply_keyframes(std::shared_ptr frame, int width, int height) pixels[byte_index + 2] *= alpha_value; pixels[byte_index + 3] *= alpha_value; } - pixels = NULL; // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index f7f018e7c..4d12b1374 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -492,7 +492,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in /* Apply effects to the source frame (if any). If multiple clips are overlapping, only process the * effects on the top clip. */ - if (is_top_clip && source_frame) { + if (is_top_clip) { #pragma omp critical (T_addLayer) source_frame = apply_effects(source_frame, timeline_frame_number, source_clip->Layer()); } diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index d0e86cd16..a631775fa 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -102,12 +102,6 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram unsigned char *pixels = (unsigned char *) frame_image->bits(); unsigned char *mask_pixels = (unsigned char *) original_mask->bits(); - int R = 0; - int G = 0; - int B = 0; - int A = 0; - int gray_value = 0; - float factor = 0.0; double contrast_value = (contrast.GetValue(frame_number)); double brightness_value = (brightness.GetValue(frame_number)); @@ -115,16 +109,16 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram for (int pixel = 0, byte_index=0; pixel < original_mask->width() * original_mask->height(); pixel++, byte_index+=4) { // Get the RGB values from the pixel - R = mask_pixels[byte_index]; - G = mask_pixels[byte_index + 1]; - B = mask_pixels[byte_index + 2]; - A = mask_pixels[byte_index + 3]; + int R = mask_pixels[byte_index]; + int G = mask_pixels[byte_index + 1]; + int B = mask_pixels[byte_index + 2]; + int A = mask_pixels[byte_index + 3]; // Get the average luminosity - gray_value = qGray(R, G, B); + int gray_value = qGray(R, G, B); // Adjust the contrast - factor = (259 * (contrast_value + 255)) / (255 * (259 - contrast_value)); + float factor = (259 * (contrast_value + 255)) / (255 * (259 - contrast_value)); gray_value = constrain((factor * (gray_value - 128)) + 128); // Adjust the brightness From cf89891bf0df20ff7228d486cddf2d520b751160 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 19 Oct 2020 17:42:12 -0400 Subject: [PATCH 86/86] Finish conflict resolution --- src/Clip.cpp | 2 +- src/TimelineBase.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Clip.cpp b/src/Clip.cpp index 2830ec4ba..1cffc9b2c 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -396,7 +396,7 @@ std::shared_ptr Clip::GetFrame(std::shared_ptr frame, in // Get time mapped frame number (used to increase speed, change direction, etc...) // TODO: Handle variable # of samples, since this resamples audio for different speeds (only when time curve is set) - get_time_mapped_frame(frame, requested_frame); + get_time_mapped_frame(frame, new_frame_number); // Adjust # of samples to match requested (the interaction with time curves will make this tricky) // TODO: Implement move samples to/from next frame diff --git a/src/TimelineBase.cpp b/src/TimelineBase.cpp index cc59a2433..4356167f5 100644 --- a/src/TimelineBase.cpp +++ b/src/TimelineBase.cpp @@ -28,7 +28,7 @@ * along with OpenShot Library. If not, see . */ -#include "../include/TimelineBase.h" +#include "TimelineBase.h" using namespace openshot;