You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
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.
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include <tr1/memory>
|
||||
#include "Frame.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace openshot {
|
||||
|
||||
@@ -44,7 +45,8 @@ namespace openshot {
|
||||
class CacheBase
|
||||
{
|
||||
protected:
|
||||
int64 max_bytes; ///< This is the max number of bytes to cache (0 = no limit)
|
||||
string cache_type; ///< This is a friendly type name of the derived cache instance
|
||||
long long int max_bytes; ///< This is the max number of bytes to cache (0 = no limit)
|
||||
|
||||
/// Section lock for multiple threads
|
||||
CriticalSection *cacheCriticalSection;
|
||||
@@ -56,7 +58,7 @@ namespace openshot {
|
||||
|
||||
/// @brief Constructor that sets the max bytes to cache
|
||||
/// @param max_bytes The maximum bytes to allow in the cache. Once exceeded, the cache will purge the oldest frames.
|
||||
CacheBase(int64 max_bytes);
|
||||
CacheBase(long long int max_bytes);
|
||||
|
||||
/// @brief Add a Frame to the cache
|
||||
/// @param frame The openshot::Frame object needing to be cached.
|
||||
@@ -73,7 +75,7 @@ namespace openshot {
|
||||
virtual tr1::shared_ptr<Frame> GetFrame(long int frame_number) = 0;
|
||||
|
||||
/// Gets the maximum bytes value
|
||||
virtual int64 GetBytes() = 0;
|
||||
virtual long long int GetBytes() = 0;
|
||||
|
||||
/// Get the smallest frame number
|
||||
virtual tr1::shared_ptr<Frame> GetSmallestFrame() = 0;
|
||||
@@ -82,12 +84,17 @@ namespace openshot {
|
||||
/// @param frame_number The frame number of the cached frame
|
||||
virtual void Remove(long int frame_number) = 0;
|
||||
|
||||
/// @brief Remove a range of frames
|
||||
/// @param start_frame_number The starting frame number of the cached frame
|
||||
/// @param end_frame_number The ending frame number of the cached frame
|
||||
virtual void Remove(long int start_frame_number, long int end_frame_number) = 0;
|
||||
|
||||
/// Gets the maximum bytes value
|
||||
int64 GetMaxBytes() { return max_bytes; };
|
||||
long long int GetMaxBytes() { return max_bytes; };
|
||||
|
||||
/// @brief Set maximum bytes to a different amount
|
||||
/// @param number_of_bytes The maximum bytes to allow in the cache. Once exceeded, the cache will purge the oldest frames.
|
||||
void SetMaxBytes(int64 number_of_bytes) { max_bytes = number_of_bytes; };
|
||||
void SetMaxBytes(long long int number_of_bytes) { max_bytes = number_of_bytes; };
|
||||
|
||||
/// @brief Set maximum bytes to a different amount based on a ReaderInfo struct
|
||||
/// @param number_of_frames The maximum number of frames to hold in cache
|
||||
@@ -97,6 +104,11 @@ namespace openshot {
|
||||
/// @param channels The number of audio channels in the frame
|
||||
void SetMaxBytesFromInfo(long int number_of_frames, int width, int height, int sample_rate, int channels);
|
||||
|
||||
/// Get and Set JSON methods
|
||||
virtual string Json() = 0; ///< Generate JSON string of this object
|
||||
virtual void SetJson(string value) throw(InvalidJSON) = 0; ///< Load JSON string into this object
|
||||
virtual Json::Value JsonValue() = 0; ///< Generate Json::JsonValue for this object
|
||||
virtual void SetJsonValue(Json::Value root) = 0; ///< Load Json::JsonValue into this object
|
||||
|
||||
};
|
||||
|
||||
|
||||
136
include/CacheDisk.h
Normal file
136
include/CacheDisk.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Header file for CacheDisk class
|
||||
* @author Jonathan Thomas <jonathan@openshot.org>
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (c) 2008-2014 OpenShot Studios, LLC
|
||||
* <http://www.openshotstudios.com/>. This file is part of
|
||||
* OpenShot Library (libopenshot), an open-source project dedicated to
|
||||
* delivering high quality video editing and animation solutions to the
|
||||
* world. For more information visit <http://www.openshot.org/>.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is free software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPENSHOT_CACHE_DISK_H
|
||||
#define OPENSHOT_CACHE_DISK_H
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <tr1/memory>
|
||||
#include "CacheBase.h"
|
||||
#include "Frame.h"
|
||||
#include "Exceptions.h"
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
namespace openshot {
|
||||
|
||||
/**
|
||||
* @brief This class is a disk-based cache manager for Frame objects.
|
||||
*
|
||||
* It is used by the Timeline class, if enabled, to cache video and audio frames to disk, to cut down on CPU
|
||||
* and memory utilization. This will thrash a user's disk, but save their memory and CPU. It's a trade off that
|
||||
* sometimes makes perfect sense. You can also set the max number of bytes to cache.
|
||||
*/
|
||||
class CacheDisk : public CacheBase {
|
||||
private:
|
||||
QDir path; ///< This is the folder path of the cache directory
|
||||
map<long int, long int> frames; ///< This map holds the frame number and Frame objects
|
||||
deque<long int> frame_numbers; ///< This queue holds a sequential list of cached Frame numbers
|
||||
string image_format;
|
||||
float image_quality;
|
||||
float image_scale;
|
||||
|
||||
long long int frame_size_bytes; ///< The size of the cached frame in bytes
|
||||
bool needs_range_processing; ///< Something has changed, and the range data needs to be re-calculated
|
||||
Json::Value ranges; ///< JSON ranges of frame numbers
|
||||
vector<long int> ordered_frame_numbers; ///< Ordered list of frame numbers used by cache
|
||||
map<long int, long int> frame_ranges; ///< This map holds the ranges of frames, useful for quickly displaying the contents of the cache
|
||||
long int range_version; ///< The version of the JSON range data (incremented with each change)
|
||||
|
||||
/// Clean up cached frames that exceed the max number of bytes
|
||||
void CleanUp();
|
||||
|
||||
/// Init path directory
|
||||
void InitPath(string cache_path);
|
||||
|
||||
/// Calculate ranges of frames
|
||||
void CalculateRanges();
|
||||
|
||||
public:
|
||||
/// @brief Default constructor, no max bytes
|
||||
/// @param cache_path The folder path of the cache directory (empty string = /tmp/preview-cache/)
|
||||
/// @param format The image format for disk caching (ppm, jpg, png)
|
||||
/// @param quality The quality of the image (1.0=highest quality/slowest speed, 0.0=worst quality/fastest speed)
|
||||
/// @param scale The scale factor for the preview images (1.0 = original size, 0.5=half size, 0.25=quarter size, etc...)
|
||||
CacheDisk(string cache_path, string format, float quality, float scale);
|
||||
|
||||
/// @brief Constructor that sets the max bytes to cache
|
||||
/// @param cache_path The folder path of the cache directory (empty string = /tmp/preview-cache/)
|
||||
/// @param format The image format for disk caching (ppm, jpg, png)
|
||||
/// @param quality The quality of the image (1.0=highest quality/slowest speed, 0.0=worst quality/fastest speed)
|
||||
/// @param scale The scale factor for the preview images (1.0 = original size, 0.5=half size, 0.25=quarter size, etc...)
|
||||
/// @param max_bytes The maximum bytes to allow in the cache. Once exceeded, the cache will purge the oldest frames.
|
||||
CacheDisk(string cache_path, string format, float quality, float scale, long long int max_bytes);
|
||||
|
||||
// Default destructor
|
||||
~CacheDisk();
|
||||
|
||||
/// @brief Add a Frame to the cache
|
||||
/// @param frame The openshot::Frame object needing to be cached.
|
||||
void Add(tr1::shared_ptr<Frame> frame);
|
||||
|
||||
/// Clear the cache of all frames
|
||||
void Clear();
|
||||
|
||||
/// Count the frames in the queue
|
||||
long int Count();
|
||||
|
||||
/// @brief Get a frame from the cache
|
||||
/// @param frame_number The frame number of the cached frame
|
||||
tr1::shared_ptr<Frame> GetFrame(long int frame_number);
|
||||
|
||||
/// Gets the maximum bytes value
|
||||
long long int GetBytes();
|
||||
|
||||
/// Get the smallest frame number
|
||||
tr1::shared_ptr<Frame> GetSmallestFrame();
|
||||
|
||||
/// @brief Move frame to front of queue (so it lasts longer)
|
||||
/// @param frame_number The frame number of the cached frame
|
||||
void MoveToFront(long int frame_number);
|
||||
|
||||
/// @brief Remove a specific frame
|
||||
/// @param frame_number The frame number of the cached frame
|
||||
void Remove(long int frame_number);
|
||||
|
||||
/// @brief Remove a range of frames
|
||||
/// @param start_frame_number The starting frame number of the cached frame
|
||||
/// @param end_frame_number The ending frame number of the cached frame
|
||||
void Remove(long int start_frame_number, long int end_frame_number);
|
||||
|
||||
/// Get and Set JSON methods
|
||||
string Json(); ///< Generate JSON string of this object
|
||||
void SetJson(string value) throw(InvalidJSON); ///< Load JSON string into this object
|
||||
Json::Value JsonValue(); ///< Generate Json::JsonValue for this object
|
||||
void SetJsonValue(Json::Value root) throw(InvalidFile, ReaderClosed); ///< Load Json::JsonValue into this object
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Header file for Cache class
|
||||
* @brief Header file for CacheMemory class
|
||||
* @author Jonathan Thomas <jonathan@openshot.org>
|
||||
*
|
||||
* @section LICENSE
|
||||
@@ -25,8 +25,8 @@
|
||||
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPENSHOT_CACHE_H
|
||||
#define OPENSHOT_CACHE_H
|
||||
#ifndef OPENSHOT_CACHE_MEMORY_H
|
||||
#define OPENSHOT_CACHE_MEMORY_H
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
@@ -38,7 +38,7 @@
|
||||
namespace openshot {
|
||||
|
||||
/**
|
||||
* @brief This class is a cache manager for Frame objects.
|
||||
* @brief This class is a memory-based cache manager for Frame objects.
|
||||
*
|
||||
* It is used by FileReaders (such as FFmpegReader) to cache recently accessed frames. Due to the
|
||||
* high cost of decoding streams, once a frame is decoded, converted to RGB, and a Frame object is created,
|
||||
@@ -50,9 +50,17 @@ namespace openshot {
|
||||
map<long int, tr1::shared_ptr<Frame> > frames; ///< This map holds the frame number and Frame objects
|
||||
deque<long int> frame_numbers; ///< This queue holds a sequential list of cached Frame numbers
|
||||
|
||||
bool needs_range_processing; ///< Something has changed, and the range data needs to be re-calculated
|
||||
Json::Value ranges; ///< JSON ranges of frame numbers
|
||||
vector<long int> ordered_frame_numbers; ///< Ordered list of frame numbers used by cache
|
||||
map<long int, long int> frame_ranges; ///< This map holds the ranges of frames, useful for quickly displaying the contents of the cache
|
||||
long int range_version; ///< The version of the JSON range data (incremented with each change)
|
||||
|
||||
/// Clean up cached frames that exceed the max number of bytes
|
||||
void CleanUp();
|
||||
|
||||
/// Calculate ranges of frames
|
||||
void CalculateRanges();
|
||||
|
||||
public:
|
||||
/// Default constructor, no max bytes
|
||||
@@ -60,7 +68,7 @@ namespace openshot {
|
||||
|
||||
/// @brief Constructor that sets the max bytes to cache
|
||||
/// @param max_bytes The maximum bytes to allow in the cache. Once exceeded, the cache will purge the oldest frames.
|
||||
CacheMemory(int64 max_bytes);
|
||||
CacheMemory(long long int max_bytes);
|
||||
|
||||
// Default destructor
|
||||
~CacheMemory();
|
||||
@@ -80,7 +88,7 @@ namespace openshot {
|
||||
tr1::shared_ptr<Frame> GetFrame(long int frame_number);
|
||||
|
||||
/// Gets the maximum bytes value
|
||||
int64 GetBytes();
|
||||
long long int GetBytes();
|
||||
|
||||
/// Get the smallest frame number
|
||||
tr1::shared_ptr<Frame> GetSmallestFrame();
|
||||
@@ -93,6 +101,16 @@ namespace openshot {
|
||||
/// @param frame_number The frame number of the cached frame
|
||||
void Remove(long int frame_number);
|
||||
|
||||
/// @brief Remove a range of frames
|
||||
/// @param start_frame_number The starting frame number of the cached frame
|
||||
/// @param end_frame_number The ending frame number of the cached frame
|
||||
void Remove(long int start_frame_number, long int end_frame_number);
|
||||
|
||||
/// Get and Set JSON methods
|
||||
string Json(); ///< Generate JSON string of this object
|
||||
void SetJson(string value) throw(InvalidJSON); ///< Load JSON string into this object
|
||||
Json::Value JsonValue(); ///< Generate Json::JsonValue for this object
|
||||
void SetJsonValue(Json::Value root) throw(InvalidFile, ReaderClosed); ///< Load Json::JsonValue into this object
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -158,10 +158,10 @@ namespace openshot
|
||||
~Frame();
|
||||
|
||||
/// Add (or replace) pixel data to the frame (based on a solid color)
|
||||
void AddColor(int width, int height, string color);
|
||||
void AddColor(int new_width, int new_height, string color);
|
||||
|
||||
/// Add (or replace) pixel data to the frame
|
||||
void AddImage(int width, int height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_);
|
||||
void AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_);
|
||||
|
||||
/// Add (or replace) pixel data to the frame
|
||||
void AddImage(tr1::shared_ptr<QImage> new_image);
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
#include "AudioBufferSource.h"
|
||||
#include "AudioReaderSource.h"
|
||||
#include "AudioResampler.h"
|
||||
#include "CacheDisk.h"
|
||||
#include "CacheMemory.h"
|
||||
#include "ChunkReader.h"
|
||||
#include "ChunkWriter.h"
|
||||
|
||||
@@ -61,10 +61,10 @@ namespace openshot
|
||||
/// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...)
|
||||
int getSpeed() const { return speed; }
|
||||
|
||||
/// Play the audio
|
||||
/// Play the video
|
||||
void Play();
|
||||
|
||||
/// Seek the audio thread
|
||||
/// Seek the reader to a particular frame number
|
||||
void Seek(int new_position);
|
||||
|
||||
/// Set the currently displaying frame number
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace openshot
|
||||
void DrawFrameOnScene(string path, long _graphics_scene_address);
|
||||
|
||||
/// Get the cache object used by this reader (note: not all readers use cache)
|
||||
virtual CacheMemory* GetCache() = 0;
|
||||
virtual CacheBase* GetCache() = 0;
|
||||
|
||||
/// This method is required for all derived classes of ReaderBase, and returns the
|
||||
/// openshot::Frame object, which contains the image and audio information for that
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <tr1/memory>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QPainter>
|
||||
#include "CacheBase.h"
|
||||
#include "CacheDisk.h"
|
||||
#include "CacheMemory.h"
|
||||
#include "Color.h"
|
||||
#include "Clip.h"
|
||||
@@ -147,7 +149,7 @@ namespace openshot {
|
||||
list<Clip*> closing_clips; ///<List of clips that need to be closed
|
||||
map<Clip*, Clip*> open_clips; ///<List of 'opened' clips on this timeline
|
||||
list<EffectBase*> effects; ///<List of clips on this timeline
|
||||
CacheMemory final_cache; ///<Final cache of timeline frames
|
||||
CacheBase *final_cache; ///<Final cache of timeline frames
|
||||
|
||||
/// Process a new layer of video or audio
|
||||
void add_layer(tr1::shared_ptr<Frame> new_frame, Clip* source_clip, long int clip_frame_number, long int timeline_frame_number, bool is_top_clip);
|
||||
@@ -228,7 +230,10 @@ namespace openshot {
|
||||
list<EffectBase*> Effects() { return effects; };
|
||||
|
||||
/// Get the cache object used by this reader
|
||||
CacheMemory* GetCache() { return &final_cache; };
|
||||
CacheBase* GetCache() { return final_cache; };
|
||||
|
||||
/// Get the cache object used by this reader
|
||||
void SetCache(CacheBase* new_cache);
|
||||
|
||||
/// Get an openshot::Frame object for a specific frame number of this timeline.
|
||||
///
|
||||
|
||||
@@ -188,6 +188,7 @@ SET ( OPENSHOT_SOURCE_FILES
|
||||
AudioReaderSource.cpp
|
||||
AudioResampler.cpp
|
||||
CacheBase.cpp
|
||||
CacheDisk.cpp
|
||||
CacheMemory.cpp
|
||||
ChunkReader.cpp
|
||||
ChunkWriter.cpp
|
||||
|
||||
@@ -37,7 +37,7 @@ CacheBase::CacheBase() : max_bytes(0) {
|
||||
};
|
||||
|
||||
// Constructor that sets the max frames to cache
|
||||
CacheBase::CacheBase(int64 max_bytes) : max_bytes(max_bytes) {
|
||||
CacheBase::CacheBase(long long int max_bytes) : max_bytes(max_bytes) {
|
||||
// Init the critical section
|
||||
cacheCriticalSection = new CriticalSection();
|
||||
};
|
||||
@@ -46,7 +46,27 @@ CacheBase::CacheBase(int64 max_bytes) : max_bytes(max_bytes) {
|
||||
void CacheBase::SetMaxBytesFromInfo(long int number_of_frames, int width, int height, int sample_rate, int channels)
|
||||
{
|
||||
// n frames X height X width X 4 colors of chars X audio channels X 4 byte floats
|
||||
int64 bytes = number_of_frames * (height * width * 4 + (sample_rate * channels * 4));
|
||||
long long int bytes = number_of_frames * (height * width * 4 + (sample_rate * channels * 4));
|
||||
SetMaxBytes(bytes);
|
||||
}
|
||||
|
||||
// Generate Json::JsonValue for this object
|
||||
Json::Value CacheBase::JsonValue() {
|
||||
|
||||
// Create root json object
|
||||
Json::Value root;
|
||||
stringstream max_bytes_stream;
|
||||
max_bytes_stream << max_bytes;
|
||||
root["max_bytes"] = max_bytes_stream.str();
|
||||
|
||||
// return JsonValue
|
||||
return root;
|
||||
}
|
||||
|
||||
// Load Json::JsonValue into this object
|
||||
void CacheBase::SetJsonValue(Json::Value root) {
|
||||
|
||||
// Set data from Json (if key is found)
|
||||
if (!root["max_bytes"].isNull())
|
||||
max_bytes = atoll(root["max_bytes"].asString().c_str());
|
||||
}
|
||||
529
src/CacheDisk.cpp
Normal file
529
src/CacheDisk.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,18 @@ using namespace openshot;
|
||||
|
||||
// Default constructor, no max bytes
|
||||
CacheMemory::CacheMemory() : CacheBase(0) {
|
||||
|
||||
// Set cache type name
|
||||
cache_type = "CacheMemory";
|
||||
range_version = 0;
|
||||
needs_range_processing = false;
|
||||
};
|
||||
|
||||
// Constructor that sets the max bytes to cache
|
||||
CacheMemory::CacheMemory(int64 max_bytes) : CacheBase(max_bytes) {
|
||||
|
||||
CacheMemory::CacheMemory(long long int max_bytes) : CacheBase(max_bytes) {
|
||||
// Set cache type name
|
||||
cache_type = "CacheMemory";
|
||||
range_version = 0;
|
||||
needs_range_processing = false;
|
||||
};
|
||||
|
||||
// Default destructor
|
||||
@@ -45,12 +51,79 @@ CacheMemory::~CacheMemory()
|
||||
{
|
||||
frames.clear();
|
||||
frame_numbers.clear();
|
||||
ordered_frame_numbers.clear();
|
||||
|
||||
// remove critical section
|
||||
delete cacheCriticalSection;
|
||||
cacheCriticalSection = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Calculate ranges of frames
|
||||
void CacheMemory::CalculateRanges() {
|
||||
// Only calculate when something has changed
|
||||
if (needs_range_processing) {
|
||||
|
||||
// Create a scoped lock, to protect the cache from multiple threads
|
||||
const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
|
||||
|
||||
// Sort ordered frame #s, and calculate JSON ranges
|
||||
std::sort(ordered_frame_numbers.begin(), ordered_frame_numbers.end());
|
||||
|
||||
// Clear existing JSON variable
|
||||
ranges.clear();
|
||||
ranges = Json::Value(Json::arrayValue);
|
||||
|
||||
// Increment range version
|
||||
range_version++;
|
||||
|
||||
vector<long int>::iterator itr_ordered;
|
||||
long int starting_frame = *ordered_frame_numbers.begin();
|
||||
long int ending_frame = *ordered_frame_numbers.begin();
|
||||
|
||||
// Loop through all known frames (in sequential order)
|
||||
for (itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered) {
|
||||
long int frame_number = *itr_ordered;
|
||||
if (frame_number - ending_frame > 1) {
|
||||
// End of range detected
|
||||
Json::Value range;
|
||||
|
||||
// Add JSON object with start/end attributes
|
||||
// Use strings, since long ints are supported in JSON
|
||||
stringstream start_str;
|
||||
start_str << starting_frame;
|
||||
stringstream end_str;
|
||||
end_str << ending_frame;
|
||||
range["start"] = start_str.str();
|
||||
range["end"] = end_str.str();
|
||||
ranges.append(range);
|
||||
|
||||
// Set new starting range
|
||||
starting_frame = frame_number;
|
||||
}
|
||||
|
||||
// Set current frame as end of range, and keep looping
|
||||
ending_frame = frame_number;
|
||||
}
|
||||
|
||||
// APPEND FINAL VALUE
|
||||
Json::Value range;
|
||||
|
||||
// Add JSON object with start/end attributes
|
||||
// Use strings, since long ints are supported in JSON
|
||||
stringstream start_str;
|
||||
start_str << starting_frame;
|
||||
stringstream end_str;
|
||||
end_str << ending_frame;
|
||||
range["start"] = start_str.str();
|
||||
range["end"] = end_str.str();
|
||||
ranges.append(range);
|
||||
|
||||
// Reset needs_range_processing
|
||||
needs_range_processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a Frame to the cache
|
||||
void CacheMemory::Add(tr1::shared_ptr<Frame> frame)
|
||||
{
|
||||
@@ -68,6 +141,8 @@ void CacheMemory::Add(tr1::shared_ptr<Frame> frame)
|
||||
// Add frame to queue and map
|
||||
frames[frame_number] = frame;
|
||||
frame_numbers.push_front(frame_number);
|
||||
ordered_frame_numbers.push_back(frame_number);
|
||||
needs_range_processing = true;
|
||||
|
||||
// Clean up old frames
|
||||
CleanUp();
|
||||
@@ -113,12 +188,12 @@ tr1::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
|
||||
}
|
||||
|
||||
// Gets the maximum bytes value
|
||||
int64 CacheMemory::GetBytes()
|
||||
long long int CacheMemory::GetBytes()
|
||||
{
|
||||
// Create a scoped lock, to protect the cache from multiple threads
|
||||
const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
|
||||
|
||||
int64 total_bytes = 0;
|
||||
long long int total_bytes = 0;
|
||||
|
||||
// Loop through frames, and calculate total bytes
|
||||
deque<long int>::reverse_iterator itr;
|
||||
@@ -132,24 +207,43 @@ int64 CacheMemory::GetBytes()
|
||||
|
||||
// Remove a specific frame
|
||||
void CacheMemory::Remove(long int frame_number)
|
||||
{
|
||||
Remove(frame_number, frame_number);
|
||||
}
|
||||
|
||||
// Remove range of frames
|
||||
void CacheMemory::Remove(long int start_frame_number, long int end_frame_number)
|
||||
{
|
||||
// Create a scoped lock, to protect the cache from multiple threads
|
||||
const GenericScopedLock<CriticalSection> lock(*cacheCriticalSection);
|
||||
|
||||
// Loop through frame numbers
|
||||
deque<long int>::iterator itr;
|
||||
for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
|
||||
deque<long int>::iterator itr = frame_numbers.begin();
|
||||
while (itr != frame_numbers.end())
|
||||
{
|
||||
if (*itr == frame_number)
|
||||
if (*itr >= start_frame_number && *itr <= end_frame_number)
|
||||
{
|
||||
// erase frame number
|
||||
frame_numbers.erase(itr);
|
||||
break;
|
||||
}
|
||||
itr = frame_numbers.erase(itr++);
|
||||
}else
|
||||
++itr;
|
||||
}
|
||||
|
||||
// Remove frame from map. If frame_number doesn't exist, frames.erase returns zero.
|
||||
frames.erase(frame_number);
|
||||
// Loop through ordered frame numbers
|
||||
vector<long int>::iterator itr_ordered = ordered_frame_numbers.begin();
|
||||
while (itr_ordered != ordered_frame_numbers.end())
|
||||
{
|
||||
if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
|
||||
{
|
||||
// erase frame number
|
||||
frames.erase(*itr_ordered);
|
||||
itr_ordered = ordered_frame_numbers.erase(itr_ordered++);
|
||||
}else
|
||||
++itr_ordered;
|
||||
}
|
||||
|
||||
// Needs range processing (since cache has changed)
|
||||
needs_range_processing = true;
|
||||
}
|
||||
|
||||
// Move frame to front of queue (so it lasts longer)
|
||||
@@ -161,7 +255,7 @@ void CacheMemory::MoveToFront(long int frame_number)
|
||||
// Does frame exists in cache?
|
||||
/* FIXME if the frame number isn't present, the loop will do nothing, so why protect it?
|
||||
* Is it to save time by avoiding a loop?
|
||||
* Do we really need to optmize the case where we've been given a nonexisting frame_number? */
|
||||
* Do we really need to optimize the case where we've been given a nonexisting frame_number? */
|
||||
if (frames.count(frame_number))
|
||||
{
|
||||
// Loop through frame numbers
|
||||
@@ -189,6 +283,7 @@ void CacheMemory::Clear()
|
||||
|
||||
frames.clear();
|
||||
frame_numbers.clear();
|
||||
ordered_frame_numbers.clear();
|
||||
}
|
||||
|
||||
// Count the frames in the queue
|
||||
@@ -220,3 +315,67 @@ void CacheMemory::CleanUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate JSON string of this object
|
||||
string CacheMemory::Json() {
|
||||
|
||||
// Return formatted string
|
||||
return JsonValue().toStyledString();
|
||||
}
|
||||
|
||||
// Generate Json::JsonValue for this object
|
||||
Json::Value CacheMemory::JsonValue() {
|
||||
|
||||
// Proccess range data (if anything has changed)
|
||||
CalculateRanges();
|
||||
|
||||
// Create root json object
|
||||
Json::Value root = CacheBase::JsonValue(); // get parent properties
|
||||
root["type"] = cache_type;
|
||||
root["ranges"] = ranges;
|
||||
|
||||
Json::Value version;
|
||||
stringstream range_version_str;
|
||||
range_version_str << range_version;
|
||||
root["version"] = range_version_str.str();
|
||||
|
||||
// return JsonValue
|
||||
return root;
|
||||
}
|
||||
|
||||
// Load JSON string into this object
|
||||
void CacheMemory::SetJson(string value) throw(InvalidJSON) {
|
||||
|
||||
// Parse JSON string into JSON objects
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
bool success = reader.parse( value, root );
|
||||
if (!success)
|
||||
// Raise exception
|
||||
throw InvalidJSON("JSON could not be parsed (or is invalid)", "");
|
||||
|
||||
try
|
||||
{
|
||||
// Set all values that match
|
||||
SetJsonValue(root);
|
||||
}
|
||||
catch (exception e)
|
||||
{
|
||||
// Error parsing JSON (or missing keys)
|
||||
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Load Json::JsonValue into this object
|
||||
void CacheMemory::SetJsonValue(Json::Value root) throw(InvalidFile, ReaderClosed) {
|
||||
|
||||
// Close timeline before we do anything (this also removes all open and closing clips)
|
||||
Clear();
|
||||
|
||||
// Set parent data
|
||||
CacheBase::SetJsonValue(root);
|
||||
|
||||
if (!root["type"].isNull())
|
||||
cache_type = root["type"].asString();
|
||||
}
|
||||
@@ -670,11 +670,11 @@ int Frame::constrain(int color_value)
|
||||
}
|
||||
|
||||
// Add (or replace) pixel data to the frame (based on a solid color)
|
||||
void Frame::AddColor(int width, int height, string color)
|
||||
void Frame::AddColor(int new_width, int new_height, string color)
|
||||
{
|
||||
// Create new image object, and fill with pixel data
|
||||
const GenericScopedLock<CriticalSection> lock(addingImageSection);
|
||||
image = tr1::shared_ptr<QImage>(new QImage(width, height, QImage::Format_RGBA8888));
|
||||
image = tr1::shared_ptr<QImage>(new QImage(new_width, new_height, QImage::Format_RGBA8888));
|
||||
|
||||
// Fill with solid color
|
||||
image->fill(QColor(QString::fromStdString(color)));
|
||||
@@ -686,18 +686,18 @@ void Frame::AddColor(int width, int height, string color)
|
||||
}
|
||||
|
||||
// Add (or replace) pixel data to the frame
|
||||
void Frame::AddImage(int width, int height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
|
||||
void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
|
||||
{
|
||||
// Create new buffer
|
||||
const GenericScopedLock<CriticalSection> lock(addingImageSection);
|
||||
int buffer_size = width * height * bytes_per_pixel;
|
||||
int buffer_size = new_width * new_height * bytes_per_pixel;
|
||||
qbuffer = new unsigned char[buffer_size]();
|
||||
|
||||
// Copy buffer data
|
||||
memcpy((unsigned char*)qbuffer, pixels_, buffer_size);
|
||||
|
||||
// Create new image object, and fill with pixel data
|
||||
image = tr1::shared_ptr<QImage>(new QImage(qbuffer, width, height, width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer));
|
||||
image = tr1::shared_ptr<QImage>(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer));
|
||||
|
||||
// Always convert to RGBA8888 (if different)
|
||||
if (image->format() != QImage::Format_RGBA8888)
|
||||
|
||||
@@ -56,13 +56,13 @@ namespace openshot
|
||||
current_display_frame = current_frame_number;
|
||||
}
|
||||
|
||||
// Seek the audio thread
|
||||
// Seek the reader to a particular frame number
|
||||
void VideoCacheThread::Seek(int new_position)
|
||||
{
|
||||
position = new_position;
|
||||
}
|
||||
|
||||
// Play the audio
|
||||
// Play the video
|
||||
void VideoCacheThread::Play() {
|
||||
// Start playing
|
||||
is_playing = true;
|
||||
|
||||
@@ -57,7 +57,8 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha
|
||||
info.video_length = info.fps.ToFloat() * info.duration;
|
||||
|
||||
// Init cache
|
||||
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
|
||||
final_cache = new CacheMemory();
|
||||
final_cache->SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
|
||||
}
|
||||
|
||||
// Add an openshot::Clip to the timeline
|
||||
@@ -125,7 +126,7 @@ void Timeline::apply_mapper_to_clip(Clip* clip)
|
||||
void Timeline::ApplyMapperToClips()
|
||||
{
|
||||
// Clear all cached frames
|
||||
final_cache.Clear();
|
||||
final_cache->Clear();
|
||||
|
||||
// Loop through all clips
|
||||
list<Clip*>::iterator clip_itr;
|
||||
@@ -582,7 +583,7 @@ void Timeline::Close()
|
||||
is_open = false;
|
||||
|
||||
// Clear cache
|
||||
final_cache.Clear();
|
||||
final_cache->Clear();
|
||||
}
|
||||
|
||||
// Open the reader (and start consuming resources)
|
||||
@@ -609,7 +610,7 @@ tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(Reader
|
||||
requested_frame = 1;
|
||||
|
||||
// Check cache
|
||||
tr1::shared_ptr<Frame> frame = final_cache.GetFrame(requested_frame);
|
||||
tr1::shared_ptr<Frame> frame = final_cache->GetFrame(requested_frame);
|
||||
if (frame) {
|
||||
// Debug output
|
||||
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Cached frame found)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
|
||||
@@ -623,7 +624,7 @@ tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(Reader
|
||||
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
|
||||
|
||||
// Check cache again (due to locking)
|
||||
frame = final_cache.GetFrame(requested_frame);
|
||||
frame = final_cache->GetFrame(requested_frame);
|
||||
if (frame) {
|
||||
// Debug output
|
||||
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Cached frame found on 2nd look)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
|
||||
@@ -683,6 +684,7 @@ tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(Reader
|
||||
|
||||
// Create blank frame (which will become the requested frame)
|
||||
tr1::shared_ptr<Frame> new_frame(tr1::shared_ptr<Frame>(new Frame(frame_number, info.width, info.height, "#000000", samples_in_frame, info.channels)));
|
||||
new_frame->AddAudioSilence(samples_in_frame);
|
||||
new_frame->SampleRate(info.sample_rate);
|
||||
new_frame->ChannelsLayout(info.channel_layout);
|
||||
|
||||
@@ -748,7 +750,7 @@ tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(Reader
|
||||
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Add frame to cache)", "frame_number", frame_number, "info.width", info.width, "info.height", info.height, "", -1, "", -1, "", -1);
|
||||
|
||||
// Add final frame to cache
|
||||
final_cache.Add(new_frame);
|
||||
final_cache->Add(new_frame);
|
||||
|
||||
} // end frame loop
|
||||
} // end parallel
|
||||
@@ -757,7 +759,7 @@ tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(Reader
|
||||
ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (end parallel region)", "requested_frame", requested_frame, "omp_get_thread_num()", omp_get_thread_num(), "", -1, "", -1, "", -1, "", -1);
|
||||
|
||||
// Return frame (or blank frame)
|
||||
return final_cache.GetFrame(requested_frame);
|
||||
return final_cache->GetFrame(requested_frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,6 +812,12 @@ vector<Clip*> Timeline::find_intersecting_clips(long int requested_frame, int nu
|
||||
return matching_clips;
|
||||
}
|
||||
|
||||
// Get the cache object used by this reader
|
||||
void Timeline::SetCache(CacheBase* new_cache) {
|
||||
// Set new cache
|
||||
final_cache = new_cache;
|
||||
}
|
||||
|
||||
// Generate JSON string of this object
|
||||
string Timeline::Json() {
|
||||
|
||||
@@ -943,9 +951,6 @@ void Timeline::SetJsonValue(Json::Value root) throw(InvalidFile, ReaderClosed) {
|
||||
// Apply a special formatted JSON object, which represents a change to the timeline (insert, update, delete)
|
||||
void Timeline::ApplyJsonDiff(string value) throw(InvalidJSON, InvalidJSONKey) {
|
||||
|
||||
// Clear internal cache (since things are about to change)
|
||||
final_cache.Clear();
|
||||
|
||||
// Parse JSON string into JSON objects
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
@@ -982,9 +987,6 @@ void Timeline::ApplyJsonDiff(string value) throw(InvalidJSON, InvalidJSONKey) {
|
||||
// Error parsing JSON (or missing keys)
|
||||
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
|
||||
}
|
||||
|
||||
// Adjust cache (in case something changed)
|
||||
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
|
||||
}
|
||||
|
||||
// Apply JSON diff to clips
|
||||
@@ -1053,6 +1055,11 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int new_starting_frame = change["value"]["position"].asDouble() * info.fps.ToDouble();
|
||||
long int new_ending_frame = (change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble();
|
||||
final_cache->Remove(new_starting_frame - 1, new_ending_frame + 1);
|
||||
|
||||
// Determine type of change operation
|
||||
if (change_type == "insert") {
|
||||
|
||||
@@ -1064,14 +1071,30 @@ void Timeline::apply_json_to_clips(Json::Value change) throw(InvalidJSONKey) {
|
||||
} else if (change_type == "update") {
|
||||
|
||||
// Update existing clip
|
||||
if (existing_clip)
|
||||
existing_clip->SetJsonValue(change["value"]); // Update clip properties from JSON
|
||||
if (existing_clip) {
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int old_starting_frame = existing_clip->Position() * info.fps.ToDouble();
|
||||
long int old_ending_frame = (existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble();
|
||||
final_cache->Remove(old_starting_frame - 1, old_ending_frame + 1);
|
||||
|
||||
// Update clip properties from JSON
|
||||
existing_clip->SetJsonValue(change["value"]);
|
||||
}
|
||||
|
||||
} else if (change_type == "delete") {
|
||||
|
||||
// Remove existing clip
|
||||
if (existing_clip)
|
||||
RemoveClip(existing_clip); // Remove clip from timeline
|
||||
if (existing_clip) {
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int old_starting_frame = existing_clip->Position() * info.fps.ToDouble();
|
||||
long int old_ending_frame = (existing_clip->Position() + existing_clip->End() - existing_clip->Start()) * info.fps.ToDouble();
|
||||
final_cache->Remove(old_starting_frame - 1, old_ending_frame + 1);
|
||||
|
||||
// Remove clip from timeline
|
||||
RemoveClip(existing_clip);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1124,6 +1147,11 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef
|
||||
// Get key and type of change
|
||||
string change_type = change["type"].asString();
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int new_starting_frame = change["value"]["position"].asDouble() * info.fps.ToDouble();
|
||||
long int new_ending_frame = (change["value"]["position"].asDouble() + change["value"]["end"].asDouble() - change["value"]["start"].asDouble()) * info.fps.ToDouble();
|
||||
final_cache->Remove(new_starting_frame - 1, new_ending_frame + 1);
|
||||
|
||||
// Determine type of change operation
|
||||
if (change_type == "insert") {
|
||||
|
||||
@@ -1145,14 +1173,30 @@ void Timeline::apply_json_to_effects(Json::Value change, EffectBase* existing_ef
|
||||
} else if (change_type == "update") {
|
||||
|
||||
// Update existing effect
|
||||
if (existing_effect)
|
||||
existing_effect->SetJsonValue(change["value"]); // Update effect properties from JSON
|
||||
if (existing_effect) {
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int old_starting_frame = existing_effect->Position() * info.fps.ToDouble();
|
||||
long int old_ending_frame = (existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble();
|
||||
final_cache->Remove(old_starting_frame - 1, old_ending_frame + 1);
|
||||
|
||||
// Update effect properties from JSON
|
||||
existing_effect->SetJsonValue(change["value"]);
|
||||
}
|
||||
|
||||
} else if (change_type == "delete") {
|
||||
|
||||
// Remove existing effect
|
||||
if (existing_effect)
|
||||
RemoveEffect(existing_effect); // Remove effect from timeline
|
||||
if (existing_effect) {
|
||||
|
||||
// Calculate start and end frames that this impacts, and remove those frames from the cache
|
||||
long int old_starting_frame = existing_effect->Position() * info.fps.ToDouble();
|
||||
long int old_ending_frame = (existing_effect->Position() + existing_effect->End() - existing_effect->Start()) * info.fps.ToDouble();
|
||||
final_cache->Remove(old_starting_frame - 1, old_ending_frame + 1);
|
||||
|
||||
// Remove effect from timeline
|
||||
RemoveEffect(existing_effect);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1167,6 +1211,9 @@ void Timeline::apply_json_to_timeline(Json::Value change) throw(InvalidJSONKey)
|
||||
if (change["key"].size() >= 2)
|
||||
sub_key = change["key"][(uint)1].asString();
|
||||
|
||||
// Clear entire cache
|
||||
final_cache->Clear();
|
||||
|
||||
// Determine type of change operation
|
||||
if (change_type == "insert" || change_type == "update") {
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "../../../include/ReaderBase.h"
|
||||
#include "../../../include/WriterBase.h"
|
||||
#include "../../../include/CacheBase.h"
|
||||
#include "../../../include/CacheDisk.h"
|
||||
#include "../../../include/CacheMemory.h"
|
||||
#include "../../../include/ChannelLayouts.h"
|
||||
#include "../../../include/ChunkReader.h"
|
||||
@@ -117,6 +118,7 @@
|
||||
%include "../../../include/ReaderBase.h"
|
||||
%include "../../../include/WriterBase.h"
|
||||
%include "../../../include/CacheBase.h"
|
||||
%include "../../../include/CacheDisk.h"
|
||||
%include "../../../include/CacheMemory.h"
|
||||
%include "../../../include/ChannelLayouts.h"
|
||||
%include "../../../include/ChunkReader.h"
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace tr1
|
||||
#include "../../../include/ReaderBase.h"
|
||||
#include "../../../include/WriterBase.h"
|
||||
#include "../../../include/CacheBase.h"
|
||||
#include "../../../include/CacheDisk.h"
|
||||
#include "../../../include/CacheMemory.h"
|
||||
#include "../../../include/ChannelLayouts.h"
|
||||
#include "../../../include/ChunkReader.h"
|
||||
@@ -112,6 +113,7 @@ namespace tr1
|
||||
%include "../../../include/ReaderBase.h"
|
||||
%include "../../../include/WriterBase.h"
|
||||
%include "../../../include/CacheBase.h"
|
||||
%include "../../../include/CacheDisk.h"
|
||||
%include "../../../include/CacheMemory.h"
|
||||
%include "../../../include/ChannelLayouts.h"
|
||||
%include "../../../include/ChunkReader.h"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "UnitTest++.h"
|
||||
#include "../include/OpenShot.h"
|
||||
#include "../include/Json.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace openshot;
|
||||
@@ -270,3 +271,116 @@ TEST(Cache_Set_Max_Bytes)
|
||||
c.SetMaxBytes(4 * 1024);
|
||||
CHECK_EQUAL(4 * 1024, c.GetMaxBytes());
|
||||
}
|
||||
|
||||
TEST(CacheDisk_Set_Max_Bytes)
|
||||
{
|
||||
// Create cache object (using platform /temp/ directory)
|
||||
CacheDisk c("", "PPM", 1.0, 0.25);
|
||||
|
||||
// Add frames to disk cache
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
// Add blank frame to the cache
|
||||
tr1::shared_ptr<Frame> f(new Frame());
|
||||
f->number = i;
|
||||
// Add some picture data
|
||||
f->AddColor(1280, 720, "Blue");
|
||||
f->ResizeAudio(2, 500, 44100, LAYOUT_STEREO);
|
||||
f->AddAudioSilence(500);
|
||||
c.Add(f);
|
||||
}
|
||||
|
||||
CHECK_EQUAL(0, c.GetMaxBytes()); // Cache defaults max frames to -1, unlimited frames
|
||||
|
||||
// Set max frames
|
||||
c.SetMaxBytes(8 * 1024);
|
||||
CHECK_EQUAL(8 * 1024, c.GetMaxBytes());
|
||||
|
||||
// Set max frames
|
||||
c.SetMaxBytes(4 * 1024);
|
||||
CHECK_EQUAL(4 * 1024, c.GetMaxBytes());
|
||||
|
||||
// Read frames from disk cache
|
||||
tr1::shared_ptr<Frame> f = c.GetFrame(5);
|
||||
CHECK_EQUAL(320, f->GetWidth());
|
||||
CHECK_EQUAL(180, f->GetHeight());
|
||||
CHECK_EQUAL(2, f->GetAudioChannelsCount());
|
||||
CHECK_EQUAL(500, f->GetAudioSamplesCount());
|
||||
CHECK_EQUAL(LAYOUT_STEREO, f->ChannelsLayout());
|
||||
CHECK_EQUAL(44100, f->SampleRate());
|
||||
|
||||
}
|
||||
|
||||
TEST(CacheDisk_JSON)
|
||||
{
|
||||
// Create cache object (using platform /temp/ directory)
|
||||
CacheDisk c("", "PPM", 1.0, 0.25);
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f3(new Frame(3, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f3);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("1", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f1(new Frame(1, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f1);
|
||||
CHECK_EQUAL(2, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("2", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f2(new Frame(2, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f2);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("3", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f5(new Frame(5, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f5);
|
||||
CHECK_EQUAL(2, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("4", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f4(new Frame(4, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f4);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("5", c.JsonValue()["version"].asString());
|
||||
|
||||
}
|
||||
|
||||
TEST(CacheMemory_JSON)
|
||||
{
|
||||
// Create memory cache object
|
||||
CacheMemory c;
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f3(new Frame(3, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f3);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("1", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f1(new Frame(1, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f1);
|
||||
CHECK_EQUAL(2, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("2", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f2(new Frame(2, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f2);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("3", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f5(new Frame(5, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f5);
|
||||
CHECK_EQUAL(2, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("4", c.JsonValue()["version"].asString());
|
||||
|
||||
// Add some frames (out of order)
|
||||
tr1::shared_ptr<Frame> f4(new Frame(4, 1280, 720, "Blue", 500, 2));
|
||||
c.Add(f4);
|
||||
CHECK_EQUAL(1, c.JsonValue()["ranges"].size());
|
||||
CHECK_EQUAL("5", c.JsonValue()["version"].asString());
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ TEST(ReaderBase_Derived_Class)
|
||||
{
|
||||
public:
|
||||
TestReader() { };
|
||||
CacheMemory* GetCache() { return NULL; };
|
||||
CacheBase* GetCache() { return NULL; };
|
||||
tr1::shared_ptr<Frame> GetFrame(long int number) { tr1::shared_ptr<Frame> f(new Frame()); return f; }
|
||||
void Close() { };
|
||||
void Open() { };
|
||||
|
||||
Reference in New Issue
Block a user