diff --git a/include/FrameMapper.h b/include/FrameMapper.h
index 784c5cd1..58ed4dc7 100644
--- a/include/FrameMapper.h
+++ b/include/FrameMapper.h
@@ -214,6 +214,9 @@ namespace openshot
/// Print all of the original frames and which new frames they map to
void PrintMapping();
+ /// Get the current reader
+ ReaderBase* Reader() throw(ReaderClosed);
+
/// Resample audio and map channels (if needed)
void ResampleMappedAudio(tr1::shared_ptr frame, long int original_frame_number);
diff --git a/include/Timeline.h b/include/Timeline.h
index f8998321..cd3235c8 100644
--- a/include/Timeline.h
+++ b/include/Timeline.h
@@ -221,6 +221,9 @@ namespace openshot {
/// @brief Automatically map all clips to the timeline's framerate and samplerate
void AutoMapClips(bool auto_map) { auto_map_clips = auto_map; };
+ /// Clear all cache for this timeline instance, and all clips, mappers, and readers under it
+ void ClearAllCache();
+
/// Return a list of clips on the timeline
list Clips() { return clips; };
@@ -278,7 +281,6 @@ namespace openshot {
/// @brief Remove an effect from the timeline
/// @param effect Remove an effect from the timeline.
void RemoveEffect(EffectBase* effect);
-
};
diff --git a/src/CacheMemory.cpp b/src/CacheMemory.cpp
index 36d40e64..f41948d5 100644
--- a/src/CacheMemory.cpp
+++ b/src/CacheMemory.cpp
@@ -283,6 +283,7 @@ void CacheMemory::Clear()
frames.clear();
frame_numbers.clear();
ordered_frame_numbers.clear();
+ needs_range_processing = true;
}
// Count the frames in the queue
diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp
index 76c6a1e3..bcd01516 100644
--- a/src/FrameMapper.cpp
+++ b/src/FrameMapper.cpp
@@ -66,6 +66,16 @@ FrameMapper::~FrameMapper() {
Close();
}
+/// Get the current reader
+ReaderBase* FrameMapper::Reader() throw(ReaderClosed)
+{
+ if (reader)
+ return reader;
+ else
+ // Throw error if reader not initialized
+ throw ReaderClosed("No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.", "");
+}
+
void FrameMapper::AddField(long int frame)
{
// Add a field, and toggle the odd / even field
diff --git a/src/Timeline.cpp b/src/Timeline.cpp
index bb0b9254..29703d3b 100644
--- a/src/Timeline.cpp
+++ b/src/Timeline.cpp
@@ -59,6 +59,9 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha
info.has_video = true;
info.video_length = info.fps.ToFloat() * info.duration;
+ // Init max image size
+ SetMaxSize(info.width, info.height);
+
// Init cache
final_cache = new CacheMemory();
final_cache->SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
@@ -164,11 +167,8 @@ float Timeline::calculate_time(long int number, Fraction rate)
// Apply effects to the source frame (if any)
tr1::shared_ptr Timeline::apply_effects(tr1::shared_ptr frame, long int timeline_frame_number, int layer)
{
- // Calculate time of frame
- float requested_time = calculate_time(timeline_frame_number, info.fps);
-
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects", "requested_time", requested_time, "frame->number", frame->number, "timeline_frame_number", timeline_frame_number, "layer", layer, "", -1, "", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects", "frame->number", frame->number, "timeline_frame_number", timeline_frame_number, "layer", layer, "", -1, "", -1, "", -1);
// Find Effects at this position and layer
list::iterator effect_itr;
@@ -178,21 +178,22 @@ tr1::shared_ptr Timeline::apply_effects(tr1::shared_ptr frame, lon
EffectBase *effect = (*effect_itr);
// Does clip intersect the current requested time
- float effect_duration = effect->End() - effect->Start();
- bool does_effect_intersect = (effect->Position() <= requested_time && effect->Position() + effect_duration >= requested_time && effect->Layer() == layer);
+ long effect_start_position = round(effect->Position() * info.fps.ToDouble()) + 1;
+ long effect_end_position = round((effect->Position() + (effect->End() - effect->Start())) * info.fps.ToDouble()) + 1;
+
+ bool does_effect_intersect = (effect_start_position <= timeline_frame_number && effect_end_position >= timeline_frame_number && effect->Layer() == layer);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects (Does effect intersect)", "effect->Position()", effect->Position(), "requested_time", requested_time, "does_effect_intersect", does_effect_intersect, "timeline_frame_number", timeline_frame_number, "layer", layer, "effect_duration", effect_duration);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects (Does effect intersect)", "effect->Position()", effect->Position(), "does_effect_intersect", does_effect_intersect, "timeline_frame_number", timeline_frame_number, "layer", layer, "", -1, "", -1);
// Clip is visible
if (does_effect_intersect)
{
// Determine the frame needed for this clip (based on the position on the timeline)
- float time_diff = (requested_time - effect->Position()) + effect->Start();
- int effect_frame_number = round(time_diff * info.fps.ToFloat()) + 1;
+ int effect_frame_number = (timeline_frame_number - effect_start_position) + 1;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects (Process Effect)", "time_diff", time_diff, "effect_frame_number", effect_frame_number, "effect_duration", effect_duration, "does_effect_intersect", does_effect_intersect, "", -1, "", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects (Process Effect)", "effect_frame_number", effect_frame_number, "does_effect_intersect", does_effect_intersect, "", -1, "", -1, "", -1, "", -1);
// Apply the effect to this frame
frame = effect->GetFrame(frame, effect_frame_number);
@@ -656,19 +657,19 @@ tr1::shared_ptr Timeline::GetFrame(long int requested_frame) throw(Reader
// Determine all clip frames, and request them in order (to keep resampled audio in sequence)
for (long int frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
{
- // Calculate time of timeline frame
- float requested_time = calculate_time(frame_number, info.fps);
// Loop through clips
for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++)
{
// Get clip object from the iterator
Clip *clip = nearby_clips[clip_index];
- bool does_clip_intersect = (clip->Position() <= requested_time && clip->Position() + clip->Duration() >= requested_time);
+ long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1;
+ long clip_end_position = round((clip->Position() + (clip->End() - clip->Start())) * info.fps.ToDouble()) + 1;
+
+ bool does_clip_intersect = (clip_start_position <= frame_number && clip_end_position >= frame_number);
if (does_clip_intersect)
{
// Get clip frame #
- float time_diff = (requested_time - clip->Position()) + clip->Start();
- int clip_frame_number = round(time_diff * info.fps.ToFloat()) + 1;
+ int clip_frame_number = (frame_number - clip_start_position) + 1;
// Cache clip object
clip->GetFrame(clip_frame_number);
}
@@ -701,23 +702,21 @@ tr1::shared_ptr Timeline::GetFrame(long int requested_frame) throw(Reader
(color.red.GetValue(frame_number) != 0.0 || color.green.GetValue(frame_number) != 0.0 || color.blue.GetValue(frame_number) != 0.0))
new_frame->AddColor(info.width, info.height, color.GetColorHex(frame_number));
- // Calculate time of frame
- float requested_time = calculate_time(frame_number, info.fps);
-
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "requested_time", requested_time, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size(), "", -1, "", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size(), "", -1, "", -1, "", -1);
// Find Clips near this time
for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++)
{
// Get clip object from the iterator
Clip *clip = nearby_clips[clip_index];
+ long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1;
+ long clip_end_position = round((clip->Position() + (clip->End() - clip->Start())) * info.fps.ToDouble()) + 1;
- // Does clip intersect the current requested time
- bool does_clip_intersect = (clip->Position() <= requested_time && clip->Position() + clip->Duration() >= requested_time);
+ bool does_clip_intersect = (clip_start_position <= frame_number && clip_end_position >= frame_number);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Does clip intersect)", "frame_number", frame_number, "requested_time", requested_time, "clip->Position()", clip->Position(), "clip->Duration()", clip->Duration(), "does_clip_intersect", does_clip_intersect, "", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Does clip intersect)", "frame_number", frame_number, "clip->Position()", clip->Position(), "clip->Duration()", clip->Duration(), "does_clip_intersect", does_clip_intersect, "", -1, "", -1);
// Clip is visible
if (does_clip_intersect)
@@ -727,27 +726,29 @@ tr1::shared_ptr Timeline::GetFrame(long int requested_frame) throw(Reader
for (int top_clip_index = 0; top_clip_index < nearby_clips.size(); top_clip_index++)
{
Clip *nearby_clip = nearby_clips[top_clip_index];
+ long nearby_clip_start_position = round(nearby_clip->Position() * info.fps.ToDouble()) + 1;
+ long nearby_clip_end_position = round((nearby_clip->Position() + (nearby_clip->End() - nearby_clip->Start())) * info.fps.ToDouble()) + 1;
+
if (clip->Id() != nearby_clip->Id() && clip->Layer() == nearby_clip->Layer() &&
- nearby_clip->Position() <= requested_time && nearby_clip->Position() + nearby_clip->Duration() >= requested_time &&
- nearby_clip->Position() > clip->Position()) {
+ nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number &&
+ nearby_clip_start_position > clip_start_position) {
is_top_clip = false;
break;
}
}
// Determine the frame needed for this clip (based on the position on the timeline)
- float time_diff = (requested_time - clip->Position()) + clip->Start();
- int clip_frame_number = round(time_diff * info.fps.ToFloat()) + 1;
+ int clip_frame_number = (frame_number - clip_start_position) + 1;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Calculate clip's frame #)", "time_diff", time_diff, "requested_time", requested_time, "clip->Position()", clip->Position(), "clip->Start()", clip->Start(), "info.fps.ToFloat()", info.fps.ToFloat(), "clip_frame_number", clip_frame_number);
+ 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, "", -1, "", -1);
// Add clip's frame as layer
add_layer(new_frame, clip, clip_frame_number, frame_number, is_top_clip);
} else
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (clip does not intersect)", "frame_number", frame_number, "requested_time", requested_time, "does_clip_intersect", does_clip_intersect, "", -1, "", -1, "", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (clip does not intersect)", "frame_number", frame_number, "does_clip_intersect", does_clip_intersect, "", -1, "", -1, "", -1, "", -1);
} // end clip loop
@@ -779,8 +780,8 @@ vector Timeline::find_intersecting_clips(long int requested_frame, int nu
vector matching_clips;
// Calculate time of frame
- float min_requested_time = calculate_time(requested_frame, info.fps);
- float max_requested_time = calculate_time(requested_frame + (number_of_frames - 1), info.fps);
+ float min_requested_frame = requested_frame;
+ float max_requested_frame = requested_frame + (number_of_frames - 1);
// Re-Sort Clips (since they likely changed)
sort_clips();
@@ -793,18 +794,20 @@ vector Timeline::find_intersecting_clips(long int requested_frame, int nu
Clip *clip = (*clip_itr);
// Does clip intersect the current requested time
- float clip_duration = clip->End() - clip->Start();
- bool does_clip_intersect = (clip->Position() <= min_requested_time && clip->Position() + clip_duration >= min_requested_time) ||
- (clip->Position() > min_requested_time && clip->Position() <= max_requested_time);
+ long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1;
+ long clip_end_position = round((clip->Position() + (clip->End() - clip->Start())) * info.fps.ToDouble()) + 1;
+
+ bool does_clip_intersect =
+ (clip_start_position <= min_requested_frame || clip_start_position <= max_requested_frame) &&
+ (clip_end_position >= min_requested_frame || clip_end_position >= max_requested_frame);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::find_intersecting_clips (Is clip near or intersecting)", "requested_frame", requested_frame, "min_requested_time", min_requested_time, "max_requested_time", max_requested_time, "clip->Position()", clip->Position(), "clip_duration", clip_duration, "does_clip_intersect", does_clip_intersect);
+ ZmqLogger::Instance()->AppendDebugMethod("Timeline::find_intersecting_clips (Is clip near or intersecting)", "requested_frame", requested_frame, "min_requested_frame", min_requested_frame, "max_requested_frame", max_requested_frame, "clip->Position()", clip->Position(), "does_clip_intersect", does_clip_intersect, "", -1);
// Open (or schedule for closing) this clip, based on if it's intersecting or not
#pragma omp critical (reader_lock)
update_open_clips(clip, does_clip_intersect);
-
// Clip is visible
if (does_clip_intersect && include)
// Add the intersecting clip
@@ -1059,6 +1062,12 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
if (e->Id() == effect_id) {
// Apply the change to the effect directly
apply_json_to_effects(change, e);
+
+ // Calculate start and end frames that this impacts, and remove those frames from the cache
+ long int new_starting_frame = (existing_clip->Position() * info.fps.ToDouble()) + 1;
+ long int new_ending_frame = ((existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble()) + 1;
+ final_cache->Remove(new_starting_frame - 2, new_ending_frame + 2);
+
return; // effect found, don't update clip
}
}
@@ -1068,8 +1077,8 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
// Calculate start and end frames that this impacts, and remove those frames from the cache
if (!change["value"].isArray() && !change["value"]["position"].isNull()) {
- long int new_starting_frame = change["value"]["position"].asDouble() * info.fps.ToDouble();
- long int new_ending_frame = (change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble();
+ long int new_starting_frame = (change["value"]["position"].asDouble() * info.fps.ToDouble()) + 1;
+ long int new_ending_frame = ((change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble()) + 1;
final_cache->Remove(new_starting_frame - 2, new_ending_frame + 2);
}
@@ -1087,8 +1096,8 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
if (existing_clip) {
// Calculate start and end frames that this impacts, and remove those frames from the cache
- long int old_starting_frame = existing_clip->Position() * info.fps.ToDouble();
- long int old_ending_frame = (existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble();
+ long int old_starting_frame = (existing_clip->Position() * info.fps.ToDouble()) + 1;
+ long int old_ending_frame = ((existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble()) + 1;
final_cache->Remove(old_starting_frame - 2, old_ending_frame + 2);
// Update clip properties from JSON
@@ -1101,8 +1110,8 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
if (existing_clip) {
// Calculate start and end frames that this impacts, and remove those frames from the cache
- long int old_starting_frame = existing_clip->Position() * info.fps.ToDouble();
- long int old_ending_frame = (existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble();
+ long int old_starting_frame = (existing_clip->Position() * info.fps.ToDouble()) + 1;
+ long int old_ending_frame = ((existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble()) + 1;
final_cache->Remove(old_starting_frame - 2, old_ending_frame + 2);
// Remove clip from timeline
@@ -1162,8 +1171,8 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef
// Calculate start and end frames that this impacts, and remove those frames from the cache
if (!change["value"].isArray() && !change["value"]["position"].isNull()) {
- long int new_starting_frame = change["value"]["position"].asDouble() * info.fps.ToDouble();
- long int new_ending_frame = (change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble();
+ long int new_starting_frame = (change["value"]["position"].asDouble() * info.fps.ToDouble()) + 1;
+ long int new_ending_frame = ((change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble()) + 1;
final_cache->Remove(new_starting_frame - 2, new_ending_frame + 2);
}
@@ -1191,8 +1200,8 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef
if (existing_effect) {
// Calculate start and end frames that this impacts, and remove those frames from the cache
- long int old_starting_frame = existing_effect->Position() * info.fps.ToDouble();
- long int old_ending_frame = (existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble();
+ long int old_starting_frame = (existing_effect->Position() * info.fps.ToDouble()) + 1;
+ long int old_ending_frame = ((existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble()) + 1;
final_cache->Remove(old_starting_frame - 2, old_ending_frame + 2);
// Update effect properties from JSON
@@ -1205,8 +1214,8 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef
if (existing_effect) {
// Calculate start and end frames that this impacts, and remove those frames from the cache
- long int old_starting_frame = existing_effect->Position() * info.fps.ToDouble();
- long int old_ending_frame = (existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble();
+ long int old_starting_frame = (existing_effect->Position() * info.fps.ToDouble()) + 1;
+ long int old_ending_frame = ((existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble()) + 1;
final_cache->Remove(old_starting_frame - 2, old_ending_frame + 2);
// Remove effect from timeline
@@ -1310,6 +1319,27 @@ void Timeline::apply_json_to_timeline(Json::Value change) throw(InvalidJSONKey)
}
+// Clear all caches
+void Timeline::ClearAllCache() {
+ // Clear primary cache
+ final_cache->Clear();
+ // Loop through all clips
+ list::iterator clip_itr;
+ for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr)
+ {
+ // Get clip object from the iterator
+ Clip *clip = (*clip_itr);
+ // Clear cache on clip
+ clip->Reader()->GetCache()->Clear();
+ // Clear nested Reader (if any)
+ if (clip->Reader()->Name() == "FrameMapper") {
+ FrameMapper* nested_reader = (FrameMapper*) clip->Reader();
+ if (nested_reader->Reader() && nested_reader->Reader()->GetCache())
+ nested_reader->Reader()->GetCache()->Clear();
+ }
+
+ }
+}
\ No newline at end of file