OpenShot Library | libopenshot 0.2.7
CacheMemory.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Source file for Cache class
4 * @author Jonathan Thomas <jonathan@openshot.org>
5 *
6 * @ref License
7 */
8
9/* LICENSE
10 *
11 * Copyright (c) 2008-2019 OpenShot Studios, LLC
12 * <http://www.openshotstudios.com/>. This file is part of
13 * OpenShot Library (libopenshot), an open-source project dedicated to
14 * delivering high quality video editing and animation solutions to the
15 * world. For more information visit <http://www.openshot.org/>.
16 *
17 * OpenShot Library (libopenshot) is free software: you can redistribute it
18 * and/or modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation, either version 3 of the
20 * License, or (at your option) any later version.
21 *
22 * OpenShot Library (libopenshot) is distributed in the hope that it will be
23 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29 */
30
31#include "CacheMemory.h"
32#include "Exceptions.h"
33
34using namespace std;
35using namespace openshot;
36
37// Default constructor, no max bytes
38CacheMemory::CacheMemory() : CacheBase(0) {
39 // Set cache type name
40 cache_type = "CacheMemory";
41 range_version = 0;
42 needs_range_processing = false;
43}
44
45// Constructor that sets the max bytes to cache
46CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes) {
47 // Set cache type name
48 cache_type = "CacheMemory";
49 range_version = 0;
50 needs_range_processing = false;
51}
52
53// Default destructor
55{
56 frames.clear();
57 frame_numbers.clear();
58 ordered_frame_numbers.clear();
59
60 // remove critical section
63}
64
65
66// Calculate ranges of frames
67void CacheMemory::CalculateRanges() {
68 // Only calculate when something has changed
69 if (needs_range_processing) {
70
71 // Create a scoped lock, to protect the cache from multiple threads
72 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
73
74 // Sort ordered frame #s, and calculate JSON ranges
75 std::sort(ordered_frame_numbers.begin(), ordered_frame_numbers.end());
76
77 // Clear existing JSON variable
78 Json::Value ranges = Json::Value(Json::arrayValue);
79
80 // Increment range version
81 range_version++;
82
83 std::vector<int64_t>::iterator itr_ordered;
84 int64_t starting_frame = *ordered_frame_numbers.begin();
85 int64_t ending_frame = *ordered_frame_numbers.begin();
86
87 // Loop through all known frames (in sequential order)
88 for (itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered) {
89 int64_t frame_number = *itr_ordered;
90 if (frame_number - ending_frame > 1) {
91 // End of range detected
92 Json::Value range;
93
94 // Add JSON object with start/end attributes
95 // Use strings, since int64_ts are supported in JSON
96 range["start"] = std::to_string(starting_frame);
97 range["end"] = std::to_string(ending_frame);
98 ranges.append(range);
99
100 // Set new starting range
101 starting_frame = frame_number;
102 }
103
104 // Set current frame as end of range, and keep looping
105 ending_frame = frame_number;
106 }
107
108 // APPEND FINAL VALUE
109 Json::Value range;
110
111 // Add JSON object with start/end attributes
112 // Use strings, since int64_ts are not supported in JSON
113 range["start"] = std::to_string(starting_frame);
114 range["end"] = std::to_string(ending_frame);
115 ranges.append(range);
116
117 // Cache range JSON as string
118 json_ranges = ranges.toStyledString();
119
120 // Reset needs_range_processing
121 needs_range_processing = false;
122 }
123}
124
125// Add a Frame to the cache
126void CacheMemory::Add(std::shared_ptr<Frame> frame)
127{
128 // Create a scoped lock, to protect the cache from multiple threads
129 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
130 int64_t frame_number = frame->number;
131
132 // Freshen frame if it already exists
133 if (frames.count(frame_number))
134 // Move frame to front of queue
135 MoveToFront(frame_number);
136
137 else
138 {
139 // Add frame to queue and map
140 frames[frame_number] = frame;
141 frame_numbers.push_front(frame_number);
142 ordered_frame_numbers.push_back(frame_number);
143 needs_range_processing = true;
144
145 // Clean up old frames
146 CleanUp();
147 }
148}
149
150// Get a frame from the cache (or NULL shared_ptr if no frame is found)
151std::shared_ptr<Frame> CacheMemory::GetFrame(int64_t frame_number)
152{
153 // Create a scoped lock, to protect the cache from multiple threads
154 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
155
156 // Does frame exists in cache?
157 if (frames.count(frame_number))
158 // return the Frame object
159 return frames[frame_number];
160
161 else
162 // no Frame found
163 return std::shared_ptr<Frame>();
164}
165
166// Get the smallest frame number (or NULL shared_ptr if no frame is found)
167std::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
168{
169 // Create a scoped lock, to protect the cache from multiple threads
170 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
171 std::shared_ptr<openshot::Frame> f;
172
173 // Loop through frame numbers
174 std::deque<int64_t>::iterator itr;
175 int64_t smallest_frame = -1;
176 for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
177 {
178 if (*itr < smallest_frame || smallest_frame == -1)
179 smallest_frame = *itr;
180 }
181
182 // Return frame
183 f = GetFrame(smallest_frame);
184
185 return f;
186}
187
188// Gets the maximum bytes value
190{
191 // Create a scoped lock, to protect the cache from multiple threads
192 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
193
194 int64_t total_bytes = 0;
195
196 // Loop through frames, and calculate total bytes
197 std::deque<int64_t>::reverse_iterator itr;
198 for(itr = frame_numbers.rbegin(); itr != frame_numbers.rend(); ++itr)
199 {
200 total_bytes += frames[*itr]->GetBytes();
201 }
202
203 return total_bytes;
204}
205
206// Remove a specific frame
207void CacheMemory::Remove(int64_t frame_number)
208{
209 Remove(frame_number, frame_number);
210}
211
212// Remove range of frames
213void CacheMemory::Remove(int64_t start_frame_number, int64_t end_frame_number)
214{
215 // Create a scoped lock, to protect the cache from multiple threads
216 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
217
218 // Loop through frame numbers
219 std::deque<int64_t>::iterator itr;
220 for(itr = frame_numbers.begin(); itr != frame_numbers.end();)
221 {
222 if (*itr >= start_frame_number && *itr <= end_frame_number)
223 {
224 // erase frame number
225 itr = frame_numbers.erase(itr);
226 }else
227 itr++;
228 }
229
230 // Loop through ordered frame numbers
231 std::vector<int64_t>::iterator itr_ordered;
232 for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end();)
233 {
234 if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
235 {
236 // erase frame number
237 frames.erase(*itr_ordered);
238 itr_ordered = ordered_frame_numbers.erase(itr_ordered);
239 }else
240 itr_ordered++;
241 }
242
243 // Needs range processing (since cache has changed)
244 needs_range_processing = true;
245}
246
247// Move frame to front of queue (so it lasts longer)
248void CacheMemory::MoveToFront(int64_t frame_number)
249{
250 // Create a scoped lock, to protect the cache from multiple threads
251 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
252
253 // Does frame exists in cache?
254 if (frames.count(frame_number))
255 {
256 // Loop through frame numbers
257 std::deque<int64_t>::iterator itr;
258 for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
259 {
260 if (*itr == frame_number)
261 {
262 // erase frame number
263 frame_numbers.erase(itr);
264
265 // add frame number to 'front' of queue
266 frame_numbers.push_front(frame_number);
267 break;
268 }
269 }
270 }
271}
272
273// Clear the cache of all frames
275{
276 // Create a scoped lock, to protect the cache from multiple threads
277 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
278
279 frames.clear();
280 frame_numbers.clear();
281 ordered_frame_numbers.clear();
282 needs_range_processing = true;
283}
284
285// Count the frames in the queue
287{
288 // Create a scoped lock, to protect the cache from multiple threads
289 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
290
291 // Return the number of frames in the cache
292 return frames.size();
293}
294
295// Clean up cached frames that exceed the number in our max_bytes variable
296void CacheMemory::CleanUp()
297{
298 // Do we auto clean up?
299 if (max_bytes > 0)
300 {
301 // Create a scoped lock, to protect the cache from multiple threads
302 const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
303
304 while (GetBytes() > max_bytes && frame_numbers.size() > 20)
305 {
306 // Get the oldest frame number.
307 int64_t frame_to_remove = frame_numbers.back();
308
309 // Remove frame_number and frame
310 Remove(frame_to_remove);
311 }
312 }
313}
314
315
316// Generate JSON string of this object
317std::string CacheMemory::Json() {
318
319 // Return formatted string
320 return JsonValue().toStyledString();
321}
322
323// Generate Json::Value for this object
325
326 // Process range data (if anything has changed)
327 CalculateRanges();
328
329 // Create root json object
330 Json::Value root = CacheBase::JsonValue(); // get parent properties
331 root["type"] = cache_type;
332
333 root["version"] = std::to_string(range_version);
334
335 // Parse and append range data (if any)
336 try {
337 const Json::Value ranges = openshot::stringToJson(json_ranges);
338 root["ranges"] = ranges;
339 } catch (...) { }
340
341 // return JsonValue
342 return root;
343}
344
345// Load JSON string into this object
346void CacheMemory::SetJson(const std::string value) {
347
348 try
349 {
350 // Parse string to Json::Value
351 const Json::Value root = openshot::stringToJson(value);
352 // Set all values that match
353 SetJsonValue(root);
354 }
355 catch (const std::exception& e)
356 {
357 // Error parsing JSON (or missing keys)
358 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
359 }
360}
361
362// Load Json::Value into this object
363void CacheMemory::SetJsonValue(const Json::Value root) {
364
365 // Close timeline before we do anything (this also removes all open and closing clips)
366 Clear();
367
368 // Set parent data
370
371 if (!root["type"].isNull())
372 cache_type = root["type"].asString();
373}
Header file for CacheMemory class.
Header file for all Exception classes.
All cache managers in libopenshot are based on this CacheBase class.
Definition: CacheBase.h:49
virtual Json::Value JsonValue()=0
Generate Json::Value for this object.
Definition: CacheBase.cpp:57
std::string cache_type
This is a friendly type name of the derived cache instance.
Definition: CacheBase.h:51
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: CacheBase.cpp:70
juce::CriticalSection * cacheCriticalSection
Section lock for multiple threads.
Definition: CacheBase.h:55
int64_t max_bytes
This is the max number of bytes to cache (0 = no limit)
Definition: CacheBase.h:52
CacheMemory()
Default constructor, no max bytes.
Definition: CacheMemory.cpp:38
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.
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
void MoveToFront(int64_t frame_number)
Move frame to front of queue (so it lasts longer)
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.
std::shared_ptr< openshot::Frame > GetSmallestFrame()
Get the smallest frame number.
Exception for invalid JSON.
Definition: Exceptions.h:206
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:34