Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timeline: Add clip/effect lookup api, GetMaxFrame/GetMaxTime method (w/ unit tests) #563

Merged
merged 8 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions include/Clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ namespace openshot {
/// Return the list of effects on the timeline
std::list<openshot::EffectBase*> Effects() { return effects; };

/// 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.
///
/// @returns The requested frame (containing the image)
Expand Down Expand Up @@ -253,8 +256,6 @@ namespace openshot {
openshot::Keyframe has_audio; ///< An optional override to determine if this clip has audio (-1=undefined, 0=no, 1=yes)
openshot::Keyframe has_video; ///< An optional override to determine if this clip has video (-1=undefined, 0=no, 1=yes)
};
} // namespace


}

#endif
#endif // OPENSHOT_CLIP_H
10 changes: 8 additions & 2 deletions include/ReaderBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,16 @@ namespace openshot
openshot::ReaderInfo info;

/// Parent clip object of this reader (which can be unparented and NULL)
openshot::ClipBase* GetClip();
inline openshot::ClipBase* GetParentClip() { return parent; };

/// Deprecated alias for GetParentClip()
inline openshot::ClipBase* GetClip() { return parent; };

/// Set parent clip object of this reader
void SetClip(openshot::ClipBase* clip);
inline void SetParentClip(openshot::ClipBase* clip) { parent = clip; };

/// Deprecated alias for SetParentClip()
inline void SetClip(openshot::ClipBase* clip) { parent = clip; };

