diff --git a/src/AvTranscoder/Library.cpp b/src/AvTranscoder/Library.cpp index 80cedf17..961a8906 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 @@ -94,6 +96,8 @@ Libraries getLibraries() #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; } 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/common.cpp b/src/AvTranscoder/common.cpp index 762212ec..5b683c83 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) 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()); } 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; } diff --git a/src/AvTranscoder/decoder/AudioGenerator.hpp b/src/AvTranscoder/decoder/AudioGenerator.hpp index 4f677043..16b6e16e 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.hpp +++ b/src/AvTranscoder/decoder/AudioGenerator.hpp @@ -22,8 +22,8 @@ class AvExport AudioGenerator : public IDecoder void setNextFrame(Frame& inputFrame); private: - Frame* _inputFrame; ///< Has link (no ownership) - AudioFrame* _silent; ///< The generated silent (has ownership) + Frame* _inputFrame; ///< Has link (no ownership) + AudioFrame* _silent; ///< The generated silent (has ownership) }; } 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 diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp new file mode 100644 index 00000000..8cd40d5d --- /dev/null +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -0,0 +1,184 @@ +#include "FilterGraph.hpp" + +#include +#include +#include + +extern "C" { +#include +#include +#include +} + +#include +#include + +namespace avtranscoder +{ + +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."); +} + +FilterGraph::~FilterGraph() +{ + for(std::vector::iterator it = _filters.begin(); it < _filters.end(); ++it) + { + it = _filters.erase(it); + } + avfilter_graph_free(&_graph); +} + +void FilterGraph::process(Frame& frame) +{ + if(!hasFilters()) + { + LOG_DEBUG("No filter to process.") + return; + } + + // 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("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)); + } + } +} + +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) +{ + // push filters to the graph + pushInBuffer(frame); + for(size_t i = 1; 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()) + const int err = + avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(index + 1)->getAVFilterContext(), 0); + if(err < 0) + { + throw std::runtime_error("Error when connecting filters."); + } + } + + // 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: " + 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" << std::hex << 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 new file mode 100644 index 00000000..bd42f979 --- /dev/null +++ b/src/AvTranscoder/filter/FilterGraph.hpp @@ -0,0 +1,90 @@ +#ifndef _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_ +#define _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_ + +#include +#include +#include +#include + +#include + +struct AVFilterGraph; + +namespace avtranscoder +{ + +/** + * @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 +{ +private: + FilterGraph(const FilterGraph& otherFilterGraph); + FilterGraph& operator=(const FilterGraph& otherFilterGraph); + +public: + FilterGraph(const ICodec& codec); + ~FilterGraph(); + + /** + * @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(); } + + /** + * @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; ///< 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; +}; +} + +#endif 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 diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 0ae48dba..c88870b5 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; } @@ -461,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) { diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 1bdc45c8..7c8a8c5d 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -10,7 +10,7 @@ #include #include - +#include #include namespace avtranscoder @@ -76,10 +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 reference to the stream which unwraps data + /// Returns a pointer to the object which manage filtering + FilterGraph* getFilterGraph() const { return _filterGraph; } + + /// 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; } @@ -131,6 +134,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. 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.") 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