Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding some new functionality and documentation to DummyReader #527

Merged
merged 3 commits into from
Jun 7, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 49 additions & 1 deletion include/DummyReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,58 @@
namespace openshot
{
/**
* @brief This class is used as a simple, dummy reader, which always returns a blank frame.
* @brief This class is used as a simple, dummy reader, which can be very useful when writing
* unit tests. It can return a single blank frame or it can return custom frame objects
* which were added using the WriteFrame() method.
*
* A dummy reader can be created with any framerate or samplerate. This is useful in unit
* tests that need to test different framerates or samplerates.
*
* @code
* // Create a reader (Fraction fps, int width, int height, int sample_rate, int channels, float duration)
* openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
* r.Open(); // Open the reader
*
* // Get a frame (which will be blank, since we haven't added any frames yet)
* std::shared_ptr<openshot::Frame> f = r.GetFrame(1);
*
* // Now let's create some test frames
* for (int64_t frame_number = 1; frame_number <= 30; frame_number++)
* {
* // Create blank frame (with specific frame #, samples, and channels)
* // Sample count should be 44100 / 30 fps = 1470 samples per frame
* int sample_count = 1470;
* std::shared_ptr<openshot::Frame> f(new openshot::Frame(frame_number, sample_count, 2));
*
* // Create test samples with incrementing value
* float *audio_buffer = new float[sample_count];
* for (int64_t sample_number = 0; sample_number < sample_count; sample_number++)
* {
* // Generate an incrementing audio sample value (just as an example)
* audio_buffer[sample_number] = float(frame_number) + (float(sample_number) / float(sample_count));
* }
*
* // Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source,
* // int numSamples, float gainToApplyToSource = 1.0f)
* f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1
* f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2
*
* // Write test frame to dummy reader
* r.WriteFrame(f);
* }
*
* // Now let's verify our DummyReader works
* std::shared_ptr<openshot::Frame> f = r.GetFrame(1);
* // r.GetFrame(1)->GetAudioSamples(0)[1] should equal 1.00068033 based on our above calculations
*
* // Close the reader
* r.Close();
* @endcode
*/
class DummyReader : public ReaderBase
{
private:
CacheMemory dummy_cache;
std::shared_ptr<openshot::Frame> image_frame;
bool is_open;

Expand Down Expand Up @@ -94,6 +138,10 @@ namespace openshot

/// Open File - which is called by the constructor automatically
void Open() override;

/// @brief Add a frame to the dummy reader. This is useful when constructing unit tests that require custom frames.
/// @param frame The openshot::Frame object to write to this image
void WriteFrame(std::shared_ptr<openshot::Frame> frame);
};

}
Expand Down
31 changes: 28 additions & 3 deletions src/DummyReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,24 +99,49 @@ void DummyReader::Close()
{
// Mark as "closed"
is_open = false;

// Clear cache
dummy_cache.Clear();
}
}

// Add Frame objects to DummyReader
void DummyReader::WriteFrame(std::shared_ptr<openshot::Frame> frame)
{
if (frame) {
dummy_cache.Add(frame);
}
}

// Get an openshot::Frame object for a specific frame number of this reader.
// Get an openshot::Frame object for a specific frame number of this reader. It is either a blank frame
// or a custom frame added with the WriteFrame() method.
std::shared_ptr<Frame> DummyReader::GetFrame(int64_t requested_frame)
{
// Check for open reader (or throw exception)
if (!is_open)
throw ReaderClosed("The ImageReader is closed. Call Open() before calling this method.", "dummy");

if (image_frame)
{
if (dummy_cache.Count() == 0 && image_frame) {
// Create a scoped lock, allowing only a single thread to run the following code at one time
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);

// Always return same frame (regardless of which frame number was requested)
image_frame->number = requested_frame;
return image_frame;

} else if (dummy_cache.Count() > 0) {
// Create a scoped lock, allowing only a single thread to run the following code at one time
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);

// Get a frame from the dummy cache
std::shared_ptr<openshot::Frame> f = dummy_cache.GetFrame(requested_frame);
if (f) {
// return frame from cache (if found)
return f;
} else {
// No cached frame found
throw InvalidFile("Requested frame not found. You can only access Frame numbers added with WriteFrame().", "dummy");
}
}
else
// no frame loaded
Expand Down
5 changes: 5 additions & 0 deletions src/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ const unsigned char* Frame::GetPixels()
// Get pixel data (for only a single scan-line)
const unsigned char* Frame::GetPixels(int row)
{
// Check for blank image
jonoomph marked this conversation as resolved.
Show resolved Hide resolved
if (!image)
// Fill with black
AddColor(width, height, color);

// Return array of pixel packets
return image->constScanLine(row);
}
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ set(OPENSHOT_TEST_FILES
Clip_Tests.cpp
Color_Tests.cpp
Coordinate_Tests.cpp
DummyReader_Tests.cpp
ReaderBase_Tests.cpp
ImageWriter_Tests.cpp
FFmpegReader_Tests.cpp
Expand Down
102 changes: 102 additions & 0 deletions tests/DummyReader_Tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @file
* @brief Unit tests for openshot::DummyReader
* @author Jonathan Thomas <jonathan@openshot.org>
*
* @ref License
*/

/* LICENSE
*
* Copyright (c) 2008-2019 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/>.
*/

#include "UnitTest++.h"
// Prevent name clashes with juce::UnitTest
#define DONT_SET_USING_JUCE_NAMESPACE 1

#include "../include/OpenShot.h"

using namespace std;
using namespace openshot;

TEST (DummyReader_Constructor) {
// Create a default fraction (should be 1/1)
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
r.Open(); // Open the reader

// Check values
CHECK_EQUAL(1920, r.info.width);
CHECK_EQUAL(1080, r.info.height);
CHECK_EQUAL(30, r.info.fps.num);
CHECK_EQUAL(1, r.info.fps.den);
CHECK_EQUAL(44100, r.info.sample_rate);
CHECK_EQUAL(2, r.info.channels);
CHECK_EQUAL(30.0, r.info.duration);
}

TEST (DummyReader_Blank_Frame) {
// Create a default fraction (should be 1/1)
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
r.Open(); // Open the reader

// Get a blank frame (because we have not added any frames using WriteFrame() yet)
// Check values
CHECK_EQUAL(1, r.GetFrame(1)->number);
CHECK_EQUAL(1, r.GetFrame(1)->GetPixels(700)[700] == 0); // black pixel
CHECK_EQUAL(1, r.GetFrame(1)->GetPixels(701)[701] == 0); // black pixel
}

TEST (DummyReader_Fake_Frame) {
// Create a default fraction (should be 1/1)
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
r.Open(); // Open the reader

// Let's create some test frames
jonoomph marked this conversation as resolved.
Show resolved Hide resolved
for (int64_t frame_number = 1; frame_number <= 30; frame_number++) {
// Create blank frame (with specific frame #, samples, and channels)
// Sample count should be 44100 / 30 fps = 1470 samples per frame
int sample_count = 1470;
std::shared_ptr<openshot::Frame> f(new openshot::Frame(frame_number, sample_count, 2));

// Create test samples with incrementing value
float *audio_buffer = new float[sample_count];
for (int64_t sample_number = 0; sample_number < sample_count; sample_number++) {
// Generate an incrementing audio sample value (just as an example)
audio_buffer[sample_number] = float(frame_number) + (float(sample_number) / float(sample_count));
}

// Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source,
f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1
f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2

// Write test frame to dummy reader
r.WriteFrame(f);
}

// Verify our artificial audio sample data is correct
CHECK_EQUAL(1, r.GetFrame(1)->number);
CHECK_EQUAL(1, r.GetFrame(1)->GetAudioSamples(0)[0]);
CHECK_CLOSE(1.00068033, r.GetFrame(1)->GetAudioSamples(0)[1], 0.00001);
CHECK_CLOSE(1.00136054, r.GetFrame(1)->GetAudioSamples(0)[2], 0.00001);
CHECK_EQUAL(2, r.GetFrame(2)->GetAudioSamples(0)[0]);
CHECK_CLOSE(2.00068033, r.GetFrame(2)->GetAudioSamples(0)[1], 0.00001);
CHECK_CLOSE(2.00136054, r.GetFrame(2)->GetAudioSamples(0)[2], 0.00001);
}