/// Close the reader (and any resources it was consuming)
virtual void Close() = 0;
Expand Down
94 changes: 61 additions & 33 deletions include/Timeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ namespace openshot {
/// from lowest layer to top layer (since that is the sequence they need to be combined), and then
/// by position (left to right).
struct CompareClips{
bool operator()( Clip* lhs, Clip* rhs){
bool operator()( openshot::Clip* lhs, openshot::Clip* rhs){
if( lhs->Layer() < rhs->Layer() ) return true;
if( lhs->Layer() == rhs->Layer() && lhs->Position() <= rhs->Position() ) return true;
return false;
Expand All @@ -71,13 +71,28 @@ namespace openshot {
/// from lowest layer to top layer (since that is sequence clips are combined), and then by
/// position, and then by effect order.
struct CompareEffects{
bool operator()( EffectBase* lhs, EffectBase* rhs){
bool operator()( openshot::EffectBase* lhs, openshot::EffectBase* rhs){
if( lhs->Layer() < rhs->Layer() ) return true;
if( lhs->Layer() == rhs->Layer() && lhs->Position() < rhs->Position() ) return true;
if( lhs->Layer() == rhs->Layer() && lhs->Position() == rhs->Position() && lhs->Order() > rhs->Order() ) return true;
return false;
}};

/// Comparison method for finding the far end of the timeline, by locating
/// the Clip with the highest end-frame number using std::max_element
struct CompareClipEndFrames {
bool operator()(const openshot::Clip* lhs, const openshot::Clip* rhs) {
return (lhs->Position() + lhs->Duration())
<= (rhs->Position() + rhs->Duration());
ferdnyc marked this conversation as resolved.
Show resolved Hide resolved
}};

/// Like CompareClipEndFrames, but for effects
struct CompareEffectEndFrames {
bool operator()(const openshot::EffectBase* lhs, const openshot::EffectBase* rhs) {
return (lhs->Position() + lhs->Duration())
<= (rhs->Position() + rhs->Duration());
ferdnyc marked this conversation as resolved.
Show resolved Hide resolved
}};

/**
* @brief This class represents a timeline
*
Expand Down Expand Up @@ -146,47 +161,47 @@ namespace openshot {
* t.Close();
* @endcode
*/
class Timeline : public ReaderBase {
class Timeline : public openshot::ReaderBase {
private:
bool is_open; ///<Is Timeline Open?
bool auto_map_clips; ///< Auto map framerates and sample rates to all clips
std::list<Clip*> clips; ///<List of clips on this timeline
std::list<Clip*> closing_clips; ///<List of clips that need to be closed
std::map<Clip*, Clip*> open_clips; ///<List of 'opened' clips on this timeline
std::list<EffectBase*> effects; ///<List of clips on this timeline
CacheBase *final_cache; ///<Final cache of timeline frames
std::set<FrameMapper*> allocated_frame_mappers; ///< all the frame mappers we allocated and must free
std::list<openshot::Clip*> clips; ///<List of clips on this timeline
std::list<openshot::Clip*> closing_clips; ///<List of clips that need to be closed
std::map<openshot::Clip*, openshot::Clip*> open_clips; ///<List of 'opened' clips on this timeline
std::list<openshot::EffectBase*> effects; ///<List of clips on this timeline
openshot::CacheBase *final_cache; ///<Final cache of timeline frames
std::set<openshot::FrameMapper*> allocated_frame_mappers; ///< all the frame mappers we allocated and must free
bool managed_cache; ///< Does this timeline instance manage the cache object
std::string path; ///< Optional path of loaded UTF-8 OpenShot JSON project file

/// Process a new layer of video or audio
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);
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);

/// Apply a FrameMapper to a clip which matches the settings of this timeline
void apply_mapper_to_clip(Clip* clip);
void apply_mapper_to_clip(openshot::Clip* clip);

/// Apply JSON Diffs to various objects contained in this timeline
void apply_json_to_clips(Json::Value change); ///<Apply JSON diff to clips
void apply_json_to_effects(Json::Value change); ///< Apply JSON diff to effects
void apply_json_to_effects(Json::Value change, EffectBase* existing_effect); ///<Apply JSON diff to a specific effect
void apply_json_to_effects(Json::Value change, openshot::EffectBase* existing_effect); ///<Apply JSON diff to a specific effect
void apply_json_to_timeline(Json::Value change); ///<Apply JSON diff to timeline properties

/// Calculate time of a frame number, based on a framerate
double calculate_time(int64_t number, Fraction rate);
double calculate_time(int64_t number, openshot::Fraction rate);

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

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

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

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

/// Update the list of 'opened' clips
void update_open_clips(Clip *clip, bool does_clip_intersect);
void update_open_clips(openshot::Clip *clip, bool does_clip_intersect);

public:

Expand All @@ -209,7 +224,7 @@ namespace openshot {
/// @param sample_rate The sample rate of the timeline's audio
/// @param channels The number of audio channels of the timeline
/// @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);
Timeline(int width, int height, openshot::Fraction fps, int sample_rate, int channels, openshot::ChannelLayout channel_layout);

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

/// @brief Add an openshot::Clip to the timeline
/// @param clip Add an openshot::Clip to the timeline. A clip can contain any type of Reader.
void AddClip(Clip* clip);
void AddClip(openshot::Clip* clip);

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

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

/// Return a list of clips on the timeline
std::list<Clip*> Clips() { return clips; };
std::list<openshot::Clip*> Clips() { return clips; };

/// Look up a single clip by ID
openshot::ClipBase* GetClip(const std::string& id);

/// Look up a clip effect by ID
openshot::EffectBase* GetClipEffect(const std::string& id);

/// Look up a timeline effect by ID
openshot::EffectBase* GetEffect(const std::string& id);

/// Look up the end time of the latest timeline element
double GetMaxTime();
/// Look up the end frame number of the latest element on the timeline
int64_t GetMaxFrame();

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

/// Return the list of effects on the timeline
std::list<EffectBase*> Effects() { return effects; };
std::list<openshot::EffectBase*> Effects() { return effects; };

/// Get the cache object used by this reader
CacheBase* GetCache() override { return final_cache; };
openshot::CacheBase* GetCache() override { return final_cache; };

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

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

// Curves for the viewport
Keyframe viewport_scale; ///<Curve representing the scale of the viewport (0 to 100)
Keyframe viewport_x; ///<Curve representing the x coordinate for the viewport
Keyframe viewport_y; ///<Curve representing the y coordinate for the viewport
openshot::Keyframe viewport_scale; ///<Curve representing the scale of the viewport (0 to 100)
openshot::Keyframe viewport_x; ///<Curve representing the x coordinate for the viewport
openshot::Keyframe viewport_y; ///<Curve representing the y coordinate for the viewport

// Background color
Color color; ///<Background color of timeline canvas
openshot::Color color; ///<Background color of timeline canvas

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

/// @brief Remove an openshot::Clip from the timeline
/// @param clip Remove an openshot::Clip from the timeline.
void RemoveClip(Clip* clip);
void RemoveClip(openshot::Clip* clip);

/// @brief Remove an effect from the timeline
/// @param effect Remove an effect from the timeline.
void RemoveEffect(EffectBase* effect);
void RemoveEffect(openshot::EffectBase* effect);
};


}

#endif
#endif // OPENSHOT_TIMELINE_H
20 changes: 16 additions & 4 deletions src/Clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,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->SetParentClip(this);
}
}

Expand Down Expand Up @@ -210,7 +210,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->SetParentClip(this);
allocated_reader = reader;
init_reader_rotation();
}
Expand Down Expand Up @@ -239,7 +239,7 @@ void Clip::Reader(ReaderBase* new_reader)
reader = new_reader;

// set parent
reader->SetClip(this);
reader->SetParentClip(this);

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

// Look up an effect by ID
openshot::EffectBase* Clip::GetEffect(const std::string& id)
{
// Find the matching effect (if any)
for (const auto& effect : effects) {
if (effect->Id() == id) {
return effect;
}
}
return nullptr;
}

// Get file extension
std::string Clip::get_file_extension(std::string path)
{
Expand Down Expand Up @@ -993,7 +1005,7 @@ void Clip::SetJsonValue(const Json::Value root) {

// mark as managed reader and set parent
if (reader) {
reader->SetClip(this);
reader->SetParentClip(this);
allocated_reader = reader;
}

Expand Down
2 changes: 1 addition & 1 deletion src/FFmpegReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
if (max_height <= 0)
max_height = info.height;

Clip *parent = (Clip *) GetClip();
Clip *parent = (Clip *) GetParentClip();
if (parent) {
if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
// Best fit or Stretch scaling (based on max timeline size * scaling keyframes)
Expand Down
2 changes: 1 addition & 1 deletion src/QtImageReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ std::shared_ptr<Frame> QtImageReader::GetFrame(int64_t requested_frame)
if (max_height <= 0)
max_height = info.height;

Clip* parent = (Clip*) GetClip();
Clip* parent = (Clip*) GetParentClip();
if (parent) {
if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
// Best fit or Stretch scaling (based on max timeline size * scaling keyframes)
Expand Down
10 changes: 0 additions & 10 deletions src/ReaderBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,3 @@ 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;
}

/// Set parent clip object of this reader
void ReaderBase::SetClip(openshot::ClipBase* clip) {
parent = clip;
}