Skip to content

Commit cd7d6fe

Browse files
committed
Merge remote-tracking branch 'origin/develop' into effect-parenting
2 parents 461a030 + b09416c commit cd7d6fe

File tree

10 files changed

+130
-49
lines changed

10 files changed

+130
-49
lines changed

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ option(DISABLE_BUNDLED_JSONCPP "Don't fall back to bundled JsonCpp" OFF)
8080
option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF)
8181

8282
option(ENABLE_PARALLEL_CTEST "Run CTest using multiple processors" ON)
83+
option(VERBOSE_TESTS "Run CTest with maximum verbosity" OFF)
8384
option(ENABLE_COVERAGE "Scan test coverage using gcov and report" OFF)
8485

8586
option(ENABLE_DOCS "Build API documentation (requires Doxygen)" ON)
@@ -187,9 +188,12 @@ if(BUILD_TESTING)
187188
ProcessorCount(CPU_COUNT)
188189
if(CPU_COUNT GREATER 1)
189190
add_feature_info("Parallel tests" TRUE "Unit tests can use ${CPU_COUNT} processors")
190-
set(CTEST_OPTIONS "-j${CPU_COUNT}")
191+
list(APPEND CTEST_OPTIONS "-j${CPU_COUNT}")
191192
endif()
192193
endif()
194+
if(VERBOSE_TESTS)
195+
list(APPEND CTEST_OPTIONS "-VV")
196+
endif()
193197
add_subdirectory(tests)
194198
endif()
195199
add_feature_info("Unit tests" ${BUILD_TESTING} "Compile unit tests for library functions")

src/Clip.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,15 +374,36 @@ std::shared_ptr<Frame> Clip::GetFrame(int64_t frame_number)
374374

375375
// Get the original frame and pass it to GetFrame overload
376376
std::shared_ptr<Frame> original_frame = GetOrCreateFrame(frame_number);
377-
return GetFrame(original_frame, frame_number);
377+
return GetFrame(original_frame, frame_number, NULL);
378378
}
379379
else
380380
// Throw error if reader not initialized
381381
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
382382
}
383383

384-
// Use an existing openshot::Frame object and draw this Clip's frame onto it
384+
// Create an openshot::Frame object for a specific frame number of this reader.
385385
std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t frame_number)
386+
{
387+
// Check for open reader (or throw exception)
388+
if (!is_open)
389+
throw ReaderClosed("The Clip is closed. Call Open() before calling this method.");
390+
391+
if (reader)
392+
{
393+
// Adjust out of bounds frame number
394+
frame_number = adjust_frame_number_minimum(frame_number);
395+
396+
// Get the original frame and pass it to GetFrame overload
397+
std::shared_ptr<Frame> original_frame = GetOrCreateFrame(frame_number);
398+
return GetFrame(original_frame, frame_number, NULL);
399+
}
400+
else
401+
// Throw error if reader not initialized
402+
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
403+
}
404+
405+
// Use an existing openshot::Frame object and draw this Clip's frame onto it
406+
std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t frame_number, openshot::TimelineInfoStruct* options)
386407
{
387408
// Check for open reader (or throw exception)
388409
if (!is_open)
@@ -406,9 +427,18 @@ std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> backgroun
406427
// TODO: Handle variable # of samples, since this resamples audio for different speeds (only when time curve is set)
407428
get_time_mapped_frame(original_frame, new_frame_number);
408429

409-
// Apply effects to the frame (if any)
430+
// Apply local effects to the frame (if any)
410431
apply_effects(original_frame);
411432

433+
// Apply global timeline effects (i.e. transitions & masks... if any)
434+
if (timeline != NULL && options != NULL) {
435+
if (options->is_top_clip) {
436+
// Apply global timeline effects (only to top clip... if overlapping, pass in timeline frame number)
437+
Timeline* timeline_instance = (Timeline*) timeline;
438+
original_frame = timeline_instance->apply_effects(original_frame, background_frame->number, Layer());
439+
}
440+
}
441+
412442
// Apply keyframe / transforms
413443
apply_keyframes(original_frame, background_frame->GetImage());
414444

src/Clip.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ namespace openshot {
169169
/// Reverse an audio buffer
170170
void reverse_buffer(juce::AudioSampleBuffer* buffer);
171171

172+
172173
public:
173174
openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent
174175
openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent
@@ -235,25 +236,39 @@ namespace openshot {
235236
/// Look up an effect by ID
236237
openshot::EffectBase* GetEffect(const std::string& id);
237238

238-
/// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number
239+
/// @brief Get an openshot::Frame object for a specific frame number of this clip. The image size and number
239240
/// of samples match the source reader.
240241
///
241242
/// @returns A new openshot::Frame object
242-
/// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline.
243+
/// @param frame_number The frame number (starting at 1) of the clip
243244
std::shared_ptr<openshot::Frame> GetFrame(int64_t frame_number) override;
244245

245-
/// @brief Get an openshot::Frame object for a specific frame number of this timeline. The image size and number
246-
/// of samples can be customized to match the Timeline, or any custom output. Extra samples will be moved to the
247-
/// next Frame. Missing samples will be moved from the next Frame.
246+
/// @brief Get an openshot::Frame object for a specific frame number of this clip. The image size and number
247+
/// of samples match the background_frame passed in and the timeline (if available).
248248
///
249249
/// A new openshot::Frame objects is returned, based on a copy from the source image, with all keyframes and clip effects
250-
/// rendered.
250+
/// rendered/rasterized.
251251
///
252252
/// @returns The modified openshot::Frame object
253253
/// @param background_frame The frame object to use as a background canvas (i.e. an existing Timeline openshot::Frame instance)
254-
/// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline.
254+
/// @param frame_number The frame number (starting at 1) of the clip. The image size and number
255+
/// of samples match the background_frame passed in and the timeline (if available)
255256
std::shared_ptr<openshot::Frame> GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t frame_number);
256257

258+
/// @brief Get an openshot::Frame object for a specific frame number of this clip. The image size and number
259+
/// of samples match the background_frame passed in and the timeline (if available).
260+
///
261+
/// A new openshot::Frame objects is returned, based on a copy from the source image, with all keyframes and clip effects
262+
/// rendered/rasterized.
263+
///
264+
/// @returns The modified openshot::Frame object
265+
/// @param background_frame The frame object to use as a background canvas (i.e. an existing Timeline openshot::Frame instance)
266+
/// @param frame_number The frame number (starting at 1) of the clip on the timeline. The image size and number
267+
/// of samples match the timeline.
268+
/// @param options The openshot::TimelineInfoStruct pointer, with more details about this specific timeline clip,
269+
/// such as, if it's a top clip. This info is used to apply global transitions and masks, if needed.
270+
std::shared_ptr<openshot::Frame> GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t frame_number, openshot::TimelineInfoStruct* options);
271+
257272
/// Open the internal reader
258273
void Open() override;
259274

src/Settings.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
2929
*/
3030

31+
#include <cstdlib> // For std::getenv
32+
3133
#include "Settings.h"
3234

33-
using namespace std;
3435
using namespace openshot;
3536

36-
3737
// Global reference to Settings
38-
Settings *Settings::m_pInstance = NULL;
38+
Settings *Settings::m_pInstance = nullptr;
3939

4040
// Create or Get an instance of the settings singleton
4141
Settings *Settings::Instance()
@@ -53,6 +53,9 @@ Settings *Settings::Instance()
5353
m_pInstance->HW_EN_DEVICE_SET = 0;
5454
m_pInstance->PLAYBACK_AUDIO_DEVICE_NAME = "";
5555
m_pInstance->DEBUG_TO_STDERR = false;
56+
auto env_debug = std::getenv("LIBOPENSHOT_DEBUG");
57+
if (env_debug != nullptr)
58+
m_pInstance->DEBUG_TO_STDERR = true;
5659
}
5760

5861
return m_pInstance;

src/Settings.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,7 @@
3131
#ifndef OPENSHOT_SETTINGS_H
3232
#define OPENSHOT_SETTINGS_H
3333

34-
35-
#include <iostream>
36-
#include <iomanip>
37-
#include <fstream>
38-
#include <cstdlib>
3934
#include <string>
40-
#include <sstream>
41-
#include <cstdio>
42-
#include <ctime>
43-
#include <zmq.hpp>
44-
#include <unistd.h>
45-
#include <OpenShotAudio.h>
46-
4735

