From 44b24cd6e245a9411a250e08b5de2994cb186a6e Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 20 Nov 2015 18:31:26 +0100 Subject: [PATCH 01/15] CMake: added avfilter when build the project --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 78d011e6..201608ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,7 @@ include(AvTranscoderMacros) message(STATUS "AvTranscoder version is ${AVTRANSCODER_VERSION}") # Find package ffmpeg/libav -find_package(FFmpeg COMPONENTS avcodec avformat avutil swscale swresample avresample) +find_package(FFmpeg COMPONENTS avcodec avformat avutil swscale avfilter swresample avresample) if(swresample_FOUND) add_definitions(-DAVTRANSCODER_FFMPEG_DEPENDENCY) message(STATUS "Build avTranscoder with dependency to ffmpeg.") From 3d4b67fbd9871a03b091c3e29de2df40e3868c67 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 20 Nov 2015 18:31:47 +0100 Subject: [PATCH 02/15] Library: added avfilter license --- src/AvTranscoder/Library.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AvTranscoder/Library.cpp b/src/AvTranscoder/Library.cpp index 4dc10915..3ead6a4d 100644 --- a/src/AvTranscoder/Library.cpp +++ b/src/AvTranscoder/Library.cpp @@ -5,12 +5,14 @@ extern "C" { #include #include #include +#include #ifdef AVTRANSCODER_LIBAV_DEPENDENCY #include #else #include #endif #include +#include } #include @@ -86,6 +88,7 @@ Libraries getLibraries() libs.push_back( Library( "swresample", avutil_license(), LIBSWRESAMPLE_VERSION_MAJOR, LIBSWRESAMPLE_VERSION_MINOR, LIBSWRESAMPLE_VERSION_MICRO ) ); #endif libs.push_back( Library( "swscale", swscale_license(), LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO ) ); + libs.push_back( Library( "avfilter", avfilter_license(), LIBAVFILTER_VERSION_MAJOR, LIBAVFILTER_VERSION_MINOR, LIBAVFILTER_VERSION_MICRO ) ); return libs; } From 4b7dc836d27258c5e5264cea4a5c50624edce311 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 20 Nov 2015 19:07:28 +0100 Subject: [PATCH 03/15] Added FilterGraph The class handles a list of filters. --- src/AvTranscoder/filter/FilterGraph.cpp | 71 +++++++++++++++++++++++++ src/AvTranscoder/filter/FilterGraph.hpp | 47 ++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/AvTranscoder/filter/FilterGraph.cpp create mode 100644 src/AvTranscoder/filter/FilterGraph.hpp diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp new file mode 100644 index 00000000..fef240d4 --- /dev/null +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -0,0 +1,71 @@ +#include "FilterGraph.hpp" + +extern "C" { +#include +} + +#include + + +namespace avtranscoder +{ + +FilterGraph::FilterGraph() + : _graph( avfilter_graph_alloc() ) + , _filters() +{ + if( ! _graph ) + throw std::runtime_error( "Unable to create filter graph: out of memory."); +} + +FilterGraph::~FilterGraph() +{ + for( std::vector< Filter >::iterator it = _filters.begin(); it < _filters.end(); ++it ) + { + avfilter_free( it->second ); + } + avfilter_graph_free( &_graph ); +} + +void FilterGraph::addFilter( const std::string& filtername ) +{ + AVFilter* filter = avfilter_get_by_name( filtername.c_str() ); + if( filter ) + { + AVFilterContext* context = NULL; + const int err = avfilter_graph_create_filter( &context, filter, NULL, filtername.c_str(), NULL, _graph ); + if( err < 0 ) + { + std::string msg( "Cannot add filter " ); + msg += filtername; + msg += " to the graph: "; + msg += getDescriptionFromErrorCode(err); + throw std::runtime_error( msg ); + } + else + _filters.push_back( std::make_pair(filter, context) ); + } + else + { + throw std::runtime_error( "Cannot find filter " + filtername ); + } +} + +void FilterGraph::connectFilters() +{ + // connecting filters + for( size_t index = 0; index < _filters.size()+1; index+=2 ) + { + const int err = avfilter_link( _filters.at(index).second, 0, _filters.at(index+1).second, 0); + if( err < 0 ) + { + throw std::runtime_error( "Error connecting filters." ); + } + } + // configuring + const int err = avfilter_graph_config( _graph, NULL ); + if( err < 0 ) + throw std::runtime_error( "Error configuring the filter graph."); +} + +} diff --git a/src/AvTranscoder/filter/FilterGraph.hpp b/src/AvTranscoder/filter/FilterGraph.hpp new file mode 100644 index 00000000..8486414b --- /dev/null +++ b/src/AvTranscoder/filter/FilterGraph.hpp @@ -0,0 +1,47 @@ +#ifndef _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_ +#define _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_ + +#include + +#include +#include + +struct AVFilterGraph; +struct AVFilter; +struct AVFilterContext; + +namespace avtranscoder +{ + +/** + * @brief + **/ +class AvExport FilterGraph +{ +private: + FilterGraph( const FilterGraph& otherFilterGraph ); + FilterGraph& operator=( const FilterGraph& otherFilterGraph ); + +public: + typedef std::pair Filter; + +public: + FilterGraph(); + ~FilterGraph(); + + void addFilter( const std::string& filtername ); + Filter& getFirstFilter() { return _filters.at(0); } + Filter& getLastFilter() { return _filters.at(_filters.size()-1); } + + bool hasFilters() const { return ! _filters.empty(); } + + void connectFilters(); + +private: + AVFilterGraph* _graph; + std::vector< Filter > _filters; +}; + +} + +#endif From ec6b1de7fc63b48778a407e92ce03683f86bb327 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 20 Nov 2015 19:08:08 +0100 Subject: [PATCH 04/15] common: load filters when preloadCodecsAndFormats Rename the function? --- src/AvTranscoder/common.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AvTranscoder/common.cpp b/src/AvTranscoder/common.cpp index bc10cec1..0d08def6 100644 --- a/src/AvTranscoder/common.cpp +++ b/src/AvTranscoder/common.cpp @@ -2,6 +2,7 @@ extern "C" { #include +#include #include } @@ -15,6 +16,7 @@ namespace avtranscoder void preloadCodecsAndFormats() { av_register_all(); + avfilter_register_all(); } std::string getDescriptionFromErrorCode( const int code ) From 11b72e85af296fcae30a183a4e591adbcda76412 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 16:42:46 +0100 Subject: [PATCH 05/15] Frame: added isVideo/AudioFrame methods --- src/AvTranscoder/data/decoded/Frame.cpp | 14 ++++++++++++++ src/AvTranscoder/data/decoded/Frame.hpp | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index 9a35e7da..68491b24 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -80,4 +80,18 @@ void Frame::allocateAVFrame() throw std::runtime_error("Unable to allocate an empty Frame."); } } + +bool Frame::isAudioFrame() const +{ + if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1) + return true; + return false; +} + +bool Frame::isVideoFrame() const +{ + if(_frame->width && _frame->height && _frame->format != -1) + return true; + return false; +} } diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index 505f66a8..8d014891 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -63,6 +63,18 @@ class AvExport Frame */ void clear(); + /** + * @return If it corresponds to a valid audio frame. + * @see AudioFrame + */ + bool isAudioFrame() const; + + /** + * @return If it corresponds to a valid video frame. + * @see VideoFrame + */ + bool isVideoFrame() const; + #ifndef SWIG AVFrame& getAVFrame() { return *_frame; } const AVFrame& getAVFrame() const { return *_frame; } From bb024582200bc9049f5ef259506198d41b4592f8 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 16:42:58 +0100 Subject: [PATCH 06/15] AudioFrame: added getChannelLayout method --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 3a68fa16..f3a89a17 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -44,6 +44,7 @@ class AvExport AudioFrame : public Frame size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } size_t getNbChannels() const { return av_frame_get_channels(_frame); } + size_t getChannelLayout() const { return av_frame_get_channel_layout(_frame); } AVSampleFormat getSampleFormat() const { return static_cast(_frame->format); } size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); } From d441d2562fb266cb5925a243e9b952d666c8eb9f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 16:43:49 +0100 Subject: [PATCH 07/15] filter: added Filter class Describe a filter and its options. --- src/AvTranscoder/filter/Filter.cpp | 37 +++++++++++++++++++++++++++ src/AvTranscoder/filter/Filter.hpp | 40 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/AvTranscoder/filter/Filter.cpp create mode 100644 src/AvTranscoder/filter/Filter.hpp diff --git a/src/AvTranscoder/filter/Filter.cpp b/src/AvTranscoder/filter/Filter.cpp new file mode 100644 index 00000000..07cc30d8 --- /dev/null +++ b/src/AvTranscoder/filter/Filter.cpp @@ -0,0 +1,37 @@ +#include "Filter.hpp" + +extern "C" { +#include +} + +#include + +namespace avtranscoder +{ + +Filter::Filter(const std::string& name, const std::string& options, const std::string& instanceName) + : _filter(NULL) + , _context(NULL) + , _options(options) + , _instanceName(instanceName.empty() ? name : instanceName) +{ + _filter = avfilter_get_by_name(name.c_str()); + if(!_filter) + { + std::string msg("Cannot find filter "); + msg += name; + msg += ". It will not be added to the filter graph."; + throw std::runtime_error(msg); + } +} + +Filter::~Filter() +{ + avfilter_free(_context); +} + +std::string Filter::getName() const +{ + return _filter->name ? std::string(_filter->name) : ""; +} +} diff --git a/src/AvTranscoder/filter/Filter.hpp b/src/AvTranscoder/filter/Filter.hpp new file mode 100644 index 00000000..680a923a --- /dev/null +++ b/src/AvTranscoder/filter/Filter.hpp @@ -0,0 +1,40 @@ +#ifndef _AV_TRANSCODER_FILTER_FILTER_HPP_ +#define _AV_TRANSCODER_FILTER_FILTER_HPP_ + +#include + +struct AVFilter; +struct AVFilterContext; + +namespace avtranscoder +{ + +/** + * @brief Describe a filter and its options. + **/ +class AvExport Filter +{ +public: + Filter(const std::string& name, const std::string& options = "", const std::string& instanceName = ""); + ~Filter(); + + std::string getName() const; + std::string getOptions() const { return _options; } + std::string getInstanceName() const { return _instanceName; } + +#ifndef SWIG + AVFilter& getAVFilter() { return *_filter; } + AVFilterContext* getAVFilterContext() { return _context; } + + void setAVFilterContext(AVFilterContext* newContext) { _context = newContext; } +#endif + +private: + AVFilter* _filter; + AVFilterContext* _context; + std::string _options; + std::string _instanceName; +}; +} + +#endif From bec115599ccb83cbcfc76e0c9b49d84949a6af11 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 16:47:46 +0100 Subject: [PATCH 08/15] FilterGraph: used Filter class to manage a graph of video/audio filters --- src/AvTranscoder/filter/FilterGraph.cpp | 169 ++++++++++++++++++++---- src/AvTranscoder/filter/FilterGraph.hpp | 72 ++++++++-- 2 files changed, 200 insertions(+), 41 deletions(-) diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index 1410a64a..9d12cab1 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -1,17 +1,26 @@ #include "FilterGraph.hpp" +#include +#include +#include + extern "C" { #include +#include +#include } #include +#include namespace avtranscoder { -FilterGraph::FilterGraph() +FilterGraph::FilterGraph(const ICodec& codec) : _graph(avfilter_graph_alloc()) , _filters() + , _codec(codec) + , _isInit(false) { if(!_graph) throw std::runtime_error("Unable to create filter graph: out of memory."); @@ -19,51 +28,157 @@ FilterGraph::FilterGraph() FilterGraph::~FilterGraph() { - for(std::vector::iterator it = _filters.begin(); it < _filters.end(); ++it) + for(std::vector::iterator it = _filters.begin(); it < _filters.end(); ++it) { - avfilter_free(it->second); + it = _filters.erase(it); } avfilter_graph_free(&_graph); } -void FilterGraph::addFilter(const std::string& filtername) +void FilterGraph::process(Frame& frame) { - AVFilter* filter = avfilter_get_by_name(filtername.c_str()); - if(filter) + if(!hasFilters()) { - AVFilterContext* context = NULL; - const int err = avfilter_graph_create_filter(&context, filter, NULL, filtername.c_str(), NULL, _graph); - if(err < 0) - { - std::string msg("Cannot add filter "); - msg += filtername; - msg += " to the graph: "; - msg += getDescriptionFromErrorCode(err); - throw std::runtime_error(msg); - } - else - _filters.push_back(std::make_pair(filter, context)); + LOG_DEBUG("No filter to process.") + return; } - else + + // init filter graph + if(!_isInit) + init(frame); + + // setup source frame + int ret = av_buffersrc_write_frame(_filters.at(0)->getAVFilterContext(), &frame.getAVFrame()); + if(ret < 0) { - throw std::runtime_error("Cannot find filter " + filtername); + 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()); + if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + break; + if(ret < 0) + { + throw std::runtime_error("Error reading buffer from buffersink: " + getDescriptionFromErrorCode(ret)); + } } } -void FilterGraph::connectFilters() +Filter& FilterGraph::addFilter(const std::string& filterName, const std::string& filterOptions, + const std::string& instanceName) +{ + LOG_INFO("Add filter " << filterName << " to the graph.") + Filter* filter = new Filter(filterName, filterOptions, instanceName); + _filters.push_back(filter); + return *_filters.back(); +} + +void FilterGraph::init(const Frame& frame) { - // connecting filters - for(size_t index = 0; index < _filters.size() + 1; index += 2) + // push filters to the graph + pushInBuffer(frame); + for(size_t i = 1; i < _filters.size(); ++i) { - const int err = avfilter_link(_filters.at(index).second, 0, _filters.at(index + 1).second, 0); + 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()) + const int err = + avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(index + 1)->getAVFilterContext(), 0); if(err < 0) { - throw std::runtime_error("Error connecting filters."); + throw std::runtime_error("Error when connecting filters."); } } - // configuring + + // configuring the graph + LOG_INFO("Configuring filter graph.") const int err = avfilter_graph_config(_graph, NULL); if(err < 0) - throw std::runtime_error("Error configuring the filter graph."); + { + throw std::runtime_error("Error configuring the filter graph: " + getDescriptionFromErrorCode(err)); + } + + _isInit = true; +} + +void FilterGraph::pushFilter(Filter& filter) +{ + AVFilterContext* context = NULL; + const int err = avfilter_graph_create_filter(&context, &filter.getAVFilter(), filter.getInstanceName().c_str(), + filter.getOptions().c_str(), NULL, _graph); + filter.setAVFilterContext(context); + if(err < 0) + { + std::string msg("Cannot add filter "); + msg += filter.getName(); + msg += " (instance="; + msg += filter.getInstanceName(); + msg += ") to the graph: "; + msg += getDescriptionFromErrorCode(err); + throw std::runtime_error(msg); + } +} + +void FilterGraph::pushInBuffer(const Frame& frame) +{ + std::string filterName; + std::stringstream filterOptions; + // audio frame + if(frame.isAudioFrame()) + { + filterName = "abuffer"; + const AudioFrame& audioFrame = dynamic_cast(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" << audioFrame.getChannelLayout(); + } + // video frame + else if(frame.isVideoFrame()) + { + filterName = "buffer"; + const VideoFrame& videoFrame = dynamic_cast(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; + } + // 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) +{ + std::string filterName; + + if(frame.isAudioFrame()) + filterName = "abuffersink"; + else if(frame.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); } } diff --git a/src/AvTranscoder/filter/FilterGraph.hpp b/src/AvTranscoder/filter/FilterGraph.hpp index 5f9c6daf..bd42f979 100644 --- a/src/AvTranscoder/filter/FilterGraph.hpp +++ b/src/AvTranscoder/filter/FilterGraph.hpp @@ -2,19 +2,21 @@ #define _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_ #include +#include +#include +#include #include -#include struct AVFilterGraph; -struct AVFilter; -struct AVFilterContext; namespace avtranscoder { /** - * @brief + * @brief Manager of filters. + * @warning Currently, the class manages only filters which has one input and one output. + * @note See 'complex graph' definition in ffmpeg documentation. **/ class AvExport FilterGraph { @@ -23,23 +25,65 @@ class AvExport FilterGraph FilterGraph& operator=(const FilterGraph& otherFilterGraph); public: - typedef std::pair Filter; - -public: - FilterGraph(); + FilterGraph(const ICodec& codec); ~FilterGraph(); - void addFilter(const std::string& filtername); - Filter& getFirstFilter() { return _filters.at(0); } - Filter& getLastFilter() { return _filters.at(_filters.size() - 1); } + /** + * @brief Add a filter. + * @param filterName: the method gets the filter definition from this name. + * @param filterArgs: options to initialize the filter with. This must be a ':'-separated list of options in the + * 'key=value' form. + * @param instanceName: name of the instance filter in the graph (if empty, same as filterName). + * @return the filter added + * @throw runtime exception if the filter is not found + * @warning The filter will be added to the filter graph when calling process method. + * @see process + */ + Filter& addFilter(const std::string& filterName, const std::string& filterOptions = "", + const std::string& instanceName = ""); + /** + * @brief Pull filtered data from the filter graph, and put result to the given frame. + * @param frame: both input and output data of the method. + * @note Do nothing if there was no filter added. + */ + void process(Frame& frame); + +private: + /** + * @return If at least one filter has been added to the filter graph + */ bool hasFilters() const { return !_filters.empty(); } - void connectFilters(); + /** + * @brief Initialize the graph of filters to process. + * @see pushFilterToGraph + * @see pushInBuffer + * @see pushOutBuffer + */ + void init(const Frame& frame); + + /** + * @brief Push the given Filter to the graph. + */ + void pushFilter(Filter& filter); + + ///@{ + /// @brief Push the input and output buffer at the beginning and the end of the graph. + void pushInBuffer(const Frame& frame); + void pushOutBuffer(const Frame& frame); + //@} private: - AVFilterGraph* _graph; - std::vector _filters; + AVFilterGraph* _graph; ///< The graph which holds the filters. + std::vector _filters; ///< List of filters to process. + const ICodec& _codec; ///< Codec of the stream on which the filters will be applied. + + /** + * @brief Is the FilterGraph initialized. + * @see init + */ + bool _isInit; }; } From b2b5d0a37260be6b091fa2829ad730f12b40935c Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 17:01:22 +0100 Subject: [PATCH 09/15] StreamTranscoder: added a FilterGraph attribute --- .../transcoder/StreamTranscoder.cpp | 22 +++++++++++++++++++ .../transcoder/StreamTranscoder.hpp | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 0ae48dba..54f43689 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -30,6 +30,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu , _currentDecoder(NULL) , _outputEncoder(NULL) , _transform(NULL) + , _filterGraph(NULL) , _subStreamIndex(-1) , _offset(offset) , _needToSwitchToGenerator(false) @@ -44,6 +45,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu try { + // filter + _filterGraph = new FilterGraph(_inputStream->getVideoCodec()); + VideoFrameDesc inputFrameDesc(_inputStream->getVideoCodec().getVideoFrameDesc()); // generator decoder @@ -78,6 +82,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu try { + // filter + _filterGraph = new FilterGraph(_inputStream->getAudioCodec()); + AudioFrameDesc inputFrameDesc(_inputStream->getAudioCodec().getAudioFrameDesc()); // generator decoder @@ -128,6 +135,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu , _currentDecoder(NULL) , _outputEncoder(NULL) , _transform(NULL) + , _filterGraph(NULL) , _subStreamIndex(subStreamIndex) , _offset(offset) , _needToSwitchToGenerator(false) @@ -137,6 +145,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu { case AVMEDIA_TYPE_VIDEO: { + // filter + _filterGraph = new FilterGraph(_inputStream->getVideoCodec()); + // input decoder VideoDecoder* inputVideo = new VideoDecoder(*static_cast(_inputStream)); inputVideo->setupDecoder(); @@ -170,6 +181,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu } case AVMEDIA_TYPE_AUDIO: { + // filter + _filterGraph = new FilterGraph(_inputStream->getAudioCodec()); + // input decoder AudioDecoder* inputAudio = new AudioDecoder(*static_cast(_inputStream)); inputAudio->setupDecoder(); @@ -228,6 +242,7 @@ StreamTranscoder::StreamTranscoder(const ICodec& inputCodec, IOutputFile& output , _currentDecoder(NULL) , _outputEncoder(NULL) , _transform(NULL) + , _filterGraph(NULL) , _subStreamIndex(-1) , _offset(0) , _needToSwitchToGenerator(false) @@ -241,6 +256,9 @@ StreamTranscoder::StreamTranscoder(const ICodec& inputCodec, IOutputFile& output _generator = generatorVideo; _currentDecoder = _generator; + // filter + _filterGraph = new FilterGraph(inputVideoCodec); + // buffers to process VideoFrameDesc inputFrameDesc = inputVideoCodec.getVideoFrameDesc(); VideoFrameDesc outputFrameDesc = inputFrameDesc; @@ -267,6 +285,9 @@ StreamTranscoder::StreamTranscoder(const ICodec& inputCodec, IOutputFile& output _generator = generatorAudio; _currentDecoder = _generator; + // filter + _filterGraph = new FilterGraph(inputAudioCodec); + // buffers to process AudioFrameDesc inputFrameDesc = inputAudioCodec.getAudioFrameDesc(); AudioFrameDesc outputFrameDesc = inputFrameDesc; @@ -298,6 +319,7 @@ StreamTranscoder::~StreamTranscoder() delete _generator; delete _outputEncoder; delete _transform; + delete _filterGraph; delete _inputDecoder; } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 1bdc45c8..cc543dec 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -10,7 +10,7 @@ #include #include - +#include #include namespace avtranscoder @@ -131,6 +131,8 @@ class AvExport StreamTranscoder ITransform* _transform; ///< Video or audio transform (has ownership) + FilterGraph* _filterGraph; ///< Filter graph (has ownership) + int _subStreamIndex; ///< Index of channel that is processed from the input stream (<0 if no demultiplexing). float _offset; ///< Offset, in seconds, at the beginning of the StreamTranscoder. From e25fef4cc5c46f7bd202f03a70b349bcdf470712 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 17:02:13 +0100 Subject: [PATCH 10/15] StreamTranscoder: process filtering at each frame when transcode Do nothing if there is no filter previously added. --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 54f43689..c88870b5 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -483,6 +483,9 @@ bool StreamTranscoder::processTranscode(const int subStreamIndex) else decodingStatus = _currentDecoder->decodeNextFrame(*_sourceBuffer, subStreamIndex); + LOG_DEBUG("Filtering") + _filterGraph->process(*_sourceBuffer); + CodedData data; if(decodingStatus) { From 00007c9ed9e89add0c2025aaf98ba84fa6e325e0 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 17:02:31 +0100 Subject: [PATCH 11/15] StreamTranscoder: added getFilterGraph method --- src/AvTranscoder/transcoder/StreamTranscoder.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index cc543dec..8020299a 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -79,6 +79,9 @@ class AvExport StreamTranscoder /// Returns a reference to the object which transforms the decoded data ITransform* getTransform() const { return _transform; } + /// Returns a pointer to the object which manage filtering + FilterGraph* getFilterGraph() const { return _filterGraph; } + /// Returns a reference to the stream which unwraps data IInputStream* getInputStream() const { return _inputStream; } /// Returns a reference to the stream which wraps data From 612ac4ed9225b33f04e2087a831fa75bf7e11013 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 17:02:50 +0100 Subject: [PATCH 12/15] StreamTranscoder: updated doc --- src/AvTranscoder/transcoder/StreamTranscoder.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 8020299a..7c8a8c5d 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -76,13 +76,13 @@ class AvExport StreamTranscoder /// Returns a pointer to the encoder IEncoder* getEncoder() const { return _outputEncoder; } - /// Returns a reference to the object which transforms the decoded data + /// Returns a pointer to the object which transforms the decoded data ITransform* getTransform() const { return _transform; } /// Returns a pointer to the object which manage filtering FilterGraph* getFilterGraph() const { return _filterGraph; } - /// Returns a reference to the stream which unwraps data + /// Returns a pointer to the stream which unwraps data IInputStream* getInputStream() const { return _inputStream; } /// Returns a reference to the stream which wraps data IOutputStream& getOutputStream() const { return *_outputStream; } From 35d273c823bdd51c9e0b3a86d9b13562e379ef29 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 21 Jan 2016 17:18:48 +0100 Subject: [PATCH 13/15] filter: added classes to SWIG interface --- src/AvTranscoder/avTranscoder.i | 1 + src/AvTranscoder/filter/filter.i | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 src/AvTranscoder/filter/filter.i diff --git a/src/AvTranscoder/avTranscoder.i b/src/AvTranscoder/avTranscoder.i index c5578383..fe9db742 100644 --- a/src/AvTranscoder/avTranscoder.i +++ b/src/AvTranscoder/avTranscoder.i @@ -38,5 +38,6 @@ %include "AvTranscoder/transform/transform.i" %include "AvTranscoder/file/file.i" %include "AvTranscoder/stat/stat.i" +%include "AvTranscoder/filter/filter.i" %include "AvTranscoder/transcoder/transcoder.i" %include "AvTranscoder/reader/reader.i" diff --git a/src/AvTranscoder/filter/filter.i b/src/AvTranscoder/filter/filter.i new file mode 100644 index 00000000..016ac35a --- /dev/null +++ b/src/AvTranscoder/filter/filter.i @@ -0,0 +1,7 @@ +%{ +#include +#include +%} + +%include +%include From 1dc1f09a761bc50e115be04bc90b02a7661d7c0c Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 22 Jan 2016 11:10:00 +0100 Subject: [PATCH 14/15] FilterGraph: fixed channel layout value of abuffer Need to pass the hexa value of the channel layout. --- src/AvTranscoder/filter/FilterGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index 9d12cab1..8cd40d5d 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -141,7 +141,7 @@ void FilterGraph::pushInBuffer(const Frame& frame) << _codec.getAVCodecContext().time_base.den << ":"; filterOptions << "sample_rate=" << audioFrame.getSampleRate() << ":"; filterOptions << "sample_fmt=" << getSampleFormatName(audioFrame.getSampleFormat()) << ":"; - filterOptions << "channel_layout=0x" << audioFrame.getChannelLayout(); + filterOptions << "channel_layout=0x" << std::hex << audioFrame.getChannelLayout(); } // video frame else if(frame.isVideoFrame()) From 6371c403bbb43feb5c3b38cce62377209b791be8 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 22 Jan 2016 11:10:26 +0100 Subject: [PATCH 15/15] pyTest: added testFilter to check filter classes --- test/pyTest/testFilter.py | 130 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/pyTest/testFilter.py diff --git a/test/pyTest/testFilter.py b/test/pyTest/testFilter.py new file mode 100644 index 00000000..8bf84278 --- /dev/null +++ b/test/pyTest/testFilter.py @@ -0,0 +1,130 @@ +import os + +# Check if environment is setup to run the tests +if os.environ.get('AVTRANSCODER_TEST_VIDEO_AVI_FILE') is None or os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_FILE') is None: + from nose.plugins.skip import SkipTest + raise SkipTest("Need to define environment variables " + "AVTRANSCODER_TEST_VIDEO_AVI_FILE and " + "AVTRANSCODER_TEST_AUDIO_WAVE_FILE") + +from nose.tools import * + +from pyAvTranscoder import avtranscoder as av + + +@raises(RuntimeError) +def testUnknwonFilter(): + """ + Try to create an unknown filter. + """ + unknwonFilter = av.Filter("unknwonFilter") + + +def testFilterAttributes(): + """ + Check the attributes of an exiting filter. + """ + # video filter + options = "75:52"; + name = "scale" + scale = av.Filter(name, options) + + assert_equals(scale.getName(), name) + assert_equals(scale.getOptions(), options) + assert_equals(scale.getInstanceName(), name) + + # audio filter + options = ""; + name = "volume" + instanceName = "vol1" + volume = av.Filter(name, options, instanceName) + + # same instance name + assert_equals(volume.getName(), name) + assert_equals(volume.getOptions(), options) + assert_equals(volume.getInstanceName(), instanceName) + +def testVideoTranscodeWithFilter(): + """ + A video transcode with a yadif filter. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_AVI_FILE'] + outputFileName = "testVideoTranscodeWithFilter.avi" + + ouputFile = av.OutputFile( outputFileName ) + transcoder = av.Transcoder( ouputFile ) + + inputFile = av.InputFile( inputFileName ) + src_videoStream = inputFile.getProperties().getVideoProperties()[0] + + # transcode the video stream + videoStreamIndex = src_videoStream.getStreamIndex() + customProfile = av.ProfileMap() + customProfile[av.avProfileIdentificator] = "customProfile" + customProfile[av.avProfileIdentificatorHuman] = "custom profile" + customProfile[av.avProfileType] = av.avProfileTypeVideo + customProfile[av.avProfileCodec] = "mpeg2video" + customProfile[av.avProfilePixelFormat] = "yuv420p" + transcoder.add( inputFileName, videoStreamIndex, customProfile ) + + # add yadif filter + streamTranscoder = transcoder.getStreamTranscoder(0) + filterGraph = streamTranscoder.getFilterGraph() + filterGraph.addFilter("yadif") + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + + # check process stat returned + videoStat = processStat.getVideoStat(0) + assert_equals(src_videoStream.getDuration(), videoStat.getDuration()) + assert_equals(int(src_videoStream.getDuration() * src_videoStream.getFps()), videoStat.getNbFrames()) + + # get dst file of transcode + dst_inputFile = av.InputFile( outputFileName ) + dst_properties = dst_inputFile.getProperties() + dst_videoStream = dst_properties.getVideoProperties()[0] + + assert_equals( "mpeg2video", dst_videoStream.getCodecName() ) + assert_equals( "yuv420p", dst_videoStream.getPixelProperties().getPixelName() ) + + +def testAudioTranscodeWithFilter(): + """ + An audio transcode with a volume filter. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + outputFileName = "testAudioTranscodeWithFilter.wav" + + ouputFile = av.OutputFile( outputFileName ) + transcoder = av.Transcoder( ouputFile ) + + inputFile = av.InputFile( inputFileName ) + src_audioStream = inputFile.getProperties().getAudioProperties()[0] + + # transcode the video stream + audioStreamIndex = src_audioStream.getStreamIndex() + transcoder.add( inputFileName, audioStreamIndex, "wave24b48kmono" ) + + # add volume filter (here +150% of current volume) + streamTranscoder = transcoder.getStreamTranscoder(0) + filterGraph = streamTranscoder.getFilterGraph() + filterGraph.addFilter("volume", "1.5") + + progress = av.ConsoleProgress() + processStat = transcoder.process( progress ) + + # check process stat returned + audioStat = processStat.getAudioStat(0) + assert_equals(src_audioStream.getDuration(), audioStat.getDuration()) + + # get dst file of transcode + dst_inputFile = av.InputFile( outputFileName ) + dst_properties = dst_inputFile.getProperties() + dst_audioStream = dst_properties.getAudioProperties()[0] + + assert_equals( "pcm_s24le", dst_audioStream.getCodecName() ) + assert_equals( "s32", dst_audioStream.getSampleFormatName() ) + assert_equals( "signed 32 bits", dst_audioStream.getSampleFormatLongName() ) + assert_equals( 48000, dst_audioStream.getSampleRate() ) + assert_equals( 1, dst_audioStream.getNbChannels() ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy