From 217c54a5ae1d3cb8642fa9c3a9ef1461ebbcffdd Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Thu, 11 Oct 2012 17:30:32 -0500 Subject: [PATCH] Major refactor to the cache object (again)! It now uses bytes to determine how many frames to keep. --- include/Cache.h | 32 +++++------- include/Frame.h | 3 ++ src/Cache.cpp | 27 +++++++--- src/FFmpegReader.cpp | 10 +--- src/Frame.cpp | 13 +++++ src/Main.cpp | 8 ++- tests/Cache_Tests.cpp | 115 +++++++++--------------------------------- 7 files changed, 78 insertions(+), 130 deletions(-) diff --git a/include/Cache.h b/include/Cache.h index d0ae950f..b88ea4a1 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -21,26 +21,24 @@ namespace openshot { * * Due to the high cost of decoding streams, once a frame is decoded, converted to RGB, and a Frame object is created, * it critical to keep these Frames cached for performance reasons. However, the larger the cache, the more memory - * is required. You can set the max number of previous frames (relative to the current frame) to cache. Frames that - * come after the current frame are allowed to increase as much as needed (a requirement of the file readers). A call - * to GetFrame() sets the current frame of the cache. + * is required. You can set the max number of bytes to cache. */ class Cache { private: - int max_frames; ///< This is the max number of "previous" frames to cache - map frames; ///< This map holds the frame number and Frame objects + int64 total_bytes; ///< This is the current total bytes (that are in this cache) + int64 max_bytes; ///< This is the max number of bytes to cache (0 = no limit) + map frames; ///< This map holds the frame number and Frame objects deque frame_numbers; ///< This queue holds a sequential list of cached Frame numbers - int current_frame; ///< This is the last requested frame (used to dynamically adjust the max_frames) - /// Clean up cached frames that exceed the max number of previous frames + /// Clean up cached frames that exceed the max number of bytes void CleanUp(); public: - /// Default constructor, no max frames + /// Default constructor, no max bytes Cache(); - /// Constructor that sets the max previous frames to cache - Cache(int max_frames); + /// Constructor that sets the max bytes to cache + Cache(int64 max_bytes); /// Add a Frame to the cache void Add(int frame_number, Frame* frame); @@ -72,17 +70,11 @@ namespace openshot { /// Count the frames in the queue int Count(); - /// Set current frame number (used to determine which previous frames to delete) - void SetCurrentFrame(int frame_number) { current_frame = frame_number; CleanUp(); }; + /// Set maximum bytes to a different amount + void SetMaxBytes(int64 number_of_bytes) { max_bytes = number_of_bytes; CleanUp(); }; - /// Get the current frame number (used to determine which previous frames to delete) - int GetCurrentFrame() { return current_frame; }; - - /// Set maximum frames to a different amount - void SetMaxFrames(int number_of_frames) { max_frames = number_of_frames; }; - - /// Gets the maximum frames value - int GetMaxFrames() { return max_frames; }; + /// Gets the maximum bytes value + int64 GetMaxBytes() { return max_bytes; }; }; diff --git a/include/Frame.h b/include/Frame.h index a8eabe5a..9a257252 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -114,6 +114,9 @@ namespace openshot /// Get the audio sample rate int GetAudioSamplesRate(); + /// Get the size in bytes of this frame (rough estimate) + int64 GetBytes(); + /// Get pointer to Magick++ image object Magick::Image* GetImage(); diff --git a/src/Cache.cpp b/src/Cache.cpp index da9aafca..3edbff85 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -10,10 +10,10 @@ using namespace std; using namespace openshot; // Default constructor, no max frames -Cache::Cache() : max_frames(-1), current_frame(0) { }; +Cache::Cache() : max_bytes(0), total_bytes(0) { }; // Constructor that sets the max frames to cache -Cache::Cache(int max_frames) : max_frames(max_frames), current_frame(0) { }; +Cache::Cache(int64 max_bytes) : max_bytes(max_bytes), total_bytes(0) { }; // Add a Frame to the cache void Cache::Add(int frame_number, Frame *frame) @@ -24,6 +24,12 @@ void Cache::Add(int frame_number, Frame *frame) // Add frame to queue and map frames[frame_number] = frame; frame_numbers.push_front(frame_number); + + // Increment total bytes (of cache) + total_bytes += frame->GetBytes(); + + // Clean up old frames + CleanUp(); } } @@ -73,6 +79,9 @@ void Cache::Remove(int frame_number, bool delete_data) // Get the frame (or throw exception) Frame *f = GetFrame(frame_number); + // Decrement the total bytes (for this cache) + total_bytes -= f->GetBytes(); + // Loop through frame numbers deque::iterator itr; for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr) @@ -115,6 +124,9 @@ void Cache::Clear() // pop each of the frames from the queue... which empties the queue while(!frame_numbers.empty()) frame_numbers.pop_back(); + + // Reset total bytes (of cache) + total_bytes = 0; } // Count the frames in the queue @@ -124,14 +136,14 @@ int Cache::Count() return frames.size(); } -// Clean up cached frames that exceed the number in our max_frames variable +// Clean up cached frames that exceed the number in our max_bytes variable void Cache::CleanUp() { // Do we auto clean up? - if (max_frames > 0) + if (max_bytes > 0) { - // check against max size - if (frame_numbers.size() > max_frames) + // check against max bytes + while (total_bytes > max_bytes) { // Remove the oldest frame int frame_to_remove = frame_numbers.back(); @@ -174,6 +186,9 @@ void Cache::DisplayAndClear() // increment counter i++; } + + // Reset total bytes + total_bytes = 0; } diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 08083040..0c8fec83 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -4,7 +4,7 @@ using namespace openshot; FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, InvalidCodec) : 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), + audio_pts_offset(99999), video_pts_offset(99999), working_cache(0), 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) { @@ -453,10 +453,6 @@ Frame* FFmpegReader::ReadStream(int requested_frame) // Return requested frame (if found) if (final_cache.Exists(requested_frame)) { - // Set cache's current frame - working_cache.SetCurrentFrame(requested_frame); - final_cache.SetCurrentFrame(requested_frame); - // Return prepared frame return final_cache.GetFrame(requested_frame); } @@ -887,10 +883,6 @@ void FFmpegReader::Seek(int requested_frame) throw(TooManySeeks) // 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); diff --git a/src/Frame.cpp b/src/Frame.cpp index fdc59fd1..7a09fa48 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -358,6 +358,19 @@ int Frame::GetAudioSamplesRate() return sample_rate; } +// Get the size in bytes of this frame (rough estimate) +int64 Frame::GetBytes() +{ + int64 total_bytes = 0; + if (image) + total_bytes += image->fileSize(); + if (audio) + total_bytes += (audio->getNumSamples() * audio->getNumChannels() * sizeof(float)); + + // return size of this frame + return total_bytes; +} + // Get pixel data (as packets) const Magick::PixelPacket* Frame::GetPixels() { diff --git a/src/Main.cpp b/src/Main.cpp index 0917bbb0..e58f1f85 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -14,16 +14,14 @@ void FrameReady(int number) int main() { - //openshot::FFmpegReader r("/home/jonathan/Videos/sintel_trailer-720p.mp4"); - // Create timeline Timeline t(640, 360, Framerate(24,1)); // Add some clips Clip c1("/home/jonathan/Videos/sintel_trailer-720p.mp4"); c1.Position(0.0); - c1.time.AddPoint(1, 1240); - c1.time.AddPoint(1240, 1, LINEAR); + c1.time.AddPoint(1, 400); + c1.time.AddPoint(200, 200, LINEAR); // Add clips t.AddClip(&c1); @@ -46,7 +44,7 @@ int main() // Output stream info w.OutputStreamInfo(); - for (int frame = 1; frame <= 100; frame++) + for (int frame = 1; frame <= 200; frame++) { Frame *f = t.GetFrame(frame); diff --git a/tests/Cache_Tests.cpp b/tests/Cache_Tests.cpp index 73914885..4fe12193 100644 --- a/tests/Cache_Tests.cpp +++ b/tests/Cache_Tests.cpp @@ -18,49 +18,37 @@ TEST(Cache_Default_Constructor) } CHECK_EQUAL(50, c.Count()); // Cache should have all frames, with no limit - CHECK_EQUAL(-1, c.GetMaxFrames()); // Max frames should default to -1 + CHECK_EQUAL(0, c.GetMaxBytes()); // Max frames should default to 0 } -TEST(Cache_Max_Frames_Constructor) +TEST(Cache_Max_Bytes_Constructor) { // Create cache object (with a max of 5 previous items) - Cache c(5); + Cache c(250 * 1024); // Loop 20 times for (int i = 20; i > 0; i--) { // Add blank frame to the cache - Frame *f = new Frame(); + Frame *f = new Frame(i, 320, 240, "#000000"); c.Add(i, f); } - // Cache should have all 20 (since current frame is 0) + // Cache should have all 20 CHECK_EQUAL(20, c.Count()); - // Set current frame to 15 - c.SetCurrentFrame(15); - - // Since current frame is 15, only 5 previous items are allowed (i.e. frame 10 to 20) - CHECK_EQUAL(11, c.Count()); - // Add 10 frames again - for (int i = 9; i > 0; i--) + for (int i = 30; i > 20; i--) { // Add blank frame to the cache - Frame *f = new Frame(); + Frame *f = new Frame(i, 320, 240, "#000000"); c.Add(i, f); } - // Count should be 20, since adding frames does not clean up old ones + // Count should be 20, since we're more frames than can be cached. CHECK_EQUAL(20, c.Count()); - // Set current frame to 15, which should clean up many older frames - c.SetCurrentFrame(15); - - // Count should be 11, since we've cleaned up again - CHECK_EQUAL(11, c.Count()); - - // Check which 10 items the cache kept + // Check which items the cache kept CHECK_EQUAL(false, c.Exists(1)); CHECK_EQUAL(false, c.Exists(5)); CHECK_EQUAL(false, c.Exists(9)); @@ -69,39 +57,12 @@ TEST(Cache_Max_Frames_Constructor) CHECK_EQUAL(true, c.Exists(15)); CHECK_EQUAL(true, c.Exists(19)); CHECK_EQUAL(true, c.Exists(20)); - - // Since we're not adding any new frames, the count should decrease by 1 - c.SetCurrentFrame(16); - CHECK_EQUAL(10, c.Count()); - - // Since we're not adding any new frames, the count should decrease by 1 - c.SetCurrentFrame(17); - CHECK_EQUAL(9, c.Count()); - - // Since we're not adding any new frames, the count should decrease by 1 - c.SetCurrentFrame(18); - CHECK_EQUAL(8, c.Count()); - - // Increase frames to 50 - for (int i = 1; i <= 50; i++) - { - // Add blank frame to the cache - Frame *f = new Frame(); - c.Add(i, f); - } - - // Set current frame, which cleans up old frames - c.SetCurrentFrame(18); - - // Count should be 38 (5 previous to 18 + all frames after 18) - CHECK_EQUAL(38, c.Count()); - } TEST(Cache_Clear) { - // Create cache object (with a max of 10 previous items) - Cache c(10); + // Create cache object + Cache c(250 * 1024); // Loop 10 times for (int i = 0; i < 10; i++) @@ -123,8 +84,8 @@ TEST(Cache_Clear) TEST(Cache_Add_Duplicate_Frames) { - // Create cache object (with a max of 10 items) - Cache c(10); + // Create cache object + Cache c(250 * 1024); // Loop 10 times for (int i = 0; i < 10; i++) @@ -140,8 +101,8 @@ TEST(Cache_Add_Duplicate_Frames) TEST(Cache_Check_If_Frame_Exists) { - // Create cache object (with a max of 5 items) - Cache c(5); + // Create cache object + Cache c(250 * 1024); // Loop 5 times for (int i = 1; i < 6; i++) @@ -163,8 +124,8 @@ TEST(Cache_Check_If_Frame_Exists) TEST(Cache_GetFrame) { - // Create cache object (with a max of 10 items) - Cache c(10); + // Create cache object + Cache c(250 * 1024); // Create 3 frames Frame red(1, 300, 300, "red"); @@ -189,7 +150,7 @@ TEST(Cache_GetFrame) TEST(Cache_GetSmallest) { // Create cache object (with a max of 10 items) - Cache c(10); + Cache c(250 * 1024); // Create 3 frames Frame blue(2, 400, 400, "blue"); @@ -217,7 +178,7 @@ TEST(Cache_GetSmallest) TEST(Cache_Remove) { // Create cache object (with a max of 10 items) - Cache c(10); + Cache c(250 * 1024); // Create 3 frames Frame red(1, 300, 300, "red"); @@ -254,7 +215,7 @@ TEST(Cache_Remove) CHECK_EQUAL(1, c.Count()); } -TEST(Cache_Current_Frame) +TEST(Cache_Set_Max_Bytes) { // Create cache object Cache c; @@ -267,40 +228,14 @@ TEST(Cache_Current_Frame) c.Add(i, f); } - CHECK_EQUAL(0, c.GetCurrentFrame()); // Cache defaults current frame is 0 - - // Set current frame - c.SetCurrentFrame(10); - - CHECK_EQUAL(10, c.GetCurrentFrame()); // Cache should now be on frame 10 - - // Set current frame - c.SetCurrentFrame(20); - - CHECK_EQUAL(20, c.GetCurrentFrame()); // Cache should now be on frame 20 -} - -TEST(Cache_Set_Max_Frames) -{ - // Create cache object - Cache c; - - // Loop 20 times - for (int i = 0; i < 20; i++) - { - // Add blank frame to the cache - Frame *f = new Frame(); - c.Add(i, f); - } - - CHECK_EQUAL(-1, c.GetMaxFrames()); // Cache defaults max frames to -1, unlimited frames + CHECK_EQUAL(0, c.GetMaxBytes()); // Cache defaults max frames to -1, unlimited frames // Set max frames - c.SetMaxFrames(10); - CHECK_EQUAL(10, c.GetMaxFrames()); + c.SetMaxBytes(8 * 1024); + CHECK_EQUAL(8 * 1024, c.GetMaxBytes()); // Set max frames - c.SetMaxFrames(30); - CHECK_EQUAL(30, c.GetMaxFrames()); + c.SetMaxBytes(4 * 1024); + CHECK_EQUAL(4 * 1024, c.GetMaxBytes()); }