You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Major refactor to the cache object (again)! It now uses bytes to determine how many frames to keep.
This commit is contained in:
@@ -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<int, Frame*> 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<int, Frame*> frames; ///< This map holds the frame number and Frame objects
|
||||
deque<int> 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; };
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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<int>::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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user