Skip to content
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