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