4836
namespace openshot {
4937

@@ -118,7 +106,7 @@ namespace openshot {
118106
/// The current install path of OpenShot (needs to be set when using Timeline(path), since certain
119107
/// paths depend on the location of OpenShot transitions and files)
120108
std::string PATH_OPENSHOT_INSTALL = "";
121-
109+
122110
/// Whether to dump ZeroMQ debug messages to stderr
123111
bool DEBUG_TO_STDERR = false;
124112

src/Timeline.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ std::shared_ptr<Frame> Timeline::apply_effects(std::shared_ptr<Frame> frame, int
572572
}
573573

574574
// Get or generate a blank frame
575-
std::shared_ptr<Frame> Timeline::GetOrCreateFrame(std::shared_ptr<Frame> background_frame, Clip* clip, int64_t number)
575+
std::shared_ptr<Frame> Timeline::GetOrCreateFrame(std::shared_ptr<Frame> background_frame, Clip* clip, int64_t number, openshot::TimelineInfoStruct* options)
576576
{
577577
std::shared_ptr<Frame> new_frame;
578578

@@ -584,7 +584,7 @@ std::shared_ptr<Frame> Timeline::GetOrCreateFrame(std::shared_ptr<Frame> backgro
584584
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetOrCreateFrame (from reader)", "number", number, "samples_in_frame", samples_in_frame);
585585

586586
// Attempt to get a frame (but this could fail if a reader has just been closed)
587-
new_frame = std::shared_ptr<Frame>(clip->GetFrame(background_frame, number));
587+
new_frame = std::shared_ptr<Frame>(clip->GetFrame(background_frame, number, options));
588588

589589
// Return real frame
590590
return new_frame;
@@ -603,29 +603,28 @@ std::shared_ptr<Frame> Timeline::GetOrCreateFrame(std::shared_ptr<Frame> backgro
603603
}
604604

605605
// Process a new layer of video or audio
606-
void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, int64_t clip_frame_number, int64_t timeline_frame_number, bool is_top_clip, float max_volume)
606+
void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, int64_t clip_frame_number, bool is_top_clip, float max_volume)
607607
{
608-
// Get the clip's frame, composited on top of the current timeline frame
608+
// Create timeline options (with details about this current frame request)
609+
TimelineInfoStruct* options = new TimelineInfoStruct();
610+
options->is_top_clip = is_top_clip;
611+
612+
// Get the clip's frame, composited on top of the current timeline frame
609613
std::shared_ptr<Frame> source_frame;
610-
source_frame = GetOrCreateFrame(new_frame, source_clip, clip_frame_number);
614+
source_frame = GetOrCreateFrame(new_frame, source_clip, clip_frame_number, options);
615+
delete options;
611616

612617
// No frame found... so bail
613618
if (!source_frame)
614619
return;
615620

616621
// Debug output
617-
ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer", "new_frame->number", new_frame->number, "clip_frame_number", clip_frame_number, "timeline_frame_number", timeline_frame_number);
618-
619-
/* Apply effects to the source frame (if any). If multiple clips are overlapping, only process the
620-
* effects on the top clip. */
621-
if (is_top_clip) {
622-
source_frame = apply_effects(source_frame, timeline_frame_number, source_clip->Layer());
623-
}
622+
ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer", "new_frame->number", new_frame->number, "clip_frame_number", clip_frame_number);
624623

625624
/* COPY AUDIO - with correct volume */
626625
if (source_clip->Reader()->info.has_audio) {
627626
// Debug output
628-
ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Copy Audio)", "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);
627+
ZmqLogger::Instance()->AppendDebugMethod("Timeline::add_layer (Copy Audio)", "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);
629628

630629
if (source_frame->GetAudioChannelsCount() == info.channels && source_clip->has_audio.GetInt(clip_frame_number) != 0)
631630
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
@@ -678,7 +677,7 @@ void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, in
678677
}
679678
else
680679
// Debug output
681-
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);
680+
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);
682681
}
683682

684683
// Debug output
@@ -893,7 +892,7 @@ std::shared_ptr<Frame> Timeline::GetFrame(int64_t requested_frame)
893892
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Calculate clip's frame #)", "clip->Position()", clip->Position(), "clip->Start()", clip->Start(), "info.fps.ToFloat()", info.fps.ToFloat(), "clip_frame_number", clip_frame_number);
894893

895894
// Add clip's frame as layer
896-
add_layer(new_frame, clip, clip_frame_number, requested_frame, is_top_clip, max_volume);
895+
add_layer(new_frame, clip, clip_frame_number, is_top_clip, max_volume);
897896

