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