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 
34 using namespace std;
35 using namespace openshot;
36 
37 // Default constructor, no max bytes
38 CacheMemory::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
46 CacheMemory::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
61  delete cacheCriticalSection;
62  cacheCriticalSection = NULL;
63 }
64 
65 
66 // Calculate ranges of frames
67 void 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
126 void 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)
151 std::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)
167 std::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
207 void CacheMemory::Remove(int64_t frame_number)
208 {
209  Remove(frame_number, frame_number);
210 }
211 
212 // Remove range of frames
213 void 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)
248 void 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
296 void 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
317 std::string CacheMemory::Json() {
318 
319  // Return formatted string
320  return JsonValue().toStyledString();
321 }
322 
323 // Generate Json::Value for this object
324 Json::Value CacheMemory::JsonValue() {
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
346 void 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
363 void 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