Skip to content

Reader: enable process with a generator #238

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

Closed
7 changes: 1 addition & 6 deletions src/AvTranscoder/data/decoded/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Frame::~Frame()
}
}

void Frame::copyData(const Frame& frameToRef)
void Frame::refData(const Frame& frameToRef)
{
const int ret = av_frame_copy(_frame, &frameToRef.getAVFrame());
if(ret < 0)
Expand All @@ -63,11 +63,6 @@ void Frame::refFrame(const Frame& otherFrame)
}
}

void Frame::clear()
{
av_frame_unref(_frame);
}

void Frame::allocateAVFrame()
{
#if LIBAVCODEC_VERSION_MAJOR > 54
Expand Down
13 changes: 5 additions & 8 deletions src/AvTranscoder/data/decoded/Frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ class AvExport Frame
int* getLineSize() const { return _frame->linesize; }

/**
* @brief Copy the data of the given Frame.
*/
void copyData(const Frame& frameToRef);
* @brief Ref to 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 frameToRef.
*/
void refData(const Frame& frameToRef);

/**
* @brief Copy all the fields that do not affect the data layout in the buffers.
Expand All @@ -58,11 +60,6 @@ class AvExport Frame
*/
void refFrame(const Frame& otherFrame);

/**
* @brief Unreference all the buffers referenced by frame and reset the frame fields.
*/
void clear();

/**
* @return If it corresponds to a valid audio frame.
* @see AudioFrame
Expand Down
18 changes: 16 additions & 2 deletions 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 @@ -41,15 +45,25 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
if(!_silent)
{
AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);

std::stringstream msg;
msg << "Generate a silence with the following features:" << std::endl;
msg << "sample rate = " << audioBuffer.desc()._sampleRate << std::endl;
msg << "number of channels = " << audioBuffer.desc()._nbChannels << std::endl;
msg << "sample format = " << getSampleFormatName(audioBuffer.desc()._sampleFormat) << std::endl;
LOG_INFO(msg.str())

_silent = new AudioFrame(audioBuffer.desc());
}
LOG_INFO("Reference the silence when decode next frame")
frameBuffer.getAVFrame().nb_samples = _silent->getAVFrame().nb_samples;
frameBuffer.copyData(*_silent);
dynamic_cast<AudioFrame&>(frameBuffer).assign(_silent->getData()[0]);
}
// Take audio frame from _inputFrame
else
{
frameBuffer.copyData(*_inputFrame);
LOG_INFO("Reference the audio data specified when decode next frame")
frameBuffer.refData(*_inputFrame);
}
return true;
}
Expand Down
15 changes: 13 additions & 2 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,12 +77,14 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
VideoTransform videoTransform;
videoTransform.convert(intermediateBuffer, *_blackImage);
}
frameBuffer.copyData(*_blackImage);
LOG_INFO("Reference the black image when decode next frame")
frameBuffer.refData(*_blackImage);
}
// Take image from _inputFrame
else
{
frameBuffer.copyData(*_inputFrame);
LOG_INFO("Reference the image data specified when decode next frame")
frameBuffer.refData(*_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 an empty frame
else
{
return &_emptyFrame;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could have done :

// generate data (ie silence or black)
if(_continueWithGenerator)
{
    _generator->decodeNextFrame(*_srcFrame);
}
// or return an empty frame
else
{
    _dstFrame->clear();
    return _dstFrame;
}

or

// return an empty frame
if(!_continueWithGenerator)
{
    _dstFrame->clear();
    return _dstFrame;
}
// or generate data (ie silence or black)
_generator->decodeNextFrame(*_srcFrame);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done here 7dd618a

}
// transform
_transform->convert(*_srcFrame, *_dstFrame);
Expand Down
9 changes: 9 additions & 0 deletions src/AvTranscoder/reader/IReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,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 +76,8 @@ 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
Frame _emptyFrame; ///< If there is no more data to decode, return an empty frame
};
}

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
63 changes: 61 additions & 2 deletions test/pyTest/testReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ 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())
Expand All @@ -43,7 +44,8 @@ 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())
Expand Down Expand Up @@ -74,3 +76,60 @@ 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 the next frame is empty
frame = av.VideoFrame(reader.readNextFrame())
assert_equals( frame.getSize(), 0 )

# 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 = av.AudioFrame(reader.readNextFrame())
if frame.getSize() == 0:
break
nbSamplesPerChannel = frame.getNbSamplesPerChannel()
bytesPerSample = 2
assert_equals( frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample )

# check if the next frame is empty
frame = av.AudioFrame(reader.readNextFrame())
assert_equals( frame.getSize(), 0 )

# 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 )