Skip to content

reader: process with a generator #239

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

Merged
merged 10 commits into from
Apr 1, 2016
Merged
6 changes: 4 additions & 2 deletions src/AvTranscoder/data/decoded/Frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ class AvExport Frame
int* getLineSize() const { return _frame->linesize; }

/**
* @brief Copy the data of the given Frame.
*/
* @brief Copy the data of the given Frame.
* @note This function does not allocate anything: the current frame must be already initialized and
* allocated with the same parameters as the given frame, to be ready for memcpy instructions.
*/
void copyData(const Frame& frameToRef);

/**
Expand Down
19 changes: 18 additions & 1 deletion src/AvTranscoder/decoder/AudioGenerator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "AudioGenerator.hpp"

#include <AvTranscoder/util.hpp>

#include <sstream>

namespace avtranscoder
{

Expand Down Expand Up @@ -42,13 +46,26 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
{
AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);
_silent = new AudioFrame(audioBuffer.desc());

std::stringstream msg;
msg << "Generate a silence with the following features:" << std::endl;
msg << "sample rate = " << _silent->getSampleRate() << std::endl;
msg << "number of channels = " << _silent->getNbChannels() << std::endl;
msg << "sample format = " << getSampleFormatName(_silent->getSampleFormat()) << std::endl;
msg << "number of samples per channel = " << _silent->getNbSamplesPerChannel() << std::endl;
LOG_INFO(msg.str())

// Set the number of samples of silence to the number of samples of the given frame
// (which was allocated to expect this number of samples).
_silent->setNbSamplesPerChannel(frameBuffer.getAVFrame().nb_samples);
}
frameBuffer.getAVFrame().nb_samples = _silent->getAVFrame().nb_samples;
LOG_DEBUG("Copy data of the silence when decode next frame")
frameBuffer.copyData(*_silent);
}
// Take audio frame from _inputFrame
else
{
LOG_DEBUG("Copy data of the audio specified when decode next frame")
frameBuffer.copyData(*_inputFrame);
}
return true;
Expand Down
11 changes: 11 additions & 0 deletions src/AvTranscoder/decoder/VideoGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <AvTranscoder/util.hpp>
#include <AvTranscoder/transform/VideoTransform.hpp>

#include <sstream>

namespace avtranscoder
{

Expand Down Expand Up @@ -51,6 +53,13 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
// Generate the black image only once
if(!_blackImage)
{
std::stringstream msg;
msg << "Generate a black image with the following features:" << std::endl;
msg << "width = " << _frameDesc._width << std::endl;
msg << "height = " << _frameDesc._height << std::endl;
msg << "pixel format = rgb24" << std::endl;
LOG_INFO(msg.str())

VideoFrame& imageBuffer = static_cast<VideoFrame&>(frameBuffer);

// Input of convert
Expand All @@ -68,11 +77,13 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
VideoTransform videoTransform;
videoTransform.convert(intermediateBuffer, *_blackImage);
}
LOG_DEBUG("Copy data of the black image when decode next frame")
frameBuffer.copyData(*_blackImage);
}
// Take image from _inputFrame
else
{
LOG_DEBUG("Copy data of the image specified when decode next frame")
frameBuffer.copyData(*_inputFrame);
}
return true;
Expand Down
5 changes: 5 additions & 0 deletions src/AvTranscoder/reader/AudioReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <AvTranscoder/util.hpp>
#include <AvTranscoder/decoder/AudioDecoder.hpp>
#include <AvTranscoder/decoder/AudioGenerator.hpp>
#include <AvTranscoder/data/decoded/AudioFrame.hpp>
#include <AvTranscoder/transform/AudioTransform.hpp>
#include <AvTranscoder/progress/NoDisplayProgress.hpp>
Expand Down Expand Up @@ -42,6 +43,9 @@ void AudioReader::init()
_decoder = new AudioDecoder(_inputFile->getStream(_streamIndex));
_decoder->setupDecoder();

// generator
_generator = new AudioGenerator();

// create transform
_transform = new AudioTransform();

Expand All @@ -57,6 +61,7 @@ void AudioReader::init()
AudioReader::~AudioReader()
{
delete _decoder;
delete _generator;
delete _srcFrame;
delete _dstFrame;
delete _transform;
Expand Down
16 changes: 14 additions & 2 deletions src/AvTranscoder/reader/IReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ IReader::IReader(const std::string& filename, const size_t streamIndex, const in
, _channelIndex(channelIndex)
, _currentFrame(-1)
, _inputFileAllocated(true)
, _continueWithGenerator(false)
{
_inputFile = new InputFile(filename);
}
Expand All @@ -31,6 +32,7 @@ IReader::IReader(InputFile& inputFile, const size_t streamIndex, const int chann
, _channelIndex(channelIndex)
, _currentFrame(-1)
, _inputFileAllocated(false)
, _continueWithGenerator(false)
{
}

Expand All @@ -53,6 +55,7 @@ Frame* IReader::readPrevFrame()
Frame* IReader::readFrameAt(const size_t frame)
{
assert(_decoder != NULL);
assert(_generator != NULL);
assert(_transform != NULL);
assert(_srcFrame != NULL);
assert(_dstFrame != NULL);
Expand All @@ -70,10 +73,19 @@ Frame* IReader::readFrameAt(const size_t frame)
decodingStatus = _decoder->decodeNextFrame(*_srcFrame, _channelIndex);
else
decodingStatus = _decoder->decodeNextFrame(*_srcFrame);
// if decoding failed
if(!decodingStatus)
{
_dstFrame->clear();
return _dstFrame;
// generate data (ie silence or black)
if(_continueWithGenerator)
{
_generator->decodeNextFrame(*_srcFrame);
}
// or return NULL
else
{
return NULL;
}
}
// transform
_transform->convert(*_srcFrame, *_dstFrame);
Expand Down
12 changes: 12 additions & 0 deletions src/AvTranscoder/reader/IReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,20 @@ class AvExport IReader

/**
* @return Get next frame after decoding
* @see readFrameAt
*/
Frame* readNextFrame();

/**
* @return Get previous frame after decoding
* @see readFrameAt
*/
Frame* readPrevFrame();

/**
* @return Get indicated frame after decoding
* @warn Returns NULL if there is no more frame to read.
* @see continueWithGenerator
*/
Frame* readFrameAt(const size_t frame);

Expand All @@ -53,10 +57,17 @@ class AvExport IReader
*/
const StreamProperties* getSourceProperties() const { return _streamProperties; }

/**
* @brief Set the reader state to generate data (ie silence or black) when there is no more data to decode.
* @note By default, the reader returns an empty frame.
*/
void continueWithGenerator(const bool continueWithGenerator = true) { _continueWithGenerator = continueWithGenerator; }

protected:
InputFile* _inputFile;
const StreamProperties* _streamProperties;
IDecoder* _decoder;
IDecoder* _generator;

Frame* _srcFrame;
Frame* _dstFrame;
Expand All @@ -69,6 +80,7 @@ class AvExport IReader
private:
int _currentFrame; ///< The current decoded frame.
bool _inputFileAllocated; ///< Does the InputFile is held by the class or not (depends on the constructor called)
bool _continueWithGenerator; ///< If there is no more data to decode, complete with generated data
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/AvTranscoder/reader/VideoReader.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "VideoReader.hpp"

#include <AvTranscoder/decoder/VideoDecoder.hpp>
#include <AvTranscoder/decoder/VideoGenerator.hpp>
#include <AvTranscoder/data/decoded/VideoFrame.hpp>
#include <AvTranscoder/transform/VideoTransform.hpp>
#include <AvTranscoder/progress/NoDisplayProgress.hpp>
Expand Down Expand Up @@ -41,6 +42,11 @@ void VideoReader::init()
_decoder = new VideoDecoder(_inputFile->getStream(_streamIndex));
_decoder->setupDecoder();

// generator
VideoGenerator* generator = new VideoGenerator();
generator->setVideoFrameDesc(_inputFile->getStream(_streamIndex).getVideoCodec().getVideoFrameDesc());
_generator = generator;

// create transform
_transform = new VideoTransform();

Expand All @@ -56,6 +62,7 @@ void VideoReader::init()
VideoReader::~VideoReader()
{
delete _decoder;
delete _generator;
delete _srcFrame;
delete _dstFrame;
delete _transform;
Expand Down
73 changes: 65 additions & 8 deletions test/pyTest/testReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def testVideoReaderCreateNewInputFile():
# read all frames and check their size
for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()):
frame = av.VideoFrame(reader.readNextFrame())
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * reader.getOutputNbComponents() )
bytesPerPixel = reader.getOutputBitDepth() / 8
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel )

# check if the next frame is empty
frame = av.VideoFrame(reader.readNextFrame())
assert_equals( frame.getSize(), 0 )
# check if there is no next frame
frame = reader.readNextFrame()
assert_equals( reader.readNextFrame(), None )


def testVideoReaderReferenceInputFile():
Expand All @@ -43,11 +44,11 @@ def testVideoReaderReferenceInputFile():
# read all frames and check their size
for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()):
frame = av.VideoFrame(reader.readNextFrame())
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * reader.getOutputNbComponents() )
bytesPerPixel = reader.getOutputBitDepth() / 8
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel )

# check if the next frame is empty
frame = av.VideoFrame(reader.readNextFrame())
assert_equals( frame.getSize(), 0 )
# check if there is no next frame
assert_equals( reader.readNextFrame(), None )


def testAudioReaderChannelsExtraction():
Expand All @@ -74,3 +75,59 @@ def testAudioReaderChannelsExtraction():
sizeOfFrameWithOneChannels = frame.getSize()

assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels )


def testVideoReaderWithGenerator():
"""
Read a video stream with the VideoReader.
When there is no more data to decode, switch to a generator and process some frames.
"""
inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_AVI_FILE']
reader = av.VideoReader(inputFileName)

# read all frames and check their size
for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()):
frame = av.VideoFrame(reader.readNextFrame())
bytesPerPixel = reader.getOutputBitDepth() / 8
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel )

# check if there is no next frame
assert_equals( reader.readNextFrame(), None )

# generate 10 frames of black
reader.continueWithGenerator()
for i in xrange(0, 9):
frame = av.VideoFrame(reader.readNextFrame())
bytesPerPixel = reader.getOutputBitDepth() / 8
assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel )


def testAudioReaderWithGenerator():
"""
Read an audio stream with the AudioReader.
When there is no more data to decode, switch to a generator and process some frames.
"""
inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE']
inputFile = av.InputFile(inputFileName)
reader = av.AudioReader(inputFile)

# read all frames and check their size
while True:
frame = reader.readNextFrame()
if not frame:
break
frame = av.AudioFrame(frame)
nbSamplesPerChannel = frame.getNbSamplesPerChannel()
bytesPerSample = 2
assert_equals( frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample )

# check if there is no next frame
assert_equals( reader.readNextFrame(), None )

# generate 10 frames of silence
reader.continueWithGenerator()
for i in xrange(0, 9):
frame = av.AudioFrame(reader.readNextFrame())
nbSamplesPerChannel = frame.getNbSamplesPerChannel()
bytesPerSample = 2
assert_equals( frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample )
40 changes: 40 additions & 0 deletions test/pyTest/testTranscoderTranscodeAudioWave.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def testTranscodeWave24b48k5_1():
assert_equals( 48000, dst_audioStream.getSampleRate() )
assert_equals( 6, dst_audioStream.getNbChannels() )


def testTranscodeWave24b48kstereo():
"""
Transcode one audio stream (profile wave24b48kstereo).
Expand Down Expand Up @@ -76,6 +77,7 @@ def testTranscodeWave24b48kstereo():
assert_equals( 48000, dst_audioStream.getSampleRate() )
assert_equals( 2, dst_audioStream.getNbChannels() )


def testTranscodeWave24b48kmono():
"""
Transcode one audio stream (profile wave24b48kmono).
Expand Down Expand Up @@ -109,6 +111,7 @@ def testTranscodeWave24b48kmono():
assert_equals( 48000, dst_audioStream.getSampleRate() )
assert_equals( 1, dst_audioStream.getNbChannels() )


def testTranscodeWave16b48kmono():
"""
Transcode one audio stream (profile wave16b48kmono).
Expand Down Expand Up @@ -141,3 +144,40 @@ def testTranscodeWave16b48kmono():
assert_equals( "signed 16 bits", dst_audioStream.getSampleFormatLongName() )
assert_equals( 48000, dst_audioStream.getSampleRate() )
assert_equals( 1, dst_audioStream.getNbChannels() )


def testTranscodeWave16b48kmonoWithSilence():
"""
Transcode one audio stream (profile wave16b48kmono).
Complete with silence.
"""
inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE']
outputFileName = "testTranscodeWave16b48kmonoWithSilence.wav"
outputDuration = 50

ouputFile = av.OutputFile( outputFileName )
transcoder = av.Transcoder( ouputFile )
transcoder.setProcessMethod( av.eProcessMethodBasedOnDuration, 0, outputDuration )

inputFile = av.InputFile( inputFileName )
src_audioStream = inputFile.getProperties().getAudioProperties()[0]
audioStreamIndex = src_audioStream.getStreamIndex()
transcoder.add( inputFileName, audioStreamIndex, "wave16b48kmono" )

progress = av.ConsoleProgress()
processStat = transcoder.process( progress )

# check process stat returned
audioStat = processStat.getAudioStat(0)
assert_almost_equals( audioStat.getDuration(), outputDuration, delta=0.01 )

# get dst file of transcode
dst_inputFile = av.InputFile( outputFileName )
dst_properties = dst_inputFile.getProperties()
dst_audioStream = dst_properties.getAudioProperties()[0]

assert_equals( "pcm_s16le", dst_audioStream.getCodecName() )
assert_equals( "s16", dst_audioStream.getSampleFormatName() )
assert_equals( "signed 16 bits", dst_audioStream.getSampleFormatLongName() )
assert_equals( 48000, dst_audioStream.getSampleRate() )
assert_equals( 1, dst_audioStream.getNbChannels() )
3 changes: 2 additions & 1 deletion tools/travis/python.nosetests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export AVTRANSCODER_TEST_IMAGE_PNG_FILE=`pwd`/avTranscoder-data/image/BigBuckBun
export AVTRANSCODER_TEST_IMAGE_JPG_FILE=`pwd`/avTranscoder-data/image/BigBuckBunny/title_anouncement.thumbnail.jpg

# Launch tests
nosetests ${TRAVIS_BUILD_DIR}/test/pyTest --with-coverage
nosetests ${TRAVIS_BUILD_DIR}/test/pyTest --with-coverage > progress.txt