Skip to content

Commit 131e441

Browse files
authored
Merge pull request #563 from ferdnyc/timeline-lookup-api
Timeline: Add clip/effect lookup api, GetMaxFrame/GetMaxTime method (w/ unit tests)
2 parents 6cc00d6 + f33d5cb commit 131e441

File tree

9 files changed

+319
-62
lines changed

9 files changed

+319
-62
lines changed

include/Clip.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ namespace openshot {
169169
/// Return the list of effects on the timeline
170170
std::list<openshot::EffectBase*> Effects() { return effects; };
171171

172+
/// Look up an effect by ID
173+
openshot::EffectBase* GetEffect(const std::string& id);
174+
172175
/// @brief Get an openshot::Frame object for a specific frame number of this timeline.
173176
///
174177
/// @returns The requested frame (containing the image)
@@ -253,8 +256,6 @@ namespace openshot {
253256
openshot::Keyframe has_audio; ///< An optional override to determine if this clip has audio (-1=undefined, 0=no, 1=yes)
254257
openshot::Keyframe has_video; ///< An optional override to determine if this clip has video (-1=undefined, 0=no, 1=yes)
255258
};
259+
} // namespace
256260

257-
258-
}
259-
260-
#endif
261+
#endif // OPENSHOT_CLIP_H

include/ReaderBase.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,16 @@ namespace openshot
111111
openshot::ReaderInfo info;
112112

113113
/// Parent clip object of this reader (which can be unparented and NULL)
114-
openshot::ClipBase* GetClip();
114+
inline openshot::ClipBase* GetParentClip() { return parent; };
115+
116+
/// Deprecated alias for GetParentClip()
117+
inline openshot::ClipBase* GetClip() { return parent; };
115118

116119
/// Set parent clip object of this reader
117-
void SetClip(openshot::ClipBase* clip);
120+
inline void SetParentClip(openshot::ClipBase* clip) { parent = clip; };
121+
122+
/// Deprecated alias for SetParentClip()
123+
inline void SetClip(openshot::ClipBase* clip) { parent = clip; };
118124

119125
/// Close the reader (and any resources it was consuming)
120126
virtual void Close() = 0;

include/Timeline.h

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace openshot {
6161
/// from lowest layer to top layer (since that is the sequence they need to be combined), and then
6262
/// by position (left to right).
6363
struct CompareClips{
64-
bool operator()( Clip* lhs, Clip* rhs){
64+
bool operator()( openshot::Clip* lhs, openshot::Clip* rhs){
6565
if( lhs->Layer() < rhs->Layer() ) return true;
6666
if( lhs->Layer() == rhs->Layer() && lhs->Position() <= rhs->Position() ) return true;
6767
return false;
@@ -71,13 +71,28 @@ namespace openshot {
7171
/// from lowest layer to top layer (since that is sequence clips are combined), and then by
7272
/// position, and then by effect order.
7373
struct CompareEffects{
74-
bool operator()( EffectBase* lhs, EffectBase* rhs){
74+
bool operator()( openshot::EffectBase* lhs, openshot::EffectBase* rhs){
7575
if( lhs->Layer() < rhs->Layer() ) return true;
7676
if( lhs->Layer() == rhs->Layer() && lhs->Position() < rhs->Position() ) return true;
7777
if( lhs->Layer() == rhs->Layer() && lhs->Position() == rhs->Position() && lhs->Order() > rhs->Order() ) return true;
7878
return false;
7979
}};
8080

81+
/// Comparison method for finding the far end of the timeline, by locating
82+
/// the Clip with the highest end-frame number using std::max_element
83+
struct CompareClipEndFrames {
84+
bool operator()(const openshot::Clip* lhs, const openshot::Clip* rhs) {
85+
return (lhs->Position() + lhs->Duration())
86+
<= (rhs->Position() + rhs->Duration());
87+
}};
88+
89+
/// Like CompareClipEndFrames, but for effects
90+
struct CompareEffectEndFrames {
91+
bool operator()(const openshot::EffectBase* lhs, const openshot::EffectBase* rhs) {
92+
return (lhs->Position() + lhs->Duration())
93+
<= (rhs->Position() + rhs->Duration());
94+
}};
95+
8196
/**
8297
* @brief This class represents a timeline
8398
*
@@ -146,47 +161,47 @@ namespace openshot {
146161
* t.Close();
147162
* @endcode
148163
*/
149-
class Timeline : public ReaderBase {
164+
class Timeline : public openshot::ReaderBase {
150165
private:
151166
bool is_open; ///<Is Timeline Open?
152167
bool auto_map_clips; ///< Auto map framerates and sample rates to all clips
153-
std::list<Clip*> clips; ///<List of clips on this timeline
154-
std::list<Clip*> closing_clips; ///<List of clips that need to be closed
155-
std::map<Clip*, Clip*> open_clips; ///<List of 'opened' clips on this timeline
156-
std::list<EffectBase*> effects; ///<List of clips on this timeline
157-
CacheBase *final_cache; ///<Final cache of timeline frames
158-
std::set<FrameMapper*> allocated_frame_mappers; ///< all the frame mappers we allocated and must free
168+
std::list<openshot::Clip*> clips; ///<List of clips on this timeline
169+
std::list<openshot::Clip*> closing_clips; ///<List of clips that need to be closed
170+
std::map<openshot::Clip*, openshot::Clip*> open_clips; ///<List of 'opened' clips on this timeline
171+
std::list<openshot::EffectBase*> effects; ///<List of clips on this timeline
172+
openshot::CacheBase *final_cache; ///<Final cache of timeline frames
173+
std::set<openshot::FrameMapper*> allocated_frame_mappers; ///< all the frame mappers we allocated and must free
159174
bool managed_cache; ///< Does this timeline instance manage the cache object
160175
std::string path; ///< Optional path of loaded UTF-8 OpenShot JSON project file
161176

162177
/// Process a new layer of video or audio
163-
void 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);
178+
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);
164179

165180
/// Apply a FrameMapper to a clip which matches the settings of this timeline
166-
void apply_mapper_to_clip(Clip* clip);
181+
void apply_mapper_to_clip(openshot::Clip* clip);
167182

168183
/// Apply JSON Diffs to various objects contained in this timeline
169184
void apply_json_to_clips(Json::Value change); ///<Apply JSON diff to clips
170185
void apply_json_to_effects(Json::Value change); ///< Apply JSON diff to effects
171-
void apply_json_to_effects(Json::Value change, EffectBase* existing_effect); ///<Apply JSON diff to a specific effect
186+
void apply_json_to_effects(Json::Value change, openshot::EffectBase* existing_effect); ///<Apply JSON diff to a specific effect
172187
void apply_json_to_timeline(Json::Value change); ///<Apply JSON diff to timeline properties
173188

174189
/// Calculate time of a frame number, based on a framerate
175-
double calculate_time(int64_t number, Fraction rate);
190+
double calculate_time(int64_t number, openshot::Fraction rate);
176191

177192
/// Find intersecting (or non-intersecting) openshot::Clip objects
178193
///
179194
/// @returns A list of openshot::Clip objects
180195
/// @param requested_frame The frame number that is requested.
181196
/// @param number_of_frames The number of frames to check
182197
/// @param include Include or Exclude intersecting clips
183-
std::vector<Clip*> find_intersecting_clips(int64_t requested_frame, int number_of_frames, bool include);
198+
std::vector<openshot::Clip*> find_intersecting_clips(int64_t requested_frame, int number_of_frames, bool include);
184199

185200
/// Get or generate a blank frame
186-
std::shared_ptr<Frame> GetOrCreateFrame(Clip* clip, int64_t number);
201+
std::shared_ptr<openshot::Frame> GetOrCreateFrame(openshot::Clip* clip, int64_t number);
187202

188203
/// Apply effects to the source frame (if any)
189-
std::shared_ptr<Frame> apply_effects(std::shared_ptr<Frame> frame, int64_t timeline_frame_number, int layer);
204+
std::shared_ptr<openshot::Frame> apply_effects(std::shared_ptr<openshot::Frame> frame, int64_t timeline_frame_number, int layer);
190205

191206
/// Compare 2 floating point numbers for equality
192207
bool isEqual(double a, double b);
@@ -198,7 +213,7 @@ namespace openshot {
198213
void sort_effects();
199214

200215
/// Update the list of 'opened' clips
201-
void update_open_clips(Clip *clip, bool does_clip_intersect);
216+
void update_open_clips(openshot::Clip *clip, bool does_clip_intersect);
202217

203218
public:
204219

@@ -209,7 +224,7 @@ namespace openshot {
209224
/// @param sample_rate The sample rate of the timeline's audio
210225
/// @param channels The number of audio channels of the timeline
211226
/// @param channel_layout The channel layout (i.e. mono, stereo, 3 point surround, etc...)
212-
Timeline(int width, int height, Fraction fps, int sample_rate, int channels, ChannelLayout channel_layout);
227+
Timeline(int width, int height, openshot::Fraction fps, int sample_rate, int channels, openshot::ChannelLayout channel_layout);
213228

214229
/// @brief Constructor for the timeline (which loads a JSON structure from a file path, and initializes a timeline)
215230
/// @param projectPath The path of the UTF-8 *.osp project file (JSON contents). Contents will be loaded automatically.
@@ -220,11 +235,11 @@ namespace openshot {
220235

221236
/// @brief Add an openshot::Clip to the timeline
222237
/// @param clip Add an openshot::Clip to the timeline. A clip can contain any type of Reader.
223-
void AddClip(Clip* clip);
238+
void AddClip(openshot::Clip* clip);
224239

225240
/// @brief Add an effect to the timeline
226241
/// @param effect Add an effect to the timeline. An effect can modify the audio or video of an openshot::Frame.
227-
void AddEffect(EffectBase* effect);
242+
void AddEffect(openshot::EffectBase* effect);
228243

229244
/// Apply the timeline's framerate and samplerate to all clips
230245
void ApplyMapperToClips();
@@ -239,34 +254,48 @@ namespace openshot {
239254
void ClearAllCache();
240255

241256
/// Return a list of clips on the timeline
242-
std::list<Clip*> Clips() { return clips; };
257+
std::list<openshot::Clip*> Clips() { return clips; };
258+
259+
/// Look up a single clip by ID
260+
openshot::ClipBase* GetClip(const std::string& id);
261+
262+
/// Look up a clip effect by ID
263+
openshot::EffectBase* GetClipEffect(const std::string& id);
264+
265+
/// Look up a timeline effect by ID
266+
openshot::EffectBase* GetEffect(const std::string& id);
267+
268+
/// Look up the end time of the latest timeline element
269+
double GetMaxTime();
270+
/// Look up the end frame number of the latest element on the timeline
271+
int64_t GetMaxFrame();
243272

244273
/// Close the timeline reader (and any resources it was consuming)
245274
void Close() override;
246275

247276
/// Return the list of effects on the timeline
248-
std::list<EffectBase*> Effects() { return effects; };
277+
std::list<openshot::EffectBase*> Effects() { return effects; };
249278

250279
/// Get the cache object used by this reader
251-
CacheBase* GetCache() override { return final_cache; };
280+
openshot::CacheBase* GetCache() override { return final_cache; };
252281

253282
/// Set the cache object used by this reader. You must now manage the lifecycle
254283
/// of this cache object though (Timeline will not delete it for you).
255-
void SetCache(CacheBase* new_cache);
284+
void SetCache(openshot::CacheBase* new_cache);
256285

257286
/// Get an openshot::Frame object for a specific frame number of this timeline.
258287
///
259288
/// @returns The requested frame (containing the image)
260289
/// @param requested_frame The frame number that is requested.
261-
std::shared_ptr<Frame> GetFrame(int64_t requested_frame) override;
290+
std::shared_ptr<openshot::Frame> GetFrame(int64_t requested_frame) override;
262291

263292
// Curves for the viewport
264-
Keyframe viewport_scale; ///<Curve representing the scale of the viewport (0 to 100)
265-
Keyframe viewport_x; ///<Curve representing the x coordinate for the viewport
266-
Keyframe viewport_y; ///<Curve representing the y coordinate for the viewport
293+
openshot::Keyframe viewport_scale; ///<Curve representing the scale of the viewport (0 to 100)
294+
openshot::Keyframe viewport_x; ///<Curve representing the x coordinate for the viewport
295+
openshot::Keyframe viewport_y; ///<Curve representing the y coordinate for the viewport
267296

268297
// Background color
269-
Color color; ///<Background color of timeline canvas
298+
openshot::Color color; ///<Background color of timeline canvas
270299

271300
/// Determine if reader is open or closed
272301
bool IsOpen() override { return is_open; };
@@ -295,14 +324,13 @@ namespace openshot {
295324

296325
/// @brief Remove an openshot::Clip from the timeline
297326
/// @param clip Remove an openshot::Clip from the timeline.
298-
void RemoveClip(Clip* clip);
327+
void RemoveClip(openshot::Clip* clip);
299328

300329
/// @brief Remove an effect from the timeline
301330
/// @param effect Remove an effect from the timeline.
302-
void RemoveEffect(EffectBase* effect);
331+
void RemoveEffect(openshot::EffectBase* effect);
303332
};
304333

305-
306334
}
307335

308-
#endif
336+
#endif // OPENSHOT_TIMELINE_H

src/Clip.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), alloca
153153
// Update duration and set parent
154154
if (reader) {
155155
End(reader->info.duration);
156-
reader->SetClip(this);
156+
reader->SetParentClip(this);
157157
}
158158
}
159159

@@ -210,7 +210,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N
210210
// Update duration and set parent
211211
if (reader) {
212212
End(reader->info.duration);
213-
reader->SetClip(this);
213+
reader->SetParentClip(this);
214214
allocated_reader = reader;
215215
init_reader_rotation();
216216
}
@@ -239,7 +239,7 @@ void Clip::Reader(ReaderBase* new_reader)
239239
reader = new_reader;
240240

241241
// set parent
242-
reader->SetClip(this);
242+
reader->SetParentClip(this);
243243

244244
// Init rotation (if any)
245245
init_reader_rotation();
@@ -368,6 +368,18 @@ std::shared_ptr<Frame> Clip::GetFrame(int64_t requested_frame)
368368
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
369369
}
370370

371+
// Look up an effect by ID
372+
openshot::EffectBase* Clip::GetEffect(const std::string& id)
373+
{
374+
// Find the matching effect (if any)
375+
for (const auto& effect : effects) {
376+
if (effect->Id() == id) {
377+
return effect;
378+
}
379+
}
380+
return nullptr;
381+
}
382+
371383
// Get file extension
372384
std::string Clip::get_file_extension(std::string path)
373385
{
@@ -993,7 +1005,7 @@ void Clip::SetJsonValue(const Json::Value root) {
9931005

9941006
// mark as managed reader and set parent
9951007
if (reader) {
996-
reader->SetClip(this);
1008+
reader->SetParentClip(this);
9971009
allocated_reader = reader;
9981010
}
9991011

src/FFmpegReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
12961296
if (max_height <= 0)
12971297
max_height = info.height;
12981298

1299-
Clip *parent = (Clip *) GetClip();
1299+
Clip *parent = (Clip *) GetParentClip();
13001300
if (parent) {
13011301
if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
13021302
// Best fit or Stretch scaling (based on max timeline size * scaling keyframes)

src/QtImageReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ std::shared_ptr<Frame> QtImageReader::GetFrame(int64_t requested_frame)
187187
if (max_height <= 0)
188188
max_height = info.height;
189189

190-
Clip* parent = (Clip*) GetClip();
190+
Clip* parent = (Clip*) GetParentClip();
191191
if (parent) {
192192
if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
193193
// Best fit or Stretch scaling (based on max timeline size * scaling keyframes)

src/ReaderBase.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,3 @@ void ReaderBase::SetJsonValue(const Json::Value root) {
249249
}
250250
}
251251
}
252-
253-
/// Parent clip object of this reader (which can be unparented and NULL)
254-
openshot::ClipBase* ReaderBase::GetClip() {
255-
return parent;
256-
}
257-
258-
/// Set parent clip object of this reader
259-
void ReaderBase::SetClip(openshot::ClipBase* clip) {
260-
parent = clip;
261-
}

0 commit comments

Comments
 (0)