~mbranton/libopenshot/alpha-channel-fix

« back to all changes in this revision

Viewing changes to src/CacheMemory.cpp

  • Committer: Jonathan Thomas
  • Date: 2016-09-07 05:40:01 UTC
  • Revision ID: jonathan@openshot.org-20160907054001-v2tbe8uy8a7lk4qw
Added new CacheDisk class, which caches frames to the hard drive, dramatically speeding up preview speeds, at the expense of IO operations. New unittests for caching framework. Fixed a few bugs with Frame constructor, which was causing invalid # width & height. Integrated JSON into the cache framework, to quickly share the state of the cache (including ranges of cached frame numbers). Fixed a bug where some Timeline frames could have no audio samples.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
 
33
33
// Default constructor, no max bytes
34
34
CacheMemory::CacheMemory() : CacheBase(0) {
35
 
 
 
35
        // Set cache type name
 
36
        cache_type = "CacheMemory";
 
37
        range_version = 0;
 
38
        needs_range_processing = false;
36
39
};
37
40
 
38
41
// Constructor that sets the max bytes to cache
39
 
CacheMemory::CacheMemory(int64 max_bytes) : CacheBase(max_bytes) {
40
 
 
 
42
CacheMemory::CacheMemory(long long int max_bytes) : CacheBase(max_bytes) {
 
43
        // Set cache type name
 
44
        cache_type = "CacheMemory";
 
45
        range_version = 0;
 
46
        needs_range_processing = false;
41
47
};
42
48
 
43
49
// Default destructor
45
51
{
46
52
        frames.clear();
47
53
        frame_numbers.clear();
 
54
        ordered_frame_numbers.clear();
48
55
 
49
56
        // remove critical section
50
57
        delete cacheCriticalSection;
51
58
        cacheCriticalSection = NULL;
52
59
}
53
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
                ranges.clear();
 
75
                ranges = Json::Value(Json::arrayValue);
 
76
 
 
77
                // Increment range version
 
78
                range_version++;
 
79
 
 
80
                vector<long int>::iterator itr_ordered;
 
81
                long int starting_frame = *ordered_frame_numbers.begin();
 
82
                long int ending_frame = *ordered_frame_numbers.begin();
 
83
 
 
84
                // Loop through all known frames (in sequential order)
 
85
                for (itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered) {
 
86
                        long int frame_number = *itr_ordered;
 
87
                        if (frame_number - ending_frame > 1) {
 
88
                                // End of range detected
 
89
                                Json::Value range;
 
90
 
 
91
                                // Add JSON object with start/end attributes
 
92
                                // Use strings, since long ints are supported in JSON
 
93
                                stringstream start_str;
 
94
                                start_str << starting_frame;
 
95
                                stringstream end_str;
 
96
                                end_str << ending_frame;
 
97
                                range["start"] = start_str.str();
 
98
                                range["end"] = end_str.str();
 
99
                                ranges.append(range);
 
100
 
 
101
                                // Set new starting range
 
102
                                starting_frame = frame_number;
 
103
                        }
 
104
 
 
105
                        // Set current frame as end of range, and keep looping
 
106
                        ending_frame = frame_number;
 
107
                }
 
108
 
 
109
                // APPEND FINAL VALUE
 
110
                Json::Value range;
 
111
 
 
112
                // Add JSON object with start/end attributes
 
113
                // Use strings, since long ints are supported in JSON
 
114
                stringstream start_str;
 
115
                start_str << starting_frame;
 
116
                stringstream end_str;
 
117
                end_str << ending_frame;
 
118
                range["start"] = start_str.str();
 
119
                range["end"] = end_str.str();
 
120
                ranges.append(range);
 
121
 
 
122
                // Reset needs_range_processing
 
123
                needs_range_processing = false;
 
124
        }
 
125
}
 
126
 
54
127
// Add a Frame to the cache
55
128
void CacheMemory::Add(tr1::shared_ptr<Frame> frame)
56
129
{
68
141
                // Add frame to queue and map
69
142
                frames[frame_number] = frame;
70
143
                frame_numbers.push_front(frame_number);
 
144
                ordered_frame_numbers.push_back(frame_number);
 
145
                needs_range_processing = true;
71
146
 
72
147
                // Clean up old frames
73
148
                CleanUp();
113
188
}
114
189
 
115
190
// Gets the maximum bytes value
116
 
int64 CacheMemory::GetBytes()
 
