Skip to content

Commit 8f385c1

Browse files
authored
Improved Keyframe Performance (#197)
* Refactor of Coorindate and Keyframe optimized for performance (much faster than previously). Also refactored FrameMapper to not use a Keyframe, and to only process frame mapping when needed... speeding up Json loading of project files. * Fixing FrameMapper linear calculation to match existing Keyframe calculation * Fixing Keyframe to pass original unit tests, and correctly calculate the Keyframe repeat fractions and deltas. * Small refactor of time mapped logic in Clip.cpp
1 parent 728e002 commit 8f385c1

File tree

7 files changed

+166
-171
lines changed

7 files changed

+166
-171
lines changed

include/Clip.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ namespace openshot {
127127
std::shared_ptr<Frame> GetOrCreateFrame(int64_t number);
128128

129129
/// Adjust the audio and image of a time mapped frame
130-
std::shared_ptr<Frame> get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number);
130+
void get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number);
131131

132132
/// Init default settings for a clip
133133
void init_settings();

include/Coordinate.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ namespace openshot {
5252
* \endcode
5353
*/
5454
class Coordinate {
55-
private:
56-
bool increasing; ///< Is the Y value increasing or decreasing?
57-
Fraction repeated; ///< Fraction of repeated Y values (for example, 1/3 would be the first Y value of 3 repeated values)
58-
double delta; ///< This difference in Y value (from the previous unique Y value)
59-
6055
public:
6156
double X; ///< The X value of the coordinate (usually representing the frame #)
6257
double Y; ///< The Y value of the coordinate (usually representing the value of the property being animated)
@@ -69,27 +64,6 @@ namespace openshot {
6964
/// @param y The Y coordinate (usually representing the value of the property being animated)
7065
Coordinate(double x, double y);
7166

72-
/// @brief Set the repeating Fraction (used internally on the timeline, to track changes to coordinates)
73-
/// @param is_repeated The fraction representing how many times this coordinate Y value repeats (only used on the timeline)
74-
void Repeat(Fraction is_repeated) { repeated=is_repeated; }
75-
76-
/// Get the repeating Fraction (used internally on the timeline, to track changes to coordinates)
77-
Fraction Repeat() { return repeated; }
78-
79-
/// @brief Set the increasing flag (used internally on the timeline, to track changes to coordinates)
80-
/// @param is_increasing Indicates if this coordinate Y value is increasing (when compared to the previous coordinate)
81-
void IsIncreasing(bool is_increasing) { increasing = is_increasing; }
82-
83-
/// Get the increasing flag (used internally on the timeline, to track changes to coordinates)
84-
bool IsIncreasing() { return increasing; }
85-
86-
/// @brief Set the delta / difference between previous coordinate value (used internally on the timeline, to track changes to coordinates)
87-
/// @param new_delta Indicates how much this Y value differs from the previous Y value
88-
void Delta(double new_delta) { delta=new_delta; }
89-
90-
/// Get the delta / difference between previous coordinate value (used internally on the timeline, to track changes to coordinates)
91-
float Delta() { return delta; }
92-
9367
/// Get and Set JSON methods
9468
string Json(); ///< Generate JSON string of this object
9569
Json::Value JsonValue(); ///< Generate Json::JsonValue for this object

src/Clip.cpp

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -340,13 +340,13 @@ std::shared_ptr<Frame> Clip::GetFrame(int64_t requested_frame)
340340
frame->AddAudio(true, channel, 0, original_frame->GetAudioSamples(channel), original_frame->GetAudioSamplesCount(), 1.0);
341341

342342
// Get time mapped frame number (used to increase speed, change direction, etc...)
343-
std::shared_ptr<Frame> new_frame = get_time_mapped_frame(frame, requested_frame);
343+
get_time_mapped_frame(frame, requested_frame);
344344

345345
// Apply effects to the frame (if any)
346-
apply_effects(new_frame);
346+
apply_effects(frame);
347347

348348
// Return processed 'frame'
349-
return new_frame;
349+
return frame;
350350
}
351351
else
352352
// Throw error if reader not initialized
@@ -389,7 +389,7 @@ void Clip::reverse_buffer(juce::AudioSampleBuffer* buffer)
389389
}
390390

391391
// Adjust the audio and image of a time mapped frame
392-
std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number)
392+
void Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number)
393393
{
394394
// Check for valid reader
395395
if (!reader)
@@ -400,22 +400,14 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
400400
if (time.Values.size() > 1)
401401
{
402402
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
403-
std::shared_ptr<Frame> new_frame;
404403

405404
// create buffer and resampler
406405
juce::AudioSampleBuffer *samples = NULL;
407406
if (!resampler)
408407
resampler = new AudioResampler();
409408

410409
// Get new frame number
411-
int new_frame_number = adjust_frame_number_minimum(round(time.GetValue(frame_number)));
412-
413-
// Create a new frame
414-
int samples_in_frame = Frame::GetSamplesPerFrame(new_frame_number, reader->info.fps, reader->info.sample_rate, frame->GetAudioChannelsCount());
415-
new_frame = std::make_shared<Frame>(new_frame_number, 1, 1, "#000000", samples_in_frame, frame->GetAudioChannelsCount());
416-
417-
// Copy the image from the new frame
418-
new_frame->AddImage(std::shared_ptr<QImage>(new QImage(*GetOrCreateFrame(new_frame_number)->GetImage())));
410+
int new_frame_number = frame->number;
419411

420412
// Get delta (difference in previous Y value)
421413
int delta = int(round(time.GetDelta(frame_number)));
@@ -463,7 +455,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
463455
start -= 1;
464456
for (int channel = 0; channel < channels; channel++)
465457
// Add new (slower) samples, to the frame object
466-
new_frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, start),
458+
frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, start),
467459
number_of_samples, 1.0f);
468460

469461
// Clean up
@@ -571,7 +563,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
571563
// Add the newly resized audio samples to the current frame
572564
for (int channel = 0; channel < channels; channel++)
573565
// Add new (slower) samples, to the frame object
574-
new_frame->AddAudio(true, channel, 0, buffer->getReadPointer(channel), number_of_samples, 1.0f);
566+
frame->AddAudio(true, channel, 0, buffer->getReadPointer(channel), number_of_samples, 1.0f);
575567

576568
// Clean up
577569
buffer = NULL;
@@ -592,21 +584,15 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
592584

593585
// Add reversed samples to the frame object
594586
for (int channel = 0; channel < channels; channel++)
595-
new_frame->AddAudio(true, channel, 0, samples->getReadPointer(channel), number_of_samples, 1.0f);
587+
frame->AddAudio(true, channel, 0, samples->getReadPointer(channel), number_of_samples, 1.0f);
596588

597589

598590
}
599591

600592
delete samples;
601593
samples = NULL;
602594
}
603-
604-
// Return new time mapped frame
605-
return new_frame;
606-
607-
} else
608-
// Use original frame
609-
return frame;
595+
}
610596
}
611597

612598
// Adjust frame number minimum value

src/Coordinate.cpp

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ using namespace openshot;
3232

3333
// Default constructor for a coordinate, which defaults the X and Y to zero (0,0)
3434
Coordinate::Coordinate() :
35-
X(0), Y(0), increasing(true), repeated(1,1), delta(0.0) {
35+
X(0), Y(0) {
3636
}
3737

3838
// Constructor which also allows the user to set the X and Y
3939
Coordinate::Coordinate(double x, double y) :
40-
X(x), Y(y), increasing(true), repeated(1,1), delta(0.0) {
40+
X(x), Y(y) {
4141
}
4242

4343

@@ -96,15 +96,4 @@ void Coordinate::SetJsonValue(Json::Value root) {
9696
X = root["X"].asDouble();
9797
if (!root["Y"].isNull())
9898
Y = root["Y"].asDouble();
99-
if (!root["increasing"].isNull())
100-
increasing = root["increasing"].asBool();
101-
if (!root["repeated"].isNull() && root["repeated"].isObject())
102-
{
103-
if (!root["repeated"]["num"].isNull())
104-
repeated.num = root["repeated"]["num"].asInt();
105-
if (!root["repeated"]["den"].isNull())
106-
repeated.den = root["repeated"]["den"].asInt();
107-
}
108-
if (!root["delta"].isNull())
109-
delta = root["delta"].asDouble();
11099
}

src/FrameMapper.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe
5454

5555
// Adjust cache size based on size of frame and audio
5656
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
57-
58-
// init mapping between original and target frames
59-
Init();
6057
}
6158

6259
// Destructor
@@ -205,22 +202,23 @@ void FrameMapper::Init()
205202
}
206203

207204
} else {
208-
// Map the remaining framerates using a simple Keyframe curve
209-
// Calculate the difference (to be used as a multiplier)
205+
// Map the remaining framerates using a linear algorithm
210206
double rate_diff = target.ToDouble() / original.ToDouble();
211207
int64_t new_length = reader->info.video_length * rate_diff;
212208

213-
// Build curve for framerate mapping
214-
Keyframe rate_curve;
215-
rate_curve.AddPoint(1, 1, LINEAR);
216-
rate_curve.AddPoint(new_length, reader->info.video_length, LINEAR);
209+
// Calculate the value difference
210+
double value_increment = (reader->info.video_length + 1) / (double) (new_length);
217211

218212
// Loop through curve, and build list of frames
213+
double original_frame_num = 1.0f;
219214
for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
220215
{
221216
// Add 2 fields per frame
222-
AddField(rate_curve.GetInt(frame_num));
223-
AddField(rate_curve.GetInt(frame_num));
217+
AddField(round(original_frame_num));
218+
AddField(round(original_frame_num));
219+
220+
// Increment original frame number
221+
original_frame_num += value_increment;
224222
}
225223
}
226224

@@ -310,6 +308,11 @@ void FrameMapper::Init()
310308

311309
MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
312310
{
311+
// Check if mappings are dirty (and need to be recalculated)
312+
if (is_dirty)
313+
// Recalculate mappings
314+
Init();
315+
313316
// Ignore mapping on single image readers
314317
if (info.has_video and !info.has_audio and info.has_single_image) {
315318
// Return the same number
@@ -743,14 +746,16 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow
743746
SWR_FREE(&avr);
744747
avr = NULL;
745748
}
746-
747-
// Re-init mapping
748-
Init();
749749
}
750750

751751
// Resample audio and map channels (if needed)
752752
void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number)
753753
{
754+
// Check if mappings are dirty (and need to be recalculated)
755+
if (is_dirty)
756+
// Recalculate mappings
757+
Init();
758+
754759
// Init audio buffers / variables
755760
int total_frame_samples = 0;
756761
int channels_in_frame = frame->GetAudioChannelsCount();

0 commit comments

Comments
 (0)