Fixing invalid cache after editing effect in a clip, and frame accuracy (converting timestamps into frame numbers). This makes frame by frame editing much better.

This commit is contained in:
Jonathan Thomas
2017-03-10 00:51:08 -06:00
parent bd85f1144d
commit ec65ca39b3
5 changed files with 94 additions and 48 deletions

View File

@@ -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> frame, long int original_frame_number);

View File

@@ -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<Clip*> 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);
};

View File

@@ -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

View File

@@ -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

View File

@@ -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<Frame> Timeline::apply_effects(tr1::shared_ptr<Frame> 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<EffectBase*>::iterator effect_itr;
@@ -178,21 +178,22 @@ tr1::shared_ptr<Frame> Timeline::apply_effects(tr1::shared_ptr<Frame> 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<Frame> 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<Frame> 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<Frame> 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<Clip*> Timeline::find_intersecting_clips(long int requested_frame, int nu
vector<Clip*> 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<Clip*> 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<Clip*>::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();
}
}
}