191
long long int CacheMemory::GetBytes()
117
192
{
118
193
        // Create a scoped lock, to protect the cache from multiple threads
119
194
        const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
120
195
 
121
 
        int64 total_bytes = 0;
 
196
        long long int total_bytes = 0;
122
197
 
123
198
        // Loop through frames, and calculate total bytes
124
199
        deque<long int>::reverse_iterator itr;
133
208
// Remove a specific frame
134
209
void CacheMemory::Remove(long int frame_number)
135
210
{
 
211
        Remove(frame_number, frame_number);
 
212
}
 
213
 
 
214
// Remove range of frames
 
215
void CacheMemory::Remove(long int start_frame_number, long int end_frame_number)
 
216
{
136
217
        // Create a scoped lock, to protect the cache from multiple threads
137
218
        const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
138
219
 
139
220
        // Loop through frame numbers
140
 
        deque<long int>::iterator itr;
141
 
        for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
142
 
        {
143
 
                if (*itr == frame_number)
144
 
                {
145
 
                        // erase frame number
146
 
                        frame_numbers.erase(itr);
147
 
                        break;
148
 
                }
149
 
        }
150
 
 
151
 
        // Remove frame from map. If frame_number doesn't exist, frames.erase returns zero.
152
 
        frames.erase(frame_number);
 
221
        deque<long int>::iterator itr = frame_numbers.begin();
 
222
        while (itr != frame_numbers.end())
 
223
        {
 
224
                if (*itr >= start_frame_number && *itr <= end_frame_number)
 
225
                {
 
226
                        // erase frame number
 
227
                        itr = frame_numbers.erase(itr++);
 
228
                }else
 
229
                        ++itr;
 
230
        }
 
231
 
 
232
        // Loop through ordered frame numbers
 
233
        vector<long int>::iterator itr_ordered = ordered_frame_numbers.begin();
 
234
        while (itr_ordered != ordered_frame_numbers.end())
 
235
        {
 
236
                if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
 
237
                {
 
238
                        // erase frame number
 
239
                        frames.erase(*itr_ordered);
 
240
                        itr_ordered = ordered_frame_numbers.erase(itr_ordered++);
 
241
                }else
 
242
                        ++itr_ordered;
 
243
        }
 
244
 
 
245
        // Needs range processing (since cache has changed)
 
246
        needs_range_processing = true;
153
247
}
154
248
 
155
249
// Move frame to front of queue (so it lasts longer)
161
255
        // Does frame exists in cache?
162
256
        /* FIXME if the frame number isn't present, the loop will do nothing, so why protect it?
163
257
         * Is it to save time by avoiding a loop?
164
 
         * Do we really need to optmize the case where we've been given a nonexisting frame_number? */
 
258
         * Do we really need to optimize the case where we've been given a nonexisting frame_number? */
165
259
        if (frames.count(frame_number))
166
260
        {
167
261
                // Loop through frame numbers
189
283
 
190
284
        frames.clear();
191
285
        frame_numbers.clear();
 
286
        ordered_frame_numbers.clear();
192
287
}
193
288
 
194
289
// Count the frames in the queue
220
315
                }
221
316
        }
222
317
}
 
318
 
 
319
 
 
320
// Generate JSON string of this object
 
321
string CacheMemory::Json() {
 
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
        root["ranges"] = ranges;
 
337
 
 
338
        Json::Value version;
 
339
        stringstream range_version_str;
 
340
        range_version_str << range_version;
 
341
        root["version"] = range_version_str.str();
 
342
 
 
343
        // return JsonValue
 
344
        return root;
 
345
}
 
346
 
 
347
// Load JSON string into this object
 
348
void CacheMemory::SetJson(string value) throw(InvalidJSON) {
 
349
 
 
350
        // Parse JSON string into JSON objects
 
351
        Json::Value root;
 
352
        Json::Reader reader;
 
353
        bool success = reader.parse( value, root );
 
354
        if (!success)
 
355
                // Raise exception
 
356
                throw InvalidJSON("JSON could not be parsed (or is invalid)", "");
 
357
 
 
358
        try
 
359
        {
 
360
                // Set all values that match
 
361
                SetJsonValue(root);
 
362
        }
 
363
        catch (exception e)
 
364
        {
 
365
                // Error parsing JSON (or missing keys)
 
366
                throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
 
367
        }
 
368
}
 
369
 
 
370
// Load Json::JsonValue into this object
 
371
void CacheMemory::SetJsonValue(Json::Value root) throw(InvalidFile, ReaderClosed) {
 
372
 
 
373
        // Close timeline before we do anything (this also removes all open and closing clips)
 
374
        Clear();
 
375
 
 
376
        // Set parent data
 
377
        CacheBase::SetJsonValue(root);
 
378
 
 
379
        if (!root["type"].isNull())
 
380
                cache_type = root["type"].asString();
 
381
}
 
 
b'\\ No newline at end of file'