From cebf9728c86e99eb418e3c401ddd5d42d1ab97e6 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 10 Oct 2012 17:27:46 -0500 Subject: [PATCH] Refactored seek method a bit in the FFmpegReader, to fix some bugs. Also, being changing the cache object again, on how it cleans up pointers. Some debug code is checked in also. --- include/Exceptions.h | 10 ++++++ include/FFmpegReader.h | 5 +-- include/KeyFrame.h | 3 ++ src/Cache.cpp | 38 ++++------------------ src/Clip.cpp | 2 +- src/FFmpegReader.cpp | 73 +++++++++++++++++++++++++----------------- src/KeyFrame.cpp | 10 ++++++ src/Main.cpp | 6 ++-- 8 files changed, 80 insertions(+), 67 deletions(-) diff --git a/include/Exceptions.h b/include/Exceptions.h index 09978a78..b3420745 100644 --- a/include/Exceptions.h +++ b/include/Exceptions.h @@ -187,6 +187,16 @@ namespace openshot { virtual ~ResampleError() throw () {} }; + /// Exception when too many seek attempts happen + class TooManySeeks : public BaseException + { + public: + string file_path; + TooManySeeks(string message, string file_path) + : BaseException(message), file_path(file_path) { } + virtual ~TooManySeeks() throw () {} + }; + } #endif diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index 73b5b038..ddfc9e4a 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -83,6 +83,7 @@ namespace openshot int seeking_pts; int seeking_frame; bool is_video_seek; + int seek_count; int audio_pts_offset; int video_pts_offset; @@ -155,7 +156,7 @@ namespace openshot void RemoveScalers(); /// Seek to a specific Frame. This is not always frame accurate, it's more of an estimation on many codecs. - void Seek(int requested_frame); + void Seek(int requested_frame) throw(TooManySeeks); /// Update PTS Offset (if any) void UpdatePTSOffset(bool is_video); @@ -183,7 +184,7 @@ namespace openshot /// /// @returns The requested frame of video /// @param[requested_frame] number The frame number that is requested. - Frame* GetFrame(int requested_frame) throw(ReaderClosed); + Frame* GetFrame(int requested_frame) throw(ReaderClosed, TooManySeeks); /// Open File - which is called by the constructor automatically void Open() throw(InvalidFile, NoStreamsFound, InvalidCodec); diff --git a/include/KeyFrame.h b/include/KeyFrame.h index 8ed365de..81147427 100644 --- a/include/KeyFrame.h +++ b/include/KeyFrame.h @@ -79,6 +79,9 @@ namespace openshot { /// Add a new point on the key-frame, with some defaults set (BEZIER, AUTO Handles, etc...) void AddPoint(float x, float y); + /// Add a new point on the key-frame, with a specific interpolation type + void AddPoint(float x, float y, Interpolation_Type interpolate); + /// Set the handles, used for smooth curves. The handles are based on the surrounding points. void SetHandles(Point current); diff --git a/src/Cache.cpp b/src/Cache.cpp index a636a956..da9aafca 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -127,41 +127,17 @@ int Cache::Count() // Clean up cached frames that exceed the number in our max_frames variable void Cache::CleanUp() { + // Do we auto clean up? if (max_frames > 0) { - // Count previous frames (relative to the current frame), and determine the smallest frame number - // Loop through frame numbers - deque::iterator itr; - int previous_frames = 0; - int smallest_frame = -1; - for (itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr) { - if (*itr < current_frame) - previous_frames++; - - if (*itr < smallest_frame || smallest_frame == -1) - smallest_frame = *itr; - } - // check against max size - if (previous_frames > max_frames) { - // Get the difference - int diff = previous_frames - max_frames; - int removed_count = 0; + if (frame_numbers.size() > max_frames) + { + // Remove the oldest frame + int frame_to_remove = frame_numbers.back(); - // Loop through frames and remove the oldest - for (int x = smallest_frame; x < current_frame; x++) { - - // Does frame exist? - if (Exists(x)) { - // Remove the frame, increment count - Remove(x); - removed_count++; - } - - // Break after the correct # has been removed - if (removed_count == diff) - break; - } + // Remove frame_number and frame + Remove(frame_to_remove); } } } diff --git a/src/Clip.cpp b/src/Clip.cpp index 58aa78a1..20d3b67e 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -161,7 +161,7 @@ Frame* Clip::GetFrame(int requested_frame) throw(ReaderClosed) // Get time mapped frame number (used to increase speed, change direction, etc...) frame_number = adjust_frame_number_minimum(get_time_mapped_frame(frame_number)); - cout << "requested_frame: " << frame_number << endl; + cout << "Requested frame: " << frame_number << endl; // Now that we have re-mapped what frame number is needed, go and get the frame pointer Frame *frame = file_reader->GetFrame(frame_number); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 41612929..08083040 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -3,8 +3,8 @@ using namespace openshot; FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, InvalidCodec) - : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), - audio_pts_offset(99999), video_pts_offset(99999), working_cache(24), final_cache(24), path(path), + : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), + audio_pts_offset(99999), video_pts_offset(99999), working_cache(-1), final_cache(24), path(path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), rescaler_position(0), num_of_rescalers(32), is_open(false) { @@ -278,7 +278,7 @@ void FFmpegReader::UpdateVideoInfo() } -Frame* FFmpegReader::GetFrame(int requested_frame) throw(ReaderClosed) +Frame* FFmpegReader::GetFrame(int requested_frame) throw(ReaderClosed, TooManySeeks) { // Check for open reader (or throw exception) if (!is_open) @@ -306,21 +306,31 @@ Frame* FFmpegReader::GetFrame(int requested_frame) throw(ReaderClosed) // Get first frame ReadStream(1); - // Are we within 20 frames of the requested frame? + // Reset seek count + seek_count = 0; + + // Are we within 30 frames of the requested frame? int diff = requested_frame - last_frame; - if (abs(diff) >= 0 && abs(diff) <= 19) + if (diff >= 1 && diff <= 30) { // Continue walking the stream return ReadStream(requested_frame); } else { - // Greater than 20 frames away, we need to seek to the nearest key frame + // Greater than 30 frames away, or backwards, we need to seek to the nearest key frame cout << " >> TOO FAR, SO SEEK FIRST AND THEN WALK THE STREAM" << endl; if (enable_seek) // Only seek if enabled Seek(requested_frame); + else if (!enable_seek && diff < 0) + { + // Start over, since we can't seek, and the requested frame is smaller than our position + Close(); + Open(); + } + // Then continue walking the stream return ReadStream(requested_frame); } @@ -539,11 +549,13 @@ bool FFmpegReader::CheckSeek(bool is_video) cout << "Woops! Need to seek backwards further..." << endl; // Seek again... to the nearest Keyframe - Seek(seeking_frame - 10); + Seek(seeking_frame - 5); } else { // SEEK WORKED! + cout << "Seek worked, and we are now on current frame: " << ConvertVideoPTStoFrame(current_pts) << ", seeking frame: " << ConvertVideoPTStoFrame(seeking_pts) << endl; + // Seek worked, and we are "before" the requested frame is_seeking = false; seeking_pts = 0; @@ -856,7 +868,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int // Seek to a specific frame. This is not always frame accurate, it's more of an estimation on many codecs. -void FFmpegReader::Seek(int requested_frame) +void FFmpegReader::Seek(int requested_frame) throw(TooManySeeks) { cout << "SEEK TO " << requested_frame << endl; @@ -872,10 +884,17 @@ void FFmpegReader::Seek(int requested_frame) // Reset the last frame variable last_frame = 0; + // Increment seek count + seek_count++; + // Reset the current frame for the cache working_cache.SetCurrentFrame(requested_frame); final_cache.SetCurrentFrame(requested_frame); + // too many seeks + if (seek_count > 10) + throw TooManySeeks("Too many seek attempts... something seems wrong.", path); + // If seeking to frame 1, we need to close and re-open the file (this is more reliable than seeking) if (requested_frame == 1) { @@ -885,16 +904,17 @@ void FFmpegReader::Seek(int requested_frame) // Not actually seeking, so clear these flags is_seeking = false; + seeking_frame = 1; seeking_pts = ConvertFrameToVideoPTS(1); } else { // Seek to nearest key-frame (aka, i-frame) bool seek_worked = true; - int64_t seek_target = 0; // Seek video stream (if any) - if (info.has_video && av_seek_frame(pFormatCtx, info.video_stream_index, ConvertFrameToVideoPTS(requested_frame - 3), AVSEEK_FLAG_BACKWARD) < 0) { + int64_t seek_target = ConvertFrameToVideoPTS(requested_frame - 10); + if (info.has_video && av_seek_frame(pFormatCtx, info.video_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) { seek_worked = false; fprintf(stderr, "%s: error while seeking video stream\n", pFormatCtx->filename); } else @@ -902,15 +922,13 @@ void FFmpegReader::Seek(int requested_frame) // VIDEO SEEK is_video_seek = true; - // Set seek target - seek_target = ConvertFrameToVideoPTS(requested_frame - 3); - // Flush video buffer avcodec_flush_buffers(pCodecCtx); } // Seek audio stream (if not already seeked... and if an audio stream is found) - if (!seek_worked && info.has_audio && av_seek_frame(pFormatCtx, info.audio_stream_index, ConvertFrameToAudioPTS(requested_frame - 3), AVSEEK_FLAG_BACKWARD) < 0) { + seek_target = ConvertFrameToAudioPTS(requested_frame - 10); + if (!seek_worked && info.has_audio && av_seek_frame(pFormatCtx, info.audio_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) { seek_worked = false; fprintf(stderr, "%s: error while seeking audio stream\n", pFormatCtx->filename); } else @@ -918,9 +936,6 @@ void FFmpegReader::Seek(int requested_frame) // AUDIO SEEK is_video_seek = false; - // Set seek target - seek_target = ConvertFrameToAudioPTS(requested_frame - 3); - // Flush audio buffer avcodec_flush_buffers(aCodecCtx); } @@ -928,19 +943,17 @@ void FFmpegReader::Seek(int requested_frame) // Was the seek successful? if (seek_worked) { - // If not already seeking, set flags - if (!is_seeking) - { - // init seek flags - is_seeking = true; - seeking_frame = requested_frame; - seeking_pts = seek_target; - } - else - { - // update seek frame number - seeking_frame = requested_frame; - } + // init seek flags + is_seeking = true; + seeking_pts = seek_target; + seeking_frame = requested_frame; + } + else + { + // seek failed + is_seeking = false; + seeking_pts = 0; + seeking_frame = 0; } } diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index c3074f9b..f16a3984 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -74,6 +74,16 @@ void Keyframe::AddPoint(float x, float y) AddPoint(new_point); } +// Add a new point on the key-frame, with a specific interpolation type +void Keyframe::AddPoint(float x, float y, Interpolation_Type interpolate) +{ + // Create a point + Point new_point(x, y, interpolate); + + // Add the point + AddPoint(new_point); +} + // Set the handles, used for smooth curves. The handles are based // on the surrounding points. void Keyframe::SetHandles(Point current) diff --git a/src/Main.cpp b/src/Main.cpp index 9a95f9f3..0917bbb0 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -22,8 +22,8 @@ int main() // Add some clips Clip c1("/home/jonathan/Videos/sintel_trailer-720p.mp4"); c1.Position(0.0); - c1.time.AddPoint(600, 1240); - //c1.time.AddPoint(1240, 1); + c1.time.AddPoint(1, 1240); + c1.time.AddPoint(1240, 1, LINEAR); // Add clips t.AddClip(&c1); @@ -46,7 +46,7 @@ int main() // Output stream info w.OutputStreamInfo(); - for (int frame = 1; frame <= 1247; frame++) + for (int frame = 1; frame <= 100; frame++) { Frame *f = t.GetFrame(frame);