diff --git a/include/Cache.h b/include/Cache.h
index 224944f1..b2f16370 100644
--- a/include/Cache.h
+++ b/include/Cache.h
@@ -91,10 +91,6 @@ namespace openshot {
/// Display a list of cached frame numbers
void Display();
- /// @brief Check for the existence of a frame in the cache
- /// @param frame_number The frame number of the cached frame
- bool Exists(int frame_number);
-
/// @brief Get a frame from the cache
/// @param frame_number The frame number of the cached frame
tr1::shared_ptr GetFrame(int frame_number);
diff --git a/include/Timeline.h b/include/Timeline.h
index ea56c34b..20b7043a 100644
--- a/include/Timeline.h
+++ b/include/Timeline.h
@@ -230,7 +230,7 @@ namespace openshot {
///
/// @returns The requested frame (containing the image)
/// @param requested_frame The frame number that is requested.
- tr1::shared_ptr GetFrame(int requested_frame) throw(ReaderClosed);
+ tr1::shared_ptr GetFrame(int requested_frame) throw(ReaderClosed, OutOfBoundsFrame);
// Curves for the viewport
Keyframe viewport_scale; /// frame)
const GenericScopedLock lock(*cacheCriticalSection);
// Remove frame if it already exists
- if (Exists(frame_number))
+ if (frames.count(frame_number))
// Move frame to front of queue
MoveToFront(frame_number);
+
else
{
// Add frame to queue and map
@@ -74,40 +75,23 @@ void Cache::Add(int frame_number, tr1::shared_ptr frame)
}
}
-// Check for the existance of a frame in the cache
-bool Cache::Exists(int frame_number)
-{
- // Create a scoped lock, to protect the cache from multiple threads
- const GenericScopedLock lock(*cacheCriticalSection);
-
- // Is frame number cached
- if (frames.count(frame_number))
- return true;
- else
- return false;
-}
-
-// Get a frame from the cache
+// Get a frame from the cache (or NULL shared_ptr if no frame is found)
tr1::shared_ptr Cache::GetFrame(int frame_number)
{
// Create a scoped lock, to protect the cache from multiple threads
const GenericScopedLock lock(*cacheCriticalSection);
// Does frame exists in cache?
- if (Exists(frame_number))
- {
- // move it to the front of the cache
- MoveToFront(frame_number);
-
+ if (frames.count(frame_number))
// return the Frame object
return frames[frame_number];
- }
+
else
- // throw an exception for the missing frame
- throw OutOfBoundsFrame("Frame not found in the cache", frame_number, -1);
+ // no Frame found
+ return tr1::shared_ptr();
}
-// Get the smallest frame number
+// Get the smallest frame number (or NULL shared_ptr if no frame is found)
tr1::shared_ptr Cache::GetSmallestFrame()
{
// Create a scoped lock, to protect the cache from multiple threads
@@ -180,7 +164,7 @@ void Cache::MoveToFront(int frame_number)
const GenericScopedLock lock(*cacheCriticalSection);
// Does frame exists in cache?
- if (Exists(frame_number))
+ if (frames.count(frame_number))
{
// Loop through frame numbers
deque::iterator itr;
@@ -197,9 +181,6 @@ void Cache::MoveToFront(int frame_number)
}
}
}
- else
- // throw an exception for the missing frame
- throw OutOfBoundsFrame("Frame not found in the cache", frame_number, -1);
}
// Clear the cache of all frames
diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp
index 7dde9266..2ffdc13a 100644
--- a/src/FFmpegReader.cpp
+++ b/src/FFmpegReader.cpp
@@ -277,8 +277,8 @@ void FFmpegReader::UpdateAudioInfo()
info.audio_timebase.num = aStream->time_base.num;
info.audio_timebase.den = aStream->time_base.den;
- // Get timebase of audio stream (if valid)
- if (aStream->duration > 0.0f)
+ // Get timebase of audio stream (if valid) and greater than the current duration
+ if (aStream->duration > 0.0f && aStream->duration > info.duration)
info.duration = aStream->duration * info.audio_timebase.ToDouble();
// Check for an invalid video length
@@ -388,6 +388,9 @@ void FFmpegReader::UpdateVideoInfo()
info.fps.den = 1;
info.video_timebase.num = 1;
info.video_timebase.den = 24;
+
+ // Calculate number of frames
+ info.video_length = round(info.duration * info.fps.ToDouble());
}
}
@@ -412,12 +415,13 @@ tr1::shared_ptr FFmpegReader::GetFrame(int requested_frame) throw(OutOfBo
AppendDebugMethod("FFmpegReader::GetFrame", "requested_frame", requested_frame, "last_frame", last_frame, "", -1, "", -1, "", -1, "", -1);
// Check the cache for this frame
- if (final_cache.Exists(requested_frame)) {
+ tr1::shared_ptr frame = final_cache.GetFrame(requested_frame);
+ if (frame) {
// Debug output
AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
// Return the cached frame
- return final_cache.GetFrame(requested_frame);
+ return frame;
}
else
{
@@ -425,12 +429,13 @@ tr1::shared_ptr FFmpegReader::GetFrame(int requested_frame) throw(OutOfBo
const GenericScopedLock lock(getFrameCriticalSection);
// Check the cache a 2nd time (due to a potential previous lock)
- if (final_cache.Exists(requested_frame)) {
+ frame = final_cache.GetFrame(requested_frame);
+ if (frame) {
// Debug output
AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame on 2nd look", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
// Return the cached frame
- return final_cache.GetFrame(requested_frame);
+ return frame;
}
// Frame is not in cache
@@ -582,7 +587,7 @@ tr1::shared_ptr FFmpegReader::ReadStream(int requested_frame)
CheckWorkingFrames(false);
// Check if requested 'final' frame is available
- is_cache_found = final_cache.Exists(requested_frame);
+ is_cache_found = (final_cache.GetFrame(requested_frame) != NULL);
// Increment frames processed
packets_processed++;
@@ -600,25 +605,22 @@ tr1::shared_ptr FFmpegReader::ReadStream(int requested_frame)
AppendDebugMethod("FFmpegReader::ReadStream (Completed)", "packets_processed", packets_processed, "end_of_stream", end_of_stream, "largest_frame_processed", largest_frame_processed, "Working Cache Count", working_cache.Count(), "", -1, "", -1);
// End of stream?
- if (end_of_stream) {
+ if (end_of_stream)
// Mark the any other working frames as 'finished'
CheckWorkingFrames(end_of_stream);
- // Update readers video length (to a largest processed frame number)
- info.video_length = largest_frame_processed; // just a guess, but this frame is certainly out of bounds
- is_duration_known = largest_frame_processed;
- }
-
// Return requested frame (if found)
- if (final_cache.Exists(requested_frame))
+ tr1::shared_ptr frame = final_cache.GetFrame(requested_frame);
+ if (frame)
// Return prepared frame
- return final_cache.GetFrame(requested_frame);
+ return frame;
else {
// Check if largest frame is still cached
- if (final_cache.Exists(largest_frame_processed)) {
+ frame = final_cache.GetFrame(largest_frame_processed);
+ if (frame) {
// return the largest processed frame (assuming it was the last in the video file)
- return final_cache.GetFrame(largest_frame_processed);
+ return frame;
}
else {
// The largest processed frame is no longer in cache, return a blank frame
@@ -1394,9 +1396,10 @@ tr1::shared_ptr FFmpegReader::CreateFrame(int requested_frame)
{
tr1::shared_ptr output;
// Check working cache
- if (working_cache.Exists(requested_frame))
+ tr1::shared_ptr frame = working_cache.GetFrame(requested_frame);
+ if (frame)
// Return existing frame
- output = working_cache.GetFrame(requested_frame);
+ output = frame;
else
{
// Create a new frame on the working cache
diff --git a/src/Frame.cpp b/src/Frame.cpp
index 5d8920fc..1f008999 100644
--- a/src/Frame.cpp
+++ b/src/Frame.cpp
@@ -407,6 +407,11 @@ int64 Frame::GetBytes()
// Get pixel data (as packets)
const unsigned char* Frame::GetPixels()
{
+ // Check for blank image
+ if (!image)
+ // Fill with black
+ AddColor(width, height, "#000000");
+
// Return array of pixel packets
return image->bits();
}
@@ -728,30 +733,33 @@ void Frame::AddImage(tr1::shared_ptr new_image, bool only_odd_lines)
return;
// Check for blank source image
- if (!image)
+ if (!image) {
// Replace the blank source image
AddImage(new_image);
- // Ignore image of different sizes or formats
- if (image == new_image || image->size() != image->size() || image->format() != image->format())
- return;
+ } else {
- // Get the frame's image
- const unsigned char* pixels = image->bits();
- const unsigned char* new_pixels = new_image->bits();
+ // Ignore image of different sizes or formats
+ if (image == new_image || image->size() != image->size() || image->format() != image->format())
+ return;
- // Loop through the scanlines of the image (even or odd)
- int start = 0;
- if (only_odd_lines)
- start = 1;
- for (int row = start; row < image->height(); row += 2) {
- memcpy((unsigned char*)pixels, new_pixels + (row * image->bytesPerLine()), image->bytesPerLine());
- new_pixels += image->bytesPerLine();
+ // Get the frame's image
+ const unsigned char *pixels = image->bits();
+ const unsigned char *new_pixels = new_image->bits();
+
+ // Loop through the scanlines of the image (even or odd)
+ int start = 0;
+ if (only_odd_lines)
+ start = 1;
+ for (int row = start; row < image->height(); row += 2) {
+ memcpy((unsigned char *) pixels, new_pixels + (row * image->bytesPerLine()), image->bytesPerLine());
+ new_pixels += image->bytesPerLine();
+ }
+
+ // Update height and width
+ width = image->width();
+ height = image->height();
}
-
- // Update height and width
- width = image->width();
- height = image->height();
}
diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp
index 576f8b2a..4eda35e3 100644
--- a/src/FrameMapper.cpp
+++ b/src/FrameMapper.cpp
@@ -302,8 +302,8 @@ MappedFrame FrameMapper::GetMappedFrame(int TargetFrameNumber) throw(OutOfBounds
tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderClosed)
{
// Check final cache, and just return the frame (if it's available)
- if (final_cache.Exists(requested_frame))
- return final_cache.GetFrame(requested_frame);
+ tr1::shared_ptr final_frame = final_cache.GetFrame(requested_frame);
+ if (final_frame) return final_frame;
// Create a scoped lock, allowing only a single thread to run the following code at one time
const GenericScopedLock lock(getFrameCriticalSection);
@@ -314,8 +314,8 @@ tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderCl
Init();
// Check final cache a 2nd time (due to potential lock already generating this frame)
- if (final_cache.Exists(requested_frame))
- return final_cache.GetFrame(requested_frame);
+ final_frame = final_cache.GetFrame(requested_frame);
+ if (final_frame) return final_frame;
// Minimum number of frames to process (for performance reasons)
int minimum_frames = OPEN_MP_NUM_PROCESSORS;
@@ -343,16 +343,21 @@ tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderCl
int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, target, mapped_frame->SampleRate(), channels_in_frame);
// Create a new frame
- tr1::shared_ptr frame(new Frame(frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame));
+ tr1::shared_ptr frame = tr1::shared_ptr(new Frame(frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame));
frame->SampleRate(mapped_frame->SampleRate());
frame->ChannelsLayout(mapped_frame->ChannelsLayout());
// Copy the image from the odd field
- frame->AddImage(tr1::shared_ptr(new QImage(*reader->GetFrame(mapped.Odd.Frame)->GetImage())), true);
- if (mapped.Odd.Frame != mapped.Even.Frame)
+ tr1::shared_ptr odd_frame = reader->GetFrame(mapped.Odd.Frame);
+ if (odd_frame)
+ frame->AddImage(tr1::shared_ptr(new QImage(*odd_frame->GetImage())), true);
+ if (mapped.Odd.Frame != mapped.Even.Frame) {
// Add even lines (if different than the previous image)
- frame->AddImage(tr1::shared_ptr(new QImage(*reader->GetFrame(mapped.Even.Frame)->GetImage())), false);
+ tr1::shared_ptr even_frame = reader->GetFrame(mapped.Even.Frame);
+ if (even_frame)
+ frame->AddImage(tr1::shared_ptr(new QImage(*even_frame->GetImage())), false);
+ }
// Copy the samples
diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp
index 6891ee45..1c7dafa8 100644
--- a/src/Qt/VideoCacheThread.cpp
+++ b/src/Qt/VideoCacheThread.cpp
@@ -87,8 +87,17 @@ namespace openshot
while ((position - current_display_frame) < max_frames)
{
// Only cache up till the max_frames amount... then sleep
- if (reader)
- reader->GetFrame(position);
+ try
+ {
+ if (reader)
+ // Force the frame to be generated
+ reader->GetFrame(position);
+
+ }
+ catch (const OutOfBoundsFrame & e)
+ {
+ // Ignore out of bounds frame exceptions
+ }
// Increment frame number
position++;
diff --git a/src/Timeline.cpp b/src/Timeline.cpp
index ff6cadb7..b4937f1c 100644
--- a/src/Timeline.cpp
+++ b/src/Timeline.cpp
@@ -551,7 +551,7 @@ bool Timeline::isEqual(double a, double b)
}
// Get an openshot::Frame object for a specific frame number of this reader.
-tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClosed)
+tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClosed, OutOfBoundsFrame)
{
// Check for open reader (or throw exception)
if (!is_open)
@@ -562,12 +562,13 @@ tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClose
requested_frame = 1;
// Check cache
- if (final_cache.Exists(requested_frame)) {
+ tr1::shared_ptr frame = final_cache.GetFrame(requested_frame);
+ if (frame) {
// Debug output
AppendDebugMethod("Timeline::GetFrame (Cached frame found)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
// Return cached frame
- return final_cache.GetFrame(requested_frame);
+ return frame;
}
else
{
@@ -575,12 +576,13 @@ tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClose
const GenericScopedLock lock(getFrameCriticalSection);
// Check cache again (due to locking)
- if (final_cache.Exists(requested_frame)) {
+ frame = final_cache.GetFrame(requested_frame);
+ if (frame) {
// Debug output
AppendDebugMethod("Timeline::GetFrame (Cached frame found on 2nd look)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);
// Return cached frame
- return final_cache.GetFrame(requested_frame);
+ return frame;
}
// Minimum number of frames to process (for performance reasons)
diff --git a/tests/Cache_Tests.cpp b/tests/Cache_Tests.cpp
index a678c17a..fdc51770 100644
--- a/tests/Cache_Tests.cpp
+++ b/tests/Cache_Tests.cpp
@@ -78,13 +78,13 @@ TEST(Cache_Max_Bytes_Constructor)
CHECK_EQUAL(20, c.Count());
// Check which items the cache kept
- CHECK_EQUAL(true, c.Exists(1));
- CHECK_EQUAL(true, c.Exists(10));
- CHECK_EQUAL(true, c.Exists(11));
- CHECK_EQUAL(true, c.Exists(19));
- CHECK_EQUAL(true, c.Exists(20));
- CHECK_EQUAL(false, c.Exists(21));
- CHECK_EQUAL(false, c.Exists(30));
+ CHECK_EQUAL(true, c.GetFrame(1) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(10) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(11) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(19) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(20) != NULL);
+ CHECK_EQUAL(false, c.GetFrame(21) != NULL);
+ CHECK_EQUAL(false, c.GetFrame(30) != NULL);
}
TEST(Cache_Clear)
@@ -141,13 +141,13 @@ TEST(Cache_Check_If_Frame_Exists)
}
// Check if certain frames exists (only 1-5 exist)
- CHECK_EQUAL(false, c.Exists(0));
- CHECK_EQUAL(true, c.Exists(1));
- CHECK_EQUAL(true, c.Exists(2));
- CHECK_EQUAL(true, c.Exists(3));
- CHECK_EQUAL(true, c.Exists(4));
- CHECK_EQUAL(true, c.Exists(5));
- CHECK_EQUAL(false, c.Exists(6));
+ CHECK_EQUAL(false, c.GetFrame(0) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(1) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(2) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(3) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(4) != NULL);
+ CHECK_EQUAL(true, c.GetFrame(5) != NULL);
+ CHECK_EQUAL(false, c.GetFrame(6) != NULL);
}
TEST(Cache_GetFrame)
@@ -166,8 +166,8 @@ TEST(Cache_GetFrame)
c.Add(green->number, tr1::shared_ptr(green));
// Get frames
- CHECK_THROW(c.GetFrame(0), OutOfBoundsFrame);
- CHECK_THROW(c.GetFrame(4), OutOfBoundsFrame);
+ CHECK_EQUAL(true, c.GetFrame(0) == NULL);
+ CHECK_EQUAL(true, c.GetFrame(4) == NULL);
// Check if certain frames exists (only 1-5 exist)
CHECK_EQUAL(1, c.GetFrame(1)->number);
@@ -222,13 +222,13 @@ TEST(Cache_Remove)
CHECK_EQUAL(3, c.Count());
// Check if frame 2 exists
- CHECK_EQUAL(true, c.Exists(2));
+ CHECK_EQUAL(true, c.GetFrame(2) != NULL);
// Remove frame 2
c.Remove(2);
// Check if frame 2 exists
- CHECK_EQUAL(false, c.Exists(2));
+ CHECK_EQUAL(false, c.GetFrame(2) != NULL);
// Check if count is 2
CHECK_EQUAL(2, c.Count());
@@ -237,7 +237,7 @@ TEST(Cache_Remove)
c.Remove(1);
// Check if frame 1 exists
- CHECK_EQUAL(false, c.Exists(1));
+ CHECK_EQUAL(false, c.GetFrame(1) != NULL);
// Check if count is 1
CHECK_EQUAL(1, c.Count());