OpenShot Library | libopenshot 0.5.0
Loading...
Searching...
No Matches
CacheMemory.cpp
Go to the documentation of this file.
1
8
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "CacheMemory.h"
14#include "Exceptions.h"
15#include "Frame.h"
16#include "MemoryTrim.h"
17
18using namespace std;
19using namespace openshot;
20
21// Default constructor, no max bytes
22CacheMemory::CacheMemory() : CacheBase(0), bytes_freed_since_trim(0) {
23 // Set cache type name
24 cache_type = "CacheMemory";
25 range_version = 0;
27}
28
29// Constructor that sets the max bytes to cache
30CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes), bytes_freed_since_trim(0) {
31 // Set cache type name
32 cache_type = "CacheMemory";
33 range_version = 0;
35}
36
37// Default destructor
39{
40 Clear();
41
42 // remove mutex
43 delete cacheMutex;
44}
45
46// Add a Frame to the cache
47void CacheMemory::Add(std::shared_ptr<Frame> frame)
48{
49 // Create a scoped lock, to protect the cache from multiple threads
50 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
51 int64_t frame_number = frame->number;
52
53 // Freshen frame if it already exists
54 if (frames.count(frame_number))
55 // Move frame to front of queue
56 Touch(frame_number);
57
58 else
59 {
60 // Add frame to queue and map
61 frames[frame_number] = frame;
62 frame_numbers.push_front(frame_number);
63 ordered_frame_numbers.push_back(frame_number);
65
66 // Clean up old frames
67 CleanUp();
68 }
69}
70
71// Check if frame is already contained in cache
72bool CacheMemory::Contains(int64_t frame_number) {
73 if (frames.count(frame_number) > 0) {
74 return true;
75 } else {
76 return false;
77 }
78}
79
80// Get a frame from the cache (or NULL shared_ptr if no frame is found)
81std::shared_ptr<Frame> CacheMemory::GetFrame(int64_t frame_number)
82{
83 // Create a scoped lock, to protect the cache from multiple threads
84 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
85
86 // Does frame exists in cache?
87 if (frames.count(frame_number))
88 // return the Frame object
89 return frames[frame_number];
90
91 else
92 // no Frame found
93 return std::shared_ptr<Frame>();
94}
95
96// @brief Get an array of all Frames
97std::vector<std::shared_ptr<openshot::Frame>> CacheMemory::GetFrames()
98{
99 // Create a scoped lock, to protect the cache from multiple threads
100 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
101
102 std::vector<std::shared_ptr<openshot::Frame>> all_frames;
103 std::vector<int64_t>::iterator itr_ordered;
104 for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered)
105 {
106 int64_t frame_number = *itr_ordered;
107 all_frames.push_back(GetFrame(frame_number));
108 }
109
110 return all_frames;
111}
112
113// Get the smallest frame number (or NULL shared_ptr if no frame is found)
114std::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
115{
116 // Create a scoped lock, to protect the cache from multiple threads
117 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
118
119 // Loop through frame numbers
120 std::deque<int64_t>::iterator itr;
121 int64_t smallest_frame = -1;
122 for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
123 {
124 if (*itr < smallest_frame || smallest_frame == -1)
125 smallest_frame = *itr;
126 }
127
128 // Return frame (if any)
129 if (smallest_frame != -1) {
130 return frames[smallest_frame];
131 } else {
132 return NULL;
133 }
134}
135
136// Gets the maximum bytes value
138{
139 // Create a scoped lock, to protect the cache from multiple threads
140 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
141
142 int64_t total_bytes = 0;
143
144 // Loop through frames, and calculate total bytes
145 std::deque<int64_t>::reverse_iterator itr;
146 for(itr = frame_numbers.rbegin(); itr != frame_numbers.rend(); ++itr)
147 {
148 total_bytes += frames[*itr]->GetBytes();
149 }
150
151 return total_bytes;
152}
153
154// Remove a specific frame
155void CacheMemory::Remove(int64_t frame_number)
156{
157 Remove(frame_number, frame_number);
158}
159
160// Remove range of frames
161void CacheMemory::Remove(int64_t start_frame_number, int64_t end_frame_number)
162{
163 // Create a scoped lock, to protect the cache from multiple threads
164 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
165 int64_t removed_bytes = 0;
166
167 // Loop through frame numbers
168 std::deque<int64_t>::iterator itr;
169 for(itr = frame_numbers.begin(); itr != frame_numbers.end();)
170 {
171 if (*itr >= start_frame_number && *itr <= end_frame_number)
172 {
173 // erase frame number
174 itr = frame_numbers.erase(itr);
175 }else
176 itr++;
177 }
178
179 // Loop through ordered frame numbers
180 std::vector<int64_t>::iterator itr_ordered;
181 for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end();)
182 {
183 if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
184 {
185 // Count bytes freed before erasing the frame
186 if (frames.count(*itr_ordered))
187 removed_bytes += frames[*itr_ordered]->GetBytes();
188
189 // erase frame number
190 frames.erase(*itr_ordered);
191 itr_ordered = ordered_frame_numbers.erase(itr_ordered);
192 }else
193 itr_ordered++;
194 }
195
196 if (removed_bytes > 0)
197 {
198 bytes_freed_since_trim += removed_bytes;
199 if (bytes_freed_since_trim >= TRIM_THRESHOLD_BYTES)
200 {
201 // Periodically return freed arenas to the OS
202 if (TrimMemoryToOS())
203 bytes_freed_since_trim = 0;
204 }
205 }
206
207 // Needs range processing (since cache has changed)
209}
210
211// Move frame to front of queue (so it lasts longer)
212void CacheMemory::Touch(int64_t frame_number)
213{
214 // Create a scoped lock, to protect the cache from multiple threads
215 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
216
217 // Does frame exists in cache?
218 if (frames.count(frame_number))
219 {
220 // Loop through frame numbers
221 std::deque<int64_t>::iterator itr;
222 for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
223 {
224 if (*itr == frame_number)
225 {
226 // erase frame number
227 frame_numbers.erase(itr);
228
229 // add frame number to 'front' of queue
230 frame_numbers.push_front(frame_number);
231 break;
232 }
233 }
234 }
235}
236
237// Clear the cache of all frames
239{
240 // Create a scoped lock, to protect the cache from multiple threads
241 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
242
243 frames.clear();
244 frame_numbers.clear();
245 frame_numbers.shrink_to_fit();
246 ordered_frame_numbers.clear();
247 ordered_frame_numbers.shrink_to_fit();
249 bytes_freed_since_trim = 0;
250
251 // Trim freed arenas back to OS after large clears
252 TrimMemoryToOS(true);
253}
254
255// Count the frames in the queue
257{
258 // Create a scoped lock, to protect the cache from multiple threads
259 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
260
261 // Return the number of frames in the cache
262 return frames.size();
263}
264
265// Clean up cached frames that exceed the number in our max_bytes variable
266void CacheMemory::CleanUp()
267{
268 // Do we auto clean up?
269 if (max_bytes > 0)
270 {
271 // Create a scoped lock, to protect the cache from multiple threads
272 const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
273
274 while (GetBytes() > max_bytes && frame_numbers.size() > 20)
275 {
276 // Get the oldest frame number.
277 int64_t frame_to_remove = frame_numbers.back();
278
279 // Remove frame_number and frame
280 Remove(frame_to_remove);
281 }
282 }
283}
284
285
286// Generate JSON string of this object
287std::string CacheMemory::Json() {
288
289 // Return formatted string
290 return JsonValue().toStyledString();
291}
292
293// Generate Json::Value for this object
295
296 // Process range data (if anything has changed)
298
299 // Create root json object
300 Json::Value root = CacheBase::JsonValue(); // get parent properties
301 root["type"] = cache_type;
302
303 root["version"] = std::to_string(range_version);
304
305 // Parse and append range data (if any)
306 try {
307 const Json::Value ranges = openshot::stringToJson(json_ranges);
308 root["ranges"] = ranges;
309 } catch (...) { }
310
311 // return JsonValue
312 return root;
313}
314
315// Load JSON string into this object
316void CacheMemory::SetJson(const std::string value) {
317
318 try
319 {
320 // Parse string to Json::Value
321 const Json::Value root = openshot::stringToJson(value);
322 // Set all values that match
323 SetJsonValue(root);
324 }
325 catch (const std::exception& e)
326 {
327 // Error parsing JSON (or missing keys)
328 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
329 }
330}
331
332// Load Json::Value into this object
333void CacheMemory::SetJsonValue(const Json::Value root) {
334
335 // Close timeline before we do anything (this also removes all open and closing clips)
336 Clear();
337
338 // Set parent data
340
341 if (!root["type"].isNull())
342 cache_type = root["type"].asString();
343}
Header file for CacheMemory class.
Header file for all Exception classes.
Header file for Frame class.
Cross-platform helper to encourage returning freed memory to the OS.
int64_t range_version
The version of the JSON range data (incremented with each change).
Definition CacheBase.h:44
CacheBase()
Default constructor, no max bytes.
Definition CacheBase.cpp:19
virtual Json::Value JsonValue()=0
Generate Json::Value for this object.
std::string cache_type
This is a friendly type name of the derived cache instance.
Definition CacheBase.h:37
void CalculateRanges()
Calculate ranges of frames.
Definition CacheBase.cpp:36
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
bool needs_range_processing
Something has changed, and the range data needs to be re-calculated.
Definition CacheBase.h:40
int64_t max_bytes
This is the max number of bytes to cache (0 = no limit).
Definition CacheBase.h:38
std::recursive_mutex * cacheMutex
Mutex for multiple threads.
Definition CacheBase.h:47
std::string json_ranges
JSON ranges of frame numbers.
Definition CacheBase.h:41
std::vector< int64_t > ordered_frame_numbers
Ordered list of frame numbers used by cache.
Definition CacheBase.h:42
CacheMemory()
Default constructor, no max bytes.
int64_t Count()
Count the frames in the queue.
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
void SetJson(const std::string value)
Load JSON string into this object.
int64_t GetBytes()
Gets the maximum bytes value.
std::string Json()
Generate JSON string of this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
std::vector< std::shared_ptr< openshot::Frame > > GetFrames()
Get an array of all Frames.
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
void Remove(int64_t frame_number)
Remove a specific frame.
void Clear()
Clear the cache of all frames.
Json::Value JsonValue()
Generate Json::Value for this object.
void Touch(int64_t frame_number)
Move frame to front of queue (so it lasts longer).
bool Contains(int64_t frame_number)
Check if frame is already contained in cache.
std::shared_ptr< openshot::Frame > GetSmallestFrame()
Get the smallest frame number.
Exception for invalid JSON.
Definition Exceptions.h:218
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
bool TrimMemoryToOS(bool force) noexcept
Attempt to return unused heap memory to the operating system.
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16