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());