Skip to content

Commit 7831cfe

Browse files
committed
Adding some new functionality and documentation to DummyReader. Adding the ability to add test frames, with fake image and audio data. This will can be used in unittests, and will soon be used to verify some new audio improvements (coming soon).
1 parent 2834e77 commit 7831cfe

File tree

5 files changed

+185
-4
lines changed

5 files changed

+185
-4
lines changed

include/DummyReader.h

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,58 @@
4646
namespace openshot
4747
{
4848
/**
49-
* @brief This class is used as a simple, dummy reader, which always returns a blank frame.
49+
* @brief This class is used as a simple, dummy reader, which can be very useful when writing
50+
* unit tests. It can return a single blank frame or it can return custom frame objects
51+
* which were added using the WriteFrame() method.
5052
*
5153
* A dummy reader can be created with any framerate or samplerate. This is useful in unit
5254
* tests that need to test different framerates or samplerates.
55+
*
56+
* @code
57+
* // Create a reader (Fraction fps, int width, int height, int sample_rate, int channels, float duration)
58+
* openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
59+
* r.Open(); // Open the reader
60+
*
61+
* // Get a frame (which will be blank, since we haven't added any frames yet)
62+
* std::shared_ptr<openshot::Frame> f = r.GetFrame(1);
63+
*
64+
* // Now let's create some test frames
65+
* for (int64_t frame_number = 1; frame_number <= 30; frame_number++)
66+
* {
67+
* // Create blank frame (with specific frame #, samples, and channels)
68+
* // Sample count should be 44100 / 30 fps = 1470 samples per frame
69+
* int sample_count = 1470;
70+
* std::shared_ptr<openshot::Frame> f(new openshot::Frame(frame_number, sample_count, 2));
71+
*
72+
* // Create test samples with incrementing value
73+
* float *audio_buffer = new float[sample_count];
74+
* for (int64_t sample_number = 0; sample_number < sample_count; sample_number++)
75+
* {
76+
* // Generate an incrementing audio sample value (just as an example)
77+
* audio_buffer[sample_number] = float(frame_number) + (float(sample_number) / float(sample_count));
78+
* }
79+
*
80+
* // Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source,
81+
* // int numSamples, float gainToApplyToSource = 1.0f)
82+
* f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1
83+
* f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2
84+
*
85+
* // Write test frame to dummy reader
86+
* r.WriteFrame(f);
87+
* }
88+
*
89+
* // Now let's verify our DummyReader works
90+
* std::shared_ptr<openshot::Frame> f = r.GetFrame(1);
91+
* // r.GetFrame(1)->GetAudioSamples(0)[1] should equal 1.00068033 based on our above calculations
92+
*
93+
* // Close the reader
94+
* r.Close();
95+
* @endcode
5396
*/
5497
class DummyReader : public ReaderBase
5598
{
5699
private:
100+
CacheMemory dummy_cache;
57101
std::shared_ptr<openshot::Frame> image_frame;
58102
bool is_open;
59103

@@ -94,6 +138,10 @@ namespace openshot
94138

95139
/// Open File - which is called by the constructor automatically
96140
void Open() override;
141+
142+
/// @brief Add a frame to the dummy reader. This is useful when constructing unit tests that require custom frames.
143+
/// @param frame The openshot::Frame object to write to this image
144+
void WriteFrame(std::shared_ptr<openshot::Frame> frame);
97145
};
98146

99147
}

src/DummyReader.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,49 @@ void DummyReader::Close()
9999
{
100100
// Mark as "closed"
101101
is_open = false;
102+
103+
// Clear cache
104+
dummy_cache.Clear();
105+
}
106+
}
107+
108+
// Add Frame objects to DummyReader
109+
void DummyReader::WriteFrame(std::shared_ptr<openshot::Frame> frame)
110+
{
111+
if (frame) {
112+
dummy_cache.Add(frame);
102113
}
103114
}
104115

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

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

117128
// Always return same frame (regardless of which frame number was requested)
118129
image_frame->number = requested_frame;
119130
return image_frame;
131+
132+
} else if (dummy_cache.Count() > 0) {
133+
// Create a scoped lock, allowing only a single thread to run the following code at one time
134+
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
135+
136+
// Get a frame from the dummy cache
137+
std::shared_ptr<openshot::Frame> f = dummy_cache.GetFrame(requested_frame);
138+
if (f) {
139+
// return frame from cache (if found)
140+
return f;
141+
} else {
142+
// No cached frame found
143+
throw InvalidFile("Requested frame not found. You can only access Frame numbers added with WriteFrame().", "dummy");
144+
}
120145
}
121146
else
122147
// no frame loaded

src/Frame.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,11 @@ const unsigned char* Frame::GetPixels()
480480
// Get pixel data (for only a single scan-line)
481481
const unsigned char* Frame::GetPixels(int row)
482482
{
483+
// Check for blank image
484+
if (!image)
485+
// Fill with black
486+
AddColor(width, height, color);
487+
483488
// Return array of pixel packets
484489
return image->constScanLine(row);
485490
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ set(OPENSHOT_TEST_FILES
109109
Clip_Tests.cpp
110110
Color_Tests.cpp
111111
Coordinate_Tests.cpp
112+
DummyReader_Tests.cpp
112113
ReaderBase_Tests.cpp
113114
ImageWriter_Tests.cpp
114115
FFmpegReader_Tests.cpp

tests/DummyReader_Tests.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @file
3+
* @brief Unit tests for openshot::DummyReader
4+
* @author Jonathan Thomas <[email protected]>
5+
*
6+
* @ref License
7+
*/
8+
9+
/* LICENSE
10+
*
11+
* Copyright (c) 2008-2019 OpenShot Studios, LLC
12+
* <http://www.openshotstudios.com/>. This file is part of
13+
* OpenShot Library (libopenshot), an open-source project dedicated to
14+
* delivering high quality video editing and animation solutions to the
15+
* world. For more information visit <http://www.openshot.org/>.
16+
*
17+
* OpenShot Library (libopenshot) is free software: you can redistribute it
18+
* and/or modify it under the terms of the GNU Lesser General Public License
19+
* as published by the Free Software Foundation, either version 3 of the
20+
* License, or (at your option) any later version.
21+
*
22+
* OpenShot Library (libopenshot) is distributed in the hope that it will be
23+
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+
* GNU Lesser General Public License for more details.
26+
*
27+
* You should have received a copy of the GNU Lesser General Public License
28+
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29+
*/
30+
31+
#include "UnitTest++.h"
32+
// Prevent name clashes with juce::UnitTest
33+
#define DONT_SET_USING_JUCE_NAMESPACE 1
34+
35+
#include "../include/OpenShot.h"
36+
37+
using namespace std;
38+
using namespace openshot;
39+
40+
TEST (DummyReader_Constructor) {
41+
// Create a default fraction (should be 1/1)
42+
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
43+
r.Open(); // Open the reader
44+
45+
// Check values
46+
CHECK_EQUAL(1920, r.info.width);
47+
CHECK_EQUAL(1080, r.info.height);
48+
CHECK_EQUAL(30, r.info.fps.num);
49+
CHECK_EQUAL(1, r.info.fps.den);
50+
CHECK_EQUAL(44100, r.info.sample_rate);
51+
CHECK_EQUAL(2, r.info.channels);
52+
CHECK_EQUAL(30.0, r.info.duration);
53+
}
54+
55+
TEST (DummyReader_Blank_Frame) {
56+
// Create a default fraction (should be 1/1)
57+
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
58+
r.Open(); // Open the reader
59+
60+
// Get a blank frame (because we have not added any frames using WriteFrame() yet)
61+
// Check values
62+
CHECK_EQUAL(1, r.GetFrame(1)->number);
63+
CHECK_EQUAL(1, r.GetFrame(1)->GetPixels(700)[700] == 0); // black pixel
64+
CHECK_EQUAL(1, r.GetFrame(1)->GetPixels(701)[701] == 0); // black pixel
65+
}
66+
67+
TEST (DummyReader_Fake_Frame) {
68+
// Create a default fraction (should be 1/1)
69+
openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0);
70+
r.Open(); // Open the reader
71+
72+
// Let's create some test frames
73+
for (int64_t frame_number = 1; frame_number <= 30; frame_number++) {
74+
// Create blank frame (with specific frame #, samples, and channels)
75+
// Sample count should be 44100 / 30 fps = 1470 samples per frame
76+
int sample_count = 1470;
77+
std::shared_ptr<openshot::Frame> f(new openshot::Frame(frame_number, sample_count, 2));
78+
79+
// Create test samples with incrementing value
80+
float *audio_buffer = new float[sample_count];
81+
for (int64_t sample_number = 0; sample_number < sample_count; sample_number++) {
82+
// Generate an incrementing audio sample value (just as an example)
83+
audio_buffer[sample_number] = float(frame_number) + (float(sample_number) / float(sample_count));
84+
}
85+
86+
// Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source,
87+
f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1
88+
f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2
89+
90+
// Write test frame to dummy reader
91+
r.WriteFrame(f);
92+
}
93+
94+
// Verify our artificial audio sample data is correct
95+
CHECK_EQUAL(1, r.GetFrame(1)->number);
96+
CHECK_EQUAL(1, r.GetFrame(1)->GetAudioSamples(0)[0]);
97+
CHECK_CLOSE(1.00068033, r.GetFrame(1)->GetAudioSamples(0)[1], 0.00001);
98+
CHECK_CLOSE(1.00136054, r.GetFrame(1)->GetAudioSamples(0)[2], 0.00001);
99+
CHECK_EQUAL(2, r.GetFrame(2)->GetAudioSamples(0)[0]);
100+
CHECK_CLOSE(2.00068033, r.GetFrame(2)->GetAudioSamples(0)[1], 0.00001);
101+
CHECK_CLOSE(2.00136054, r.GetFrame(2)->GetAudioSamples(0)[2], 0.00001);
102+
}

0 commit comments

Comments
 (0)