898897
} else {
899898
// Debug output

src/Timeline.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ namespace openshot {
183183
std::map<std::string, std::shared_ptr<openshot::TrackedObjectBase>> tracked_objects; ///< map of TrackedObjectBBoxes and their IDs
184184

185185
/// Process a new layer of video or audio
186-
void add_layer(std::shared_ptr<openshot::Frame> new_frame, openshot::Clip* source_clip, int64_t clip_frame_number, int64_t timeline_frame_number, bool is_top_clip, float max_volume);
186+
void add_layer(std::shared_ptr<openshot::Frame> new_frame, openshot::Clip* source_clip, int64_t clip_frame_number, bool is_top_clip, float max_volume);
187187

188188
/// Apply a FrameMapper to a clip which matches the settings of this timeline
189189
void apply_mapper_to_clip(openshot::Clip* clip);
@@ -206,10 +206,7 @@ namespace openshot {
206206
std::vector<openshot::Clip*> find_intersecting_clips(int64_t requested_frame, int number_of_frames, bool include);
207207

208208
/// Get a clip's frame or generate a blank frame
209-
std::shared_ptr<openshot::Frame> GetOrCreateFrame(std::shared_ptr<Frame> background_frame, openshot::Clip* clip, int64_t number);
210-
211-
/// Apply effects to the source frame (if any)
212-
std::shared_ptr<openshot::Frame> apply_effects(std::shared_ptr<openshot::Frame> frame, int64_t timeline_frame_number, int layer);
209+
std::shared_ptr<openshot::Frame> GetOrCreateFrame(std::shared_ptr<Frame> background_frame, openshot::Clip* clip, int64_t number, openshot::TimelineInfoStruct* options);
213210

214211
/// Compare 2 floating point numbers for equality
215212
bool isEqual(double a, double b);
@@ -268,6 +265,9 @@ namespace openshot {
268265
/// @param effect Add an effect to the timeline. An effect can modify the audio or video of an openshot::Frame.
269266
void AddEffect(openshot::EffectBase* effect);
270267

268+
/// Apply global/timeline effects to the source frame (if any)
269+
std::shared_ptr<openshot::Frame> apply_effects(std::shared_ptr<openshot::Frame> frame, int64_t timeline_frame_number, int layer);
270+
271271
/// Apply the timeline's framerate and samplerate to all clips
272272
void ApplyMapperToClips();
273273

src/TimelineBase.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,23 @@
3131
#ifndef OPENSHOT_TIMELINE_BASE_H
3232
#define OPENSHOT_TIMELINE_BASE_H
3333

34+
#include <cstdint>
35+
36+
3437
namespace openshot {
38+
/**
39+
* @brief This struct contains info about the current Timeline clip instance
40+
*
41+
* When the Timeline requests an openshot::Frame instance from a Clip, it passes
42+
* this struct along, with some additional details from the Timeline, such as if this clip is
43+
* above or below overlapping clips, etc... This info can help determine if a Clip should apply
44+
* global effects from the Timeline, such as a global Transition/Mask effect.
45+
*/
46+
struct TimelineInfoStruct
47+
{
48+
bool is_top_clip; ///< Is clip on top (if overlapping another clip)
49+
};
50+
3551
/**
3652
* @brief This class represents a timeline (used for building generic timeline implementations)
3753
*/

tests/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
# along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
2525
################################################################################
2626

27+
# Allow spaces in test names
28+
if(POLICY CMP0110)
29+
cmake_policy(SET CMP0110 NEW)
30+
endif()
31+
2732
# Test media path, used by unit tests for input data
2833
file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH)
2934

@@ -103,6 +108,18 @@ foreach(tname ${OPENSHOT_TESTS})
103108
list(APPEND CATCH2_TEST_TARGETS openshot-${tname}-test)
104109
list(APPEND CATCH2_TEST_NAMES ${tname})
105110
endforeach()
111+
# Add an additional special-case test, for an envvar-dependent setting
112+
add_test(NAME [=["Settings:Debug logging (enabled)"]=]
113+
COMMAND
114+
openshot-Settings-test "[environment]"
115+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
116+
)
117+
set_tests_properties([=["Settings:Debug logging (enabled)"]=]
118+
PROPERTIES
119+
LABELS Settings
120+
ENVIRONMENT "LIBOPENSHOT_DEBUG=1"
121+
)
122+
106123
# Export target list for coverage use
107124
set(UNIT_TEST_TARGETS ${CATCH2_TEST_TARGETS} PARENT_SCOPE)
108125
set(UNIT_TEST_NAMES ${CATCH2_TEST_NAMES} PARENT_SCOPE)

tests/Settings.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
using namespace openshot;
3636

37-
TEST_CASE( "Default_Constructor", "[libopenshot][settings]" )
37+
TEST_CASE( "Constructor", "[libopenshot][settings]" )
3838
{
3939
// Create an empty color
4040
Settings *s = Settings::Instance();
@@ -43,7 +43,7 @@ TEST_CASE( "Default_Constructor", "[libopenshot][settings]" )
4343
CHECK_FALSE(s->HIGH_QUALITY_SCALING);
4444
}
4545

46-
TEST_CASE( "Change_Settings", "[libopenshot][settings]" )
46+
TEST_CASE( "Change settings", "[libopenshot][settings]" )
4747
{
4848
// Create an empty color
4949
Settings *s = Settings::Instance();
@@ -56,3 +56,12 @@ TEST_CASE( "Change_Settings", "[libopenshot][settings]" )
5656
CHECK(Settings::Instance()->OMP_THREADS == 8);
5757
CHECK(Settings::Instance()->HIGH_QUALITY_SCALING == true);
5858
}
59+
60+
TEST_CASE( "Debug logging", "[libopenshot][settings][environment]")
61+
{
62+
// Check the environment
63+
auto envvar = std::getenv("LIBOPENSHOT_DEBUG");
64+
const auto is_enabled = bool(envvar != nullptr);
65+
66+
CHECK(Settings::Instance()->DEBUG_TO_STDERR == is_enabled);
67+
}

0 commit comments

Comments
 (0)