Skip to content

Transcoder: use complex filter to map several audio #271

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7f979d6
StreamTranscoder: handle array of input streams and decoders
Jul 19, 2016
18dcdc4
FilterGraph: updated process method to specify input/output
Jul 19, 2016
732791a
FilterGraph: updated process method to manage several input buffers
Jul 19, 2016
356c166
StreamTranscoder: renamed Frame buffer attributes
Jul 19, 2016
9341a4c
StreamTranscoder: added _filteredData attribute
Jul 19, 2016
ecfed3f
StreamTranscoder: fixed processRewrap
Jul 19, 2016
ecd3ddd
StreamTranscoder: updated buffer attributes
Jul 19, 2016
8dbe2d6
StreamTranscoder: no need to handle vector of filtered and transforme…
Jul 20, 2016
737a3d1
StreamTranscoder: removed _currentInputStream attribute
Jul 20, 2016
935ed64
VideoGenerator: removed VideoFrameDesc parameter in constructor
Jul 20, 2016
924bf5b
AudioGenerator: removed AudioFrameDesc parameter in constructor
Jul 20, 2016
1753141
AudioGenerator: reference silent Frame instead of copying its data
Jul 20, 2016
3e85e31
StreamTranscoder: refactored how to add decoder
Jul 20, 2016
b013d4c
FilterGraph: fixed process with several inputs
Jul 20, 2016
966d949
Transcoder: updated log when a stream transcoder failed to process
Jul 20, 2016
f506f18
StreamTranscoder: updated buffer attributes
Jul 20, 2016
44f62ac
StreamTranscoder: fixed getProcessCase in case of transcoding
Jul 20, 2016
6a62a21
Transcoder: updated how to get profile from a file
Jul 20, 2016
b81e198
transcoder: added methods to transcode several inputs to one output s…
Jul 20, 2016
19663c3
ProfileLoader: renamed 'loadProfile' methods to 'addProfile'
Jul 20, 2016
72e6d5b
FilterGraph: fixed memory deallocation
Jul 20, 2016
e623e29
StreamTranscoder: fixed transcode in case of generator
Jul 20, 2016
27a65ac
AudioDecoder: fixed channel layout value after decoding
Jul 20, 2016
2d06675
StreamTranscoder: fixed amerge option if there are more than 2 inputs
Jul 20, 2016
41ca620
Transcoder: added addStream method to add several inputs and handle t…
Jul 20, 2016
736e17f
pyTest: added testAddSeveralInputsToCreateOneOutput
Jul 20, 2016
383143a
Transcoder: fixed fillProcessStat in case of generated streams
Jul 20, 2016
83bea1a
StreamTranscoder: fixed decoding in case of generated stream
Jul 20, 2016
9fd9e9d
StreamTranscoder: fixed end of offset in case of rewrap
Jul 20, 2016
fe7b3af
pyTest: fixed testSetFrame
Jul 20, 2016
7a8bed3
pyTest: clean testTranscoderGenerateStream
Jul 20, 2016
24f820f
generators: added FrameDesc parameter in constructor
Jul 21, 2016
cc6d03f
Merge branch 'develop' of https://github.com/avTranscoder/avTranscode…
Jul 21, 2016
c18072c
Transcoder: refactored how to check string parameters when addStream
Jul 21, 2016
0746921
Transcoder: added possibility to add several inputs without any profile
Jul 21, 2016
69604e5
AudioGenerator: removed check of channel layout when decode next frame
Jul 21, 2016
0db7c2e
pyTest: updated testAddSeveralInputsToCreateOneOutput
Jul 21, 2016
b9bc00f
AudioProperties: fixed getChannelLayout
Jul 21, 2016
06053fd
pyTest: renamed testAddAllStreamsOfFileWhichDoesNotExist
Jul 21, 2016
2ffb8b8
pyTest: added a test to check the behavior when adding an empty list …
Jul 21, 2016
dc82a7b
Transcoder: fixed rewrap case when we add a list of one input
Jul 21, 2016
ab34bde
Transcoder: fixed video profile computed from the inputs
Jul 21, 2016
21b89b2
PixelProperties: fixed getPixelFormatName with pixel format YUV420P
Jul 21, 2016
f05574b
Transcoder: added a check of the stream type when adding a list of in…
Jul 21, 2016
2e61259
Merge branch 'develop' of https://github.com/avTranscoder/avTranscode…
Jul 21, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/AvTranscoder/data/decoded/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ Frame::Frame(const Frame& otherFrame)
refFrame(otherFrame);
}

void Frame::operator=(const Frame& otherFrame)
{
// check if the frame could be a valid video/audio frame
if(otherFrame.getAVFrame().format == -1)
return;
// reference the other frame
refFrame(otherFrame);
}

Frame::~Frame()
{
if(_frame != NULL)
Expand Down
10 changes: 6 additions & 4 deletions src/AvTranscoder/data/decoded/Frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ class AvExport Frame
*/
Frame();

/**
* @brief Copy properties and reference data of the other frame
*/
//@{
// @brief Copy properties and reference data of the other frame.
Frame(const Frame& otherFrame);
void operator=(const Frame& otherFrame);
//@}

virtual ~Frame();

Expand Down Expand Up @@ -63,7 +64,8 @@ class AvExport Frame

/**
* @brief Copy frame properties and create a new reference to data of the given frame.
* @warning This method allocates new data that will be freed only by calling the destructor of the referenced frame.
* @warning This method allocates new data that will be freed by calling clear method or the destructor of the referenced frame.
* @see clear
*/
void refFrame(const Frame& otherFrame);

Expand Down
4 changes: 4 additions & 0 deletions src/AvTranscoder/decoder/AudioDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ void AudioDecoder::setupDecoder(const ProfileLoader::Profile& profile)
bool AudioDecoder::decodeNextFrame(Frame& frameBuffer)
{
bool decodeNextFrame = false;
const size_t channelLayout = frameBuffer.getAVFrame().channel_layout;

if(!_isSetup)
setupDecoder();
Expand All @@ -100,6 +101,9 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer)
throw std::runtime_error("An error occurred during audio decoding: " + getDescriptionFromErrorCode(ret));
}

// fixed channel layout value after decoding
frameBuffer.getAVFrame().channel_layout = channelLayout;

// if no frame could be decompressed
if(!nextPacketRead && ret == 0 && got_frame == 0)
decodeNextFrame = false;
Expand Down
19 changes: 4 additions & 15 deletions src/AvTranscoder/decoder/AudioGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,20 @@ AudioGenerator::~AudioGenerator()
bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
{
// check the given frame
if(!frameBuffer.isAudioFrame())
if(! frameBuffer.isAudioFrame())
{
LOG_WARN("The given frame is not a valid audio frame: allocate a new AVSample to put generated data into it.");
LOG_WARN("The given frame to put data is not a valid audio frame: try to reallocate it.")
frameBuffer.clear();
static_cast<AudioFrame&>(frameBuffer).allocateAVSample(_frameDesc);
}

// Check channel layout of the given frame to be able to copy audio data to it.
// @see Frame.copyData method
if(frameBuffer.getAVFrame().channel_layout == 0)
{
const size_t channelLayout = av_get_default_channel_layout(frameBuffer.getAVFrame().channels);
LOG_WARN("Channel layout en the audio frame is not set. Set it to '" << channelLayout
<< "' to be able to copy silence data.")
av_frame_set_channel_layout(&frameBuffer.getAVFrame(), channelLayout);
}

// Generate silent
if(!_inputFrame)
{
// Generate the silent only once
if(!_silent)
{
AudioFrame& audioBuffer = static_cast<AudioFrame&>(frameBuffer);
_silent = new AudioFrame(audioBuffer.desc());
_silent = new AudioFrame(_frameDesc);

std::stringstream msg;
msg << "Generate a silence with the following features:" << std::endl;
Expand All @@ -61,7 +50,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer)
_silent->setNbSamplesPerChannel(frameBuffer.getAVFrame().nb_samples);
}
LOG_DEBUG("Copy data of the silence when decode next frame")
frameBuffer.copyData(*_silent);
frameBuffer.refFrame(*_silent);
}
// Take audio frame from _inputFrame
else
Expand Down
2 changes: 1 addition & 1 deletion src/AvTranscoder/decoder/AudioGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class AvExport AudioGenerator : public IDecoder
private:
Frame* _inputFrame; ///< Has link (no ownership)
AudioFrame* _silent; ///< The generated silent (has ownership)
const AudioFrameDesc _frameDesc; ///< The description of the silence (sampleRate, channels...)
const AudioFrameDesc _frameDesc; ///< The description of the given frame buffer when decoding.
};
}

Expand Down
37 changes: 14 additions & 23 deletions src/AvTranscoder/decoder/VideoGenerator.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "VideoGenerator.hpp"

#include <AvTranscoder/util.hpp>
#include <AvTranscoder/transform/VideoTransform.hpp>

#include <sstream>

Expand All @@ -12,6 +11,7 @@ VideoGenerator::VideoGenerator(const VideoFrameDesc& frameDesc)
: _inputFrame(NULL)
, _blackImage(NULL)
, _frameDesc(frameDesc)
, _videoTransform()
{
}

Expand All @@ -23,9 +23,9 @@ VideoGenerator::~VideoGenerator()
bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
{
// check the given frame
if(!frameBuffer.isVideoFrame())
if(! frameBuffer.isVideoFrame())
{
LOG_WARN("The given frame is not a valid video frame: allocate a new AVPicture to put generated data into it.");
LOG_WARN("The given frame to put data is not a valid video frame: try to reallocate it.")
frameBuffer.clear();
static_cast<VideoFrame&>(frameBuffer).allocateAVPicture(_frameDesc);
}
Expand All @@ -36,32 +36,23 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer)
// Generate the black image only once
if(!_blackImage)
{
// Create the black RGB image
VideoFrameDesc blackDesc(_frameDesc._width, _frameDesc._height, "rgb24");
_blackImage = new VideoFrame(blackDesc);
const unsigned char fillChar = 0;
_blackImage->assign(fillChar);

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;
msg << "width = " << _blackImage->getWidth() << std::endl;
msg << "height = " << _blackImage->getHeight() << std::endl;
msg << "pixel format = " << getPixelFormatName(_blackImage->getPixelFormat()) << std::endl;
LOG_INFO(msg.str())

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

// Input of convert
// @todo support PAL (0 to 255) and NTFS (16 to 235)
VideoFrameDesc desc(_frameDesc);
desc._pixelFormat = getAVPixelFormat("rgb24");
VideoFrame intermediateBuffer(desc);
const unsigned char fillChar = 0;
intermediateBuffer.assign(fillChar);

// Output of convert
_blackImage = new VideoFrame(imageBuffer.desc());

// Convert and store the black image
VideoTransform videoTransform;
videoTransform.convert(intermediateBuffer, *_blackImage);
}
LOG_DEBUG("Copy data of the black image when decode next frame")
frameBuffer.copyData(*_blackImage);
// Convert the black image to the configuration of the given frame
_videoTransform.convert(*_blackImage, frameBuffer);
}
// Take image from _inputFrame
else
Expand Down
11 changes: 8 additions & 3 deletions src/AvTranscoder/decoder/VideoGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "IDecoder.hpp"
#include <AvTranscoder/codec/VideoCodec.hpp>
#include <AvTranscoder/transform/VideoTransform.hpp>

namespace avtranscoder
{
Expand All @@ -15,18 +16,22 @@ class AvExport VideoGenerator : public IDecoder

public:
VideoGenerator(const VideoFrameDesc& frameDesc);

~VideoGenerator();

bool decodeNextFrame(Frame& frameBuffer);
bool decodeNextFrame(Frame& frameBuffer, const std::vector<size_t> channelIndexArray);

/**
* @brief Force to return this frame when calling the decoding methods.
* @param inputFrame: should have the same properties as the given frames when decoding.
*/
void setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; }

private:
Frame* _inputFrame; ///< A frame given from outside (has link, no ownership)
VideoFrame* _blackImage; ///< The generated black image (has ownership)
const VideoFrameDesc _frameDesc; ///< The description of the black image (width, height...)
VideoFrame* _blackImage; ///< The generated RGB black image (has ownership)
const VideoFrameDesc _frameDesc; ///< The description of the given frame buffer when decoding.
VideoTransform _videoTransform; ///< To transform data of the back image to the given Frame when decoding.
};
}

Expand Down
121 changes: 68 additions & 53 deletions src/AvTranscoder/filter/FilterGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,35 @@ FilterGraph::~FilterGraph()
avfilter_graph_free(&_graph);
}

void FilterGraph::process(Frame& frame)
void FilterGraph::process(const std::vector<Frame*>& inputs, Frame& output)
{
if(!hasFilters())
{
LOG_DEBUG("No filter to process.")
LOG_DEBUG("No filter to process: reference first input frame to the given output.")
output.clear();
output.refFrame(*inputs.at(0));
return;
}

// init filter graph
if(!_isInit)
init(frame);
init(inputs, output);

// setup source frame
int ret = av_buffersrc_write_frame(_filters.at(0)->getAVFilterContext(), &frame.getAVFrame());
if(ret < 0)
// setup input frames
for(size_t index = 0; index < inputs.size(); ++index)
{
throw std::runtime_error("Error when adding a frame to the source buffer used to start to process filters: " +
getDescriptionFromErrorCode(ret));
const int ret = av_buffersrc_write_frame(_filters.at(index)->getAVFilterContext(), &inputs.at(index)->getAVFrame());
if(ret < 0)
{
throw std::runtime_error("Error when adding a frame to the source buffer used to start to process filters: " +
getDescriptionFromErrorCode(ret));
}
}

// pull filtered data from the filter graph
for(;;)
{
ret = av_buffersink_get_frame(_filters.at(_filters.size() - 1)->getAVFilterContext(), &frame.getAVFrame());
const int ret = av_buffersink_get_frame(_filters.at(_filters.size() - 1)->getAVFilterContext(), &output.getAVFrame());
if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
break;
if(ret < 0)
Expand All @@ -77,22 +82,31 @@ Filter& FilterGraph::addFilter(const std::string& filterName, const std::string&
return *_filters.back();
}

void FilterGraph::init(const Frame& frame)
void FilterGraph::init(const std::vector<Frame*>& inputs, Frame& output)
{
// push filters to the graph
pushInBuffer(frame);
for(size_t i = 1; i < _filters.size(); ++i)
addInBuffer(inputs);
addOutBuffer(output);
for(size_t i = 0; i < _filters.size(); ++i)
{
pushFilter(*_filters.at(i));
}
pushOutBuffer(frame);

// connect filters
for(size_t index = 0; index < _filters.size() - 1; ++index)
{
LOG_INFO("Connect filter " << _filters.at(index)->getName() << " to filter " << _filters.at(index + 1)->getName())
size_t indexOfOutputFilterToConnect = index + 1;
size_t indexOfInputPadOfDestinationFilter = 0;
// handle cases with several inputs
if(index < inputs.size())
{
indexOfOutputFilterToConnect = inputs.size();
indexOfInputPadOfDestinationFilter = index;
}

LOG_INFO("Connect filter " << _filters.at(index)->getName() << " to filter " << _filters.at(indexOfOutputFilterToConnect)->getName())
const int err =
avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(index + 1)->getAVFilterContext(), 0);
avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(indexOfOutputFilterToConnect)->getAVFilterContext(), indexOfInputPadOfDestinationFilter);
if(err < 0)
{
throw std::runtime_error("Error when connecting filters.");
Expand Down Expand Up @@ -128,57 +142,58 @@ void FilterGraph::pushFilter(Filter& filter)
}
}

void FilterGraph::pushInBuffer(const Frame& frame)
void FilterGraph::addInBuffer(const std::vector<Frame*>& inputs)
{
std::string filterName;
std::stringstream filterOptions;
// audio frame
if(frame.isAudioFrame())
for(std::vector<Frame*>::const_reverse_iterator it = inputs.rbegin(); it != inputs.rend(); ++it)
{
filterName = "abuffer";
const AudioFrame& audioFrame = dynamic_cast<const AudioFrame&>(frame);
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "sample_rate=" << audioFrame.getSampleRate() << ":";
filterOptions << "sample_fmt=" << getSampleFormatName(audioFrame.getSampleFormat()) << ":";
filterOptions << "channel_layout=0x" << std::hex << audioFrame.getChannelLayout();
}
// video frame
else if(frame.isVideoFrame())
{
filterName = "buffer";
const VideoFrame& videoFrame = dynamic_cast<const VideoFrame&>(frame);
filterOptions << "video_size=" << videoFrame.getWidth() << "x" << videoFrame.getHeight() << ":";
filterOptions << "pix_fmt=" << getPixelFormatName(videoFrame.getPixelFormat()) << ":";
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "pixel_aspect=" << _codec.getAVCodecContext().sample_aspect_ratio.num << "/"
<< _codec.getAVCodecContext().sample_aspect_ratio.den;
std::string filterName;
std::stringstream filterOptions;
// audio frame
if((*it)->isAudioFrame())
{
filterName = "abuffer";
const AudioFrame* audioFrame = dynamic_cast<const AudioFrame*>(*it);
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "sample_rate=" << audioFrame->getSampleRate() << ":";
filterOptions << "sample_fmt=" << getSampleFormatName(audioFrame->getSampleFormat()) << ":";
filterOptions << "channel_layout=0x" << std::hex << audioFrame->getChannelLayout();
}
// video frame
else if((*it)->isVideoFrame())
{
filterName = "buffer";
const VideoFrame* videoFrame = dynamic_cast<const VideoFrame*>(*it);
filterOptions << "video_size=" << videoFrame->getWidth() << "x" << videoFrame->getHeight() << ":";
filterOptions << "pix_fmt=" << getPixelFormatName(videoFrame->getPixelFormat()) << ":";
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "pixel_aspect=" << _codec.getAVCodecContext().sample_aspect_ratio.num << "/"
<< _codec.getAVCodecContext().sample_aspect_ratio.den;
}
// invalid frame
else
throw std::runtime_error("Cannot create input buffer of filter graph: the given frame is invalid.");

// add in buffer
Filter* in = new Filter(filterName, filterOptions.str(), "in");
LOG_INFO("Add filter '" << filterName << "' at the beginning of the graph.")
_filters.insert(_filters.begin(), in);
}
// invalid frame
else
throw std::runtime_error("Cannot create input buffer of filter graph: the given frame is invalid.");

// add in buffer
Filter* in = new Filter(filterName, filterOptions.str(), "in");
LOG_INFO("Add filter '" << filterName << "' at the beginning of the graph.")
_filters.insert(_filters.begin(), in);
pushFilter(*in);
}

void FilterGraph::pushOutBuffer(const Frame& frame)
void FilterGraph::addOutBuffer(const Frame& output)
{
std::string filterName;

if(frame.isAudioFrame())
if(output.isAudioFrame())
filterName = "abuffersink";
else if(frame.isVideoFrame())
else if(output.isVideoFrame())
filterName = "buffersink";
else
throw std::runtime_error("Cannot create output buffer of filter graph: the given frame is invalid.");

// add out buffer
Filter& out = addFilter(filterName, "", "out");
pushFilter(out);
addFilter(filterName, "", "out");
}
}
Loading