OpenShot Library | libopenshot  0.2.4
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 "../include/CacheMemory.h"
32 
33 using namespace std;
34 using namespace openshot;
35 
36 // Default constructor, no max bytes
37 CacheMemory::CacheMemory() : CacheBase(0) {
38  // Set cache type name
39  cache_type = "CacheMemory";
40  range_version = 0;
41  needs_range_processing = false;
42 }
43 
44 // Constructor that sets the max bytes to cache
46  // Set cache type name
47  cache_type = "CacheMemory";
48  range_version = 0;
49  needs_range_processing = false;
50 }
51 
52 // Default destructor
54 {
55  frames.clear();
56  frame_numbers.clear();
57  ordered_frame_numbers.clear();
58 
59  // remove critical section
60  delete cacheCriticalSection;
61  cacheCriticalSection = NULL;
62 }
63 
64 
65 // Calculate ranges of frames
66 void CacheMemory::CalculateRanges() {
67  // Only calculate when something has changed
68  if (needs_range_processing) {
69 
70  // Create a scoped lock, to protect the cache from multiple threads
71  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
72 
73  // Sort ordered frame #s, and calculate JSON ranges
74  std::sort(ordered_frame_numbers.begin(), ordered_frame_numbers.end());
75 
76  // Clear existing JSON variable
77  Json::Value ranges = Json::Value(Json::arrayValue);
78 
79  // Increment range version
80  range_version++;
81 
82  std::vector<int64_t>::iterator itr_ordered;
83  int64_t starting_frame = *ordered_frame_numbers.begin();
84  int64_t ending_frame = *ordered_frame_numbers.begin();
85 
86  // Loop through all known frames (in sequential order)
87  for (itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered) {
88  int64_t frame_number = *itr_ordered;
89  if (frame_number - ending_frame > 1) {
90  // End of range detected
91  Json::Value range;
92 
93  // Add JSON object with start/end attributes
94  // Use strings, since int64_ts are supported in JSON
95  std::stringstream start_str;
96  start_str << starting_frame;
97  std::stringstream end_str;
98  end_str << ending_frame;
99  range["start"] = start_str.str();
100  range["end"] = end_str.str();
101  ranges.append(range);
102 
103  // Set new starting range
104  starting_frame = frame_number;
105  }
106 
107  // Set current frame as end of range, and keep looping
108  ending_frame = frame_number;
109  }
110 
111  // APPEND FINAL VALUE
112  Json::Value range;
113 
114  // Add JSON object with start/end attributes
115  // Use strings, since int64_ts are not supported in JSON
116  std::stringstream start_str;
117  start_str << starting_frame;
118  std::stringstream end_str;
119  end_str << ending_frame;
120  range["start"] = start_str.str();
121  range["end"] = end_str.str();
122  ranges.append(range);
123 
124  // Cache range JSON as string
125  json_ranges = ranges.toStyledString();
126 
127  // Reset needs_range_processing
128  needs_range_processing = false;
129  }
130 }
131 
132 // Add a Frame to the cache
133 void CacheMemory::Add(std::shared_ptr<Frame> frame)
134 {
135  // Create a scoped lock, to protect the cache from multiple threads
136  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
137  int64_t frame_number = frame->number;
138 
139  // Freshen frame if it already exists
140  if (frames.count(frame_number))
141  // Move frame to front of queue
142  MoveToFront(frame_number);
143 
144  else
145  {
146  // Add frame to queue and map
147  frames[frame_number] = frame;
148  frame_numbers.push_front(frame_number);
149  ordered_frame_numbers.push_back(frame_number);
150  needs_range_processing = true;
151 
152  // Clean up old frames
153  CleanUp();
154  }
155 }
156 
157 // Get a frame from the cache (or NULL shared_ptr if no frame is found)
158 std::shared_ptr<Frame> CacheMemory::GetFrame(int64_t frame_number)
159 {
160  // Create a scoped lock, to protect the cache from multiple threads
161  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
162 
163  // Does frame exists in cache?
164  if (frames.count(frame_number))
165  // return the Frame object
166  return frames[frame_number];
167 
168  else
169  // no Frame found
170  return std::shared_ptr<Frame>();
171 }
172 
173 // Get the smallest frame number (or NULL shared_ptr if no frame is found)
174 std::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
175 {
176  // Create a scoped lock, to protect the cache from multiple threads
177  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
178  std::shared_ptr<openshot::Frame> f;
179 
180  // Loop through frame numbers
181  std::deque<int64_t>::iterator itr;
182  int64_t smallest_frame = -1;
183  for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
184  {
185  if (*itr < smallest_frame || smallest_frame == -1)
186  smallest_frame = *itr;
187  }
188 
189  // Return frame
190  f = GetFrame(smallest_frame);
191 
192  return f;
193 }
194 
195 // Gets the maximum bytes value
197 {
198  // Create a scoped lock, to protect the cache from multiple threads
199  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
200 
201  int64_t total_bytes = 0;
202 
203  // Loop through frames, and calculate total bytes
204  std::deque<int64_t>::reverse_iterator itr;
205  for(itr = frame_numbers.rbegin(); itr != frame_numbers.rend(); ++itr)
206  {
207  total_bytes += frames[*itr]->GetBytes();
208  }
209 
210  return total_bytes;
211 }
212 
213 // Remove a specific frame
214 void CacheMemory::Remove(int64_t frame_number)
215 {
216  Remove(frame_number, frame_number);
217 }
218 
219 // Remove range of frames
220 void CacheMemory::Remove(int64_t start_frame_number, int64_t end_frame_number)
221 {
222  // Create a scoped lock, to protect the cache from multiple threads
223  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
224 
225  // Loop through frame numbers
226  std::deque<int64_t>::iterator itr;
227  for(itr = frame_numbers.begin(); itr != frame_numbers.end();)
228  {
229  if (*itr >= start_frame_number && *itr <= end_frame_number)
230  {
231  // erase frame number
232  itr = frame_numbers.erase(itr);
233  }else
234  itr++;
235  }
236 
237  // Loop through ordered frame numbers
238  std::vector<int64_t>::iterator itr_ordered;
239  for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end();)
240  {
241  if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
242  {
243  // erase frame number
244  frames.erase(*itr_ordered);
245  itr_ordered = ordered_frame_numbers.erase(itr_ordered);
246  }else
247  itr_ordered++;
248  }
249 
250  // Needs range processing (since cache has changed)
251  needs_range_processing = true;
252 }
253 
254 // Move frame to front of queue (so it lasts longer)
255 void CacheMemory::MoveToFront(int64_t frame_number)
256 {
257  // Create a scoped lock, to protect the cache from multiple threads
258  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
259 
260  // Does frame exists in cache?
261  if (frames.count(frame_number))
262  {
263  // Loop through frame numbers
264  std::deque<int64_t>::iterator itr;
265  for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
266  {
267  if (*itr == frame_number)
268  {
269  // erase frame number
270  frame_numbers.erase(itr);
271 
272  // add frame number to 'front' of queue
273  frame_numbers.push_front(frame_number);
274  break;
275  }
276  }
277  }
278 }
279 
280 // Clear the cache of all frames
282 {
283  // Create a scoped lock, to protect the cache from multiple threads
284  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
285 
286  frames.clear();
287  frame_numbers.clear();
288  ordered_frame_numbers.clear();
289  needs_range_processing = true;
290 }
291 
292 // Count the frames in the queue
294 {
295  // Create a scoped lock, to protect the cache from multiple threads
296  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
297 
298  // Return the number of frames in the cache
299  return frames.size();
300 }
301 
302 // Clean up cached frames that exceed the number in our max_bytes variable
303 void CacheMemory::CleanUp()
304 {
305  // Do we auto clean up?
306  if (max_bytes > 0)
307  {
308  // Create a scoped lock, to protect the cache from multiple threads
309  const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
310 
311  while (GetBytes() > max_bytes && frame_numbers.size() > 20)
312  {
313  // Get the oldest frame number.
314  int64_t frame_to_remove = frame_numbers.back();
315 
316  // Remove frame_number and frame
317  Remove(frame_to_remove);
318  }
319  }
320 }
321 
322 
323 // Generate JSON string of this object
324 std::string CacheMemory::Json() {
325 
326  // Return formatted string
327  return JsonValue().toStyledString();
328 }
329 
330 // Generate Json::JsonValue for this object
331 Json::Value CacheMemory::JsonValue() {
332 
333  // Process range data (if anything has changed)
334  CalculateRanges();
335 
336  // Create root json object
337  Json::Value root = CacheBase::JsonValue(); // get parent properties
338  root["type"] = cache_type;
339 
340  std::stringstream range_version_str;
341  range_version_str << range_version;
342  root["version"] = range_version_str.str();
343 
344  // Parse and append range data (if any)
345  Json::Value ranges;
346  Json::CharReaderBuilder rbuilder;
347  Json::CharReader* reader(rbuilder.newCharReader());
348 
349  std::string errors;
350  bool success = reader->parse( json_ranges.c_str(),
351  json_ranges.c_str() + json_ranges.size(), &ranges, &errors );
352  delete reader;
353 
354  if (success)
355  root["ranges"] = ranges;
356 
357  // return JsonValue
358  return root;
359 }
360 
361 // Load JSON string into this object
362 void CacheMemory::SetJson(std::string value) {
363 
364  // Parse JSON string into JSON objects
365  Json::Value root;
366  Json::CharReaderBuilder rbuilder;
367  Json::CharReader* reader(rbuilder.newCharReader());
368 
369  std::string errors;
370  bool success = reader->parse( value.c_str(),
371  value.c_str() + value.size(), &root, &errors );
372  delete reader;
373  if (!success)
374  // Raise exception
375  throw InvalidJSON("JSON could not be parsed (or is invalid)");
376 
377  try
378  {
379  // Set all values that match
380  SetJsonValue(root);
381  }
382  catch (const std::exception& e)
383  {
384  // Error parsing JSON (or missing keys)
385  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
386  }
387 }
388 
389 // Load Json::JsonValue into this object
390 void CacheMemory::SetJsonValue(Json::Value root) {
391 
392  // Close timeline before we do anything (this also removes all open and closing clips)
393  Clear();
394 
395  // Set parent data
397 
398  if (!root["type"].isNull())
399  cache_type = root["type"].asString();
400 }
juce::CriticalSection * cacheCriticalSection
Section lock for multiple threads.
Definition: CacheBase.h:56
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Json::Value JsonValue()
Generate Json::JsonValue for this object.
std::string Json()
Get and Set JSON methods.
STL namespace.
CacheMemory()
Default constructor, no max bytes.
Definition: CacheMemory.cpp:37
void SetJson(std::string value)
Load JSON string into this object.
std::shared_ptr< openshot::Frame > GetSmallestFrame()
Get the smallest frame number.
All cache managers in libopenshot are based on this CacheBase class.
Definition: CacheBase.h:49
void MoveToFront(int64_t frame_number)
Move frame to front of queue (so it lasts longer)
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
virtual Json::Value JsonValue()=0
Generate Json::JsonValue for this object.
Definition: CacheBase.cpp:57
void Clear()
Clear the cache of all frames.
int64_t Count()
Count the frames in the queue.
int64_t GetBytes()
Gets the maximum bytes value.
std::string cache_type
This is a friendly type name of the derived cache instance.
Definition: CacheBase.h:52
This namespace is the default namespace for all code in the openshot library.
Exception for invalid JSON.
Definition: Exceptions.h:205
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
void Remove(int64_t frame_number)
Remove a specific frame.
int64_t max_bytes
This is the max number of bytes to cache (0 = no limit)
Definition: CacheBase.h:53
virtual void SetJsonValue(Json::Value root)=0
Load Json::JsonValue into this object.
Definition: CacheBase.cpp:70