From 88af737ae79d871d3243f4dcefed3155a387beac Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 26 Oct 2016 11:19:12 +0200 Subject: [PATCH 01/58] AudioFrame: fix memory leak when allocating a new audio buffer See ffmpeg doc: https://www.ffmpeg.org/doxygen/2.7/group__lavu__sampmanip.html#ga4db4c77f928d32c7d8854732f50b8c04 --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 10 ++++++++++ src/AvTranscoder/data/decoded/AudioFrame.hpp | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index baffbbee..82a1dfdb 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -57,6 +57,11 @@ std::string AudioFrame::getChannelLayoutDesc() const return std::string(buf); } +AudioFrame::~AudioFrame() +{ + freeAVSample(); +} + size_t AudioFrame::getSize() const { if(getSampleFormat() == AV_SAMPLE_FMT_NONE) @@ -104,6 +109,11 @@ void AudioFrame::allocateAVSample(const AudioFrameDesc& desc) } } +void AudioFrame::freeAVSample() +{ + av_freep(&_frame->data[0]); +} + void AudioFrame::assign(const unsigned char value) { // Create the audio buffer diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index ea644498..0ab21935 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -41,6 +41,7 @@ class AvExport AudioFrame : public Frame */ AudioFrame(const AudioFrameDesc& ref); AudioFrame(const Frame& otherFrame); + ~AudioFrame(); size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } size_t getNbChannels() const { return av_frame_get_channels(_frame); } @@ -69,9 +70,16 @@ class AvExport AudioFrame : public Frame private: /** * @brief Allocate the audio buffer of the frame. + * @warning The allocated data should be freed by the caller. + * @see freeAVSample */ void allocateAVSample(const AudioFrameDesc& ref); + /** + * @brief Free the audio buffer of the frame. + */ + void freeAVSample(); + /** * @note To allocate new audio buffer if needed. * @see allocateAVSample From 8d0ac3f3410e37053d11265a49e310c4b7a90ef7 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 26 Oct 2016 11:19:53 +0200 Subject: [PATCH 02/58] VideoFrame: fix memory leak when allocating a new picture See ffmpeg doc: https://www.ffmpeg.org/doxygen/3.0/group__lavc__picture.html#ga40412936f5777964c0c80b1742ec58ef --- src/AvTranscoder/data/decoded/VideoFrame.cpp | 10 ++++++++++ src/AvTranscoder/data/decoded/VideoFrame.hpp | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index c7f8dcf8..e67dfd3c 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -56,6 +56,11 @@ VideoFrame::VideoFrame(const Frame& otherFrame) { } +VideoFrame::~VideoFrame() +{ + freeAVPicture(); +} + size_t VideoFrame::getSize() const { if(getPixelFormat() == AV_PIX_FMT_NONE) @@ -87,6 +92,11 @@ void VideoFrame::allocateAVPicture(const VideoFrameDesc& desc) _frame->format = desc._pixelFormat; } +void VideoFrame::freeAVPicture() +{ + avpicture_free(reinterpret_cast(_frame)); +} + void VideoFrame::assign(const unsigned char value) { // Create the image buffer diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index eeea4b09..b5e44524 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -45,6 +45,7 @@ class AvExport VideoFrame : public Frame public: VideoFrame(const VideoFrameDesc& ref); VideoFrame(const Frame& otherFrame); + ~VideoFrame(); size_t getWidth() const { return _frame->width; } size_t getHeight() const { return _frame->height; } @@ -68,9 +69,16 @@ class AvExport VideoFrame : public Frame private: /** * @brief Allocate the image buffer of the frame. + * @warning The allocated data should be freed by the caller. + * @see freeAVPicture */ void allocateAVPicture(const VideoFrameDesc& desc); + /** + * @brief Free the image buffer of the frame. + */ + void freeAVPicture(); + /** * @note To allocate new image buffer if needed. * @see allocateAVPicture From aade71ff25e1e84676a132f763c2ec4390bdc367 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 26 Oct 2016 12:18:23 +0200 Subject: [PATCH 03/58] Frame: add isRefCounted method Will be used when calling refFrame. --- src/AvTranscoder/data/decoded/Frame.cpp | 5 +++++ src/AvTranscoder/data/decoded/Frame.hpp | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index 69b15ff4..a4397c6c 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -68,6 +68,11 @@ void Frame::copyProperties(const Frame& otherFrame) av_frame_copy_props(_frame, &otherFrame.getAVFrame()); } +bool Frame::isRefCounted() const +{ + return _frame->buf[0]; +} + void Frame::refFrame(const Frame& otherFrame) { const int ret = av_frame_ref(_frame, &otherFrame.getAVFrame()); diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index 53d5ce00..c93c03e4 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -52,8 +52,10 @@ class AvExport Frame /** * @brief Copy the data of the given Frame. - * @note This function does not allocate anything: the current frame must be already initialized and + * This function does not allocate anything: the current frame must be already initialized and * allocated with the same parameters as the given frame, to be ready for memcpy instructions. + * @note It copies the frame data (i.e. the contents of the data / extended data arrays), not any other properties. + * @see copyProperties */ void copyData(const Frame& frameToRef); @@ -62,6 +64,11 @@ class AvExport Frame */ void copyProperties(const Frame& otherFrame); + /** + * @brief If the data buffer of the frame refers to data allocated by an other frame. + */ + bool isRefCounted() const; + /** * @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 by calling clear method or the destructor of the referenced frame. From 58cfca0dad0839d8438fed21210bfa9292e62873 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 26 Oct 2016 12:37:22 +0200 Subject: [PATCH 04/58] Frame: rename clear method to unrefFrame Symmetry with refFrame. --- src/AvTranscoder/data/decoded/Frame.cpp | 2 +- src/AvTranscoder/data/decoded/Frame.hpp | 2 +- src/AvTranscoder/decoder/AudioGenerator.cpp | 2 +- src/AvTranscoder/decoder/VideoGenerator.cpp | 2 +- src/AvTranscoder/filter/FilterGraph.cpp | 2 +- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index a4397c6c..9defe78f 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -82,7 +82,7 @@ void Frame::refFrame(const Frame& otherFrame) } } -void Frame::clear() +void Frame::unrefFrame() { av_frame_unref(_frame); } diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index c93c03e4..3c5a0b38 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -79,7 +79,7 @@ class AvExport Frame /** * @brief Unreference all the buffers referenced by frame and reset the frame fields. */ - void clear(); + void unrefFrame(); /** * @return If it corresponds to a valid audio frame. diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index 48a0b424..8b10e0ab 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -25,7 +25,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) if(! frameBuffer.isAudioFrame()) { LOG_WARN("The given frame to put data is not a valid audio frame: try to reallocate it.") - frameBuffer.clear(); + frameBuffer.unrefFrame(); static_cast(frameBuffer).allocateAVSample(_frameDesc); } diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index 58ba9509..72600cb4 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -25,7 +25,7 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) if(! frameBuffer.isVideoFrame()) { LOG_WARN("The given frame to put data is not a valid video frame: try to reallocate it.") - frameBuffer.clear(); + frameBuffer.unrefFrame(); static_cast(frameBuffer).allocateAVPicture(_frameDesc); } diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index c629c81d..292fa2cf 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -40,7 +40,7 @@ void FilterGraph::process(const std::vector& inputs, Frame& output) if(!hasFilters()) { LOG_DEBUG("No filter to process: reference first input frame to the given output.") - output.clear(); + output.unrefFrame(); output.refFrame(*inputs.at(0)); return; } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 25a63d1b..df5b35ca 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -550,7 +550,7 @@ bool StreamTranscoder::processTranscode() LOG_DEBUG("Convert") _transform->convert(*_filteredData, *_transformedData); - _filteredData->clear(); + _filteredData->unrefFrame(); LOG_DEBUG("Encode") _outputEncoder->encodeFrame(*_transformedData, data); From 6e4d7d73698dcab2bb166ab4251ce1c325c39a1b Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 26 Oct 2016 12:44:49 +0200 Subject: [PATCH 05/58] Frame: add freeAVFrame private method To keep symmetry with allocateAVFrame private method. --- src/AvTranscoder/data/decoded/Frame.cpp | 31 ++++++++++++++----------- src/AvTranscoder/data/decoded/Frame.hpp | 1 + 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index 9defe78f..d8386d8c 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -34,19 +34,7 @@ void Frame::operator=(const Frame& otherFrame) Frame::~Frame() { - if(_frame != NULL) - { -#if LIBAVCODEC_VERSION_MAJOR > 54 - av_frame_free(&_frame); -#else -#if LIBAVCODEC_VERSION_MAJOR > 53 - avcodec_free_frame(&_frame); -#else - av_free(_frame); -#endif -#endif - _frame = NULL; - } + freeAVFrame(); } int Frame::getEncodedSize() const @@ -100,6 +88,23 @@ void Frame::allocateAVFrame() } } +void Frame::freeAVFrame() +{ + if(_frame != NULL) + { +#if LIBAVCODEC_VERSION_MAJOR > 54 + av_frame_free(&_frame); +#else +#if LIBAVCODEC_VERSION_MAJOR > 53 + avcodec_free_frame(&_frame); +#else + av_free(_frame); +#endif +#endif + _frame = NULL; + } +} + bool Frame::isAudioFrame() const { if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1) diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index 3c5a0b38..845f6757 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -101,6 +101,7 @@ class AvExport Frame private: void allocateAVFrame(); + void freeAVFrame(); protected: AVFrame* _frame; From 6954832163623fd75ee85ad360e85a0f688b155c Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 09:06:32 +0200 Subject: [PATCH 06/58] ICodec: refactor how to free the AVCodecContext * Same behavior as before. * But this call is explicitly mention in the documentation. --- src/AvTranscoder/codec/ICodec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/codec/ICodec.cpp b/src/AvTranscoder/codec/ICodec.cpp index a7aaae1a..d5f66e1e 100644 --- a/src/AvTranscoder/codec/ICodec.cpp +++ b/src/AvTranscoder/codec/ICodec.cpp @@ -49,7 +49,7 @@ ICodec::~ICodec() if(!_isCodecContextAllocated) return; - av_free(_avCodecContext); + avcodec_free_context(&_avCodecContext); _avCodecContext = NULL; } From 0654fa870e6dda3b5737e53d5da8d44e4c30dd91 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 09:34:47 +0200 Subject: [PATCH 07/58] Frame: remove method to ref/unref an other frame * The way to reference other frame in ffmpeg/libav is tricky. For example, if src is not reference counted, new buffers are allocated and the data is copied. And this case leads us to memory leaks in our program! * See the code: https://www.ffmpeg.org/doxygen/2.6/frame_8c_source.html#l00278 * Because we do not reference a lot of frames in our code, to avoid an overhead of complexity this commit removes these methods. * Instead, we copy the data. --- src/AvTranscoder/data/decoded/Frame.cpp | 35 +++---------------- src/AvTranscoder/data/decoded/Frame.hpp | 19 +--------- src/AvTranscoder/decoder/AudioGenerator.cpp | 4 +-- src/AvTranscoder/decoder/VideoGenerator.cpp | 2 +- src/AvTranscoder/filter/FilterGraph.cpp | 3 +- .../transcoder/StreamTranscoder.cpp | 2 -- 6 files changed, 10 insertions(+), 55 deletions(-) diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index d8386d8c..6c585841 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -14,22 +14,16 @@ Frame::Frame() Frame::Frame(const Frame& otherFrame) : _frame(NULL) { - // allocate frame allocateAVFrame(); - // check if the frame could be a valid video/audio frame - if(otherFrame.getAVFrame().format == -1) - return; - // reference the other frame - refFrame(otherFrame); + copyProperties(otherFrame); + copyData(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); + allocateAVFrame(); + copyProperties(otherFrame); + copyData(otherFrame); } Frame::~Frame() @@ -56,25 +50,6 @@ void Frame::copyProperties(const Frame& otherFrame) av_frame_copy_props(_frame, &otherFrame.getAVFrame()); } -bool Frame::isRefCounted() const -{ - return _frame->buf[0]; -} - -void Frame::refFrame(const Frame& otherFrame) -{ - const int ret = av_frame_ref(_frame, &otherFrame.getAVFrame()); - if(ret < 0) - { - throw std::ios_base::failure("Unable to reference other frame: " + getDescriptionFromErrorCode(ret)); - } -} - -void Frame::unrefFrame() -{ - av_frame_unref(_frame); -} - void Frame::allocateAVFrame() { #if LIBAVCODEC_VERSION_MAJOR > 54 diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index 845f6757..1bda2ebc 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -23,7 +23,7 @@ class AvExport Frame Frame(); //@{ - // @brief Copy properties and reference data of the other frame. + // @brief Allocate a new frame that references the same data as the given frame. Frame(const Frame& otherFrame); void operator=(const Frame& otherFrame); //@} @@ -64,23 +64,6 @@ class AvExport Frame */ void copyProperties(const Frame& otherFrame); - /** - * @brief If the data buffer of the frame refers to data allocated by an other frame. - */ - bool isRefCounted() const; - - /** - * @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 by calling clear method or the destructor of the referenced frame. - * @see clear - */ - void refFrame(const Frame& otherFrame); - - /** - * @brief Unreference all the buffers referenced by frame and reset the frame fields. - */ - void unrefFrame(); - /** * @return If it corresponds to a valid audio frame. * @see AudioFrame diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index 8b10e0ab..7181ec0b 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -25,7 +25,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) if(! frameBuffer.isAudioFrame()) { LOG_WARN("The given frame to put data is not a valid audio frame: try to reallocate it.") - frameBuffer.unrefFrame(); + static_cast(frameBuffer).freeAVSample(); static_cast(frameBuffer).allocateAVSample(_frameDesc); } @@ -50,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.refFrame(*_silent); + frameBuffer.copyData(*_silent); } // Take audio frame from _inputFrame else diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index 72600cb4..5ee63f4a 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -25,7 +25,7 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) if(! frameBuffer.isVideoFrame()) { LOG_WARN("The given frame to put data is not a valid video frame: try to reallocate it.") - frameBuffer.unrefFrame(); + static_cast(frameBuffer).freeAVPicture(); static_cast(frameBuffer).allocateAVPicture(_frameDesc); } diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index 292fa2cf..f43ce259 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -40,8 +40,7 @@ void FilterGraph::process(const std::vector& inputs, Frame& output) if(!hasFilters()) { LOG_DEBUG("No filter to process: reference first input frame to the given output.") - output.unrefFrame(); - output.refFrame(*inputs.at(0)); + output.copyData(*inputs.at(0)); return; } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index df5b35ca..9b72ec49 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -550,8 +550,6 @@ bool StreamTranscoder::processTranscode() LOG_DEBUG("Convert") _transform->convert(*_filteredData, *_transformedData); - _filteredData->unrefFrame(); - LOG_DEBUG("Encode") _outputEncoder->encodeFrame(*_transformedData, data); } From 83a63a6a50106849e8af6952971d28cd25becc5f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 10:01:20 +0200 Subject: [PATCH 08/58] FormatContext: fix destructor by freeing the streams added * According to the doc, for each new stream added, the user should call avcodec_close and avformat_free_context. * See the doc: https://www.ffmpeg.org/doxygen/3.0/group__lavf__core.html#gadcb0fd3e507d9b58fe78f61f8ad39827 --- src/AvTranscoder/file/FormatContext.cpp | 10 +++++++--- src/AvTranscoder/file/FormatContext.hpp | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/file/FormatContext.cpp b/src/AvTranscoder/file/FormatContext.cpp index efc56bfe..7e1f1a05 100644 --- a/src/AvTranscoder/file/FormatContext.cpp +++ b/src/AvTranscoder/file/FormatContext.cpp @@ -12,7 +12,7 @@ FormatContext::FormatContext(const std::string& filename, int req_flags, AVDicti , _options() , _isOpen(false) { - int ret = avformat_open_input(&_avFormatContext, filename.c_str(), NULL, options); + const int ret = avformat_open_input(&_avFormatContext, filename.c_str(), NULL, options); if(ret < 0) { std::string msg = "Unable to open file "; @@ -44,10 +44,14 @@ FormatContext::~FormatContext() if(!_avFormatContext) return; + // free the streams added + for(unsigned int i = 0; i < _avFormatContext->nb_streams; ++i) + avcodec_close(_avFormatContext->streams[i]->codec); + + // free the format context if(_isOpen) avformat_close_input(&_avFormatContext); - else - avformat_free_context(_avFormatContext); + avformat_free_context(_avFormatContext); _avFormatContext = NULL; } diff --git a/src/AvTranscoder/file/FormatContext.hpp b/src/AvTranscoder/file/FormatContext.hpp index 7e8cce88..cce31442 100644 --- a/src/AvTranscoder/file/FormatContext.hpp +++ b/src/AvTranscoder/file/FormatContext.hpp @@ -23,11 +23,13 @@ class AvExport FormatContext public: /** * @brief Allocate an AVFormatContext by opening an input file + * @see InputFile */ FormatContext(const std::string& filename, int req_flags = 0, AVDictionary** options = NULL); /** * @brief Allocate an AVFormatContext with default values + * @see OutputFile */ FormatContext(int req_flags = 0); From c2edefe9b42c9095dfcf1f9386c316c2b4061433 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 11:53:12 +0200 Subject: [PATCH 09/58] StreamTranscoder: skip filtering step if there is no filters * This is a common case, without filters added. * This avoid a copy of the data of each encoded frame. --- src/AvTranscoder/filter/FilterGraph.cpp | 7 ------- src/AvTranscoder/filter/FilterGraph.hpp | 2 +- .../transcoder/StreamTranscoder.cpp | 18 +++++++++++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index f43ce259..b1674460 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -37,13 +37,6 @@ FilterGraph::~FilterGraph() void FilterGraph::process(const std::vector& inputs, Frame& output) { - if(!hasFilters()) - { - LOG_DEBUG("No filter to process: reference first input frame to the given output.") - output.copyData(*inputs.at(0)); - return; - } - // init filter graph if(!_isInit) init(inputs, output); diff --git a/src/AvTranscoder/filter/FilterGraph.hpp b/src/AvTranscoder/filter/FilterGraph.hpp index 86a5ea4c..ee993f0a 100644 --- a/src/AvTranscoder/filter/FilterGraph.hpp +++ b/src/AvTranscoder/filter/FilterGraph.hpp @@ -57,12 +57,12 @@ class AvExport FilterGraph */ void process(const std::vector& inputs, Frame& output); -private: /** * @return If at least one filter has been added to the filter graph */ bool hasFilters() const { return !_filters.empty(); } +private: /** * @brief Initialize the graph of filters to process. * @see pushFilterToGraph diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 9b72ec49..f3eb4c36 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -526,6 +526,7 @@ bool StreamTranscoder::processTranscode() LOG_DEBUG("StreamTranscoder::processTranscode") + // Decode LOG_DEBUG("Decode next frame") bool decodingStatus = true; for(size_t index = 0; index < _generators.size(); ++index) @@ -541,14 +542,24 @@ bool StreamTranscoder::processTranscode() decodingStatus = decodingStatus && _currentDecoder->decodeNextFrame(*_decodedData.at(index)); } + // Transform CodedData data; if(decodingStatus) { - LOG_DEBUG("Filtering") - _filterGraph->process(_decodedData, *_filteredData); + Frame* dataToTransform = NULL; + if(_filterGraph->hasFilters()) + { + LOG_DEBUG("Filtering") + _filterGraph->process(_decodedData, *_filteredData); + dataToTransform = _filteredData; + } + else + { + dataToTransform = _decodedData.at(0); + } LOG_DEBUG("Convert") - _transform->convert(*_filteredData, *_transformedData); + _transform->convert(*dataToTransform, *_transformedData); LOG_DEBUG("Encode") _outputEncoder->encodeFrame(*_transformedData, data); @@ -567,6 +578,7 @@ bool StreamTranscoder::processTranscode() } } + // Wrap LOG_DEBUG("wrap (" << data.getSize() << " bytes)") const IOutputStream::EWrappingStatus wrappingStatus = _outputStream->wrap(data); switch(wrappingStatus) From 8635b3bdeda88b1f717952dd8bc431406ba3b53f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 15:30:01 +0200 Subject: [PATCH 10/58] VideoProperties: refactor how to decode the first frames to get info Because we are using ffmpeg/libav functions, we should use ffmpeg/libav structures too. --- src/AvTranscoder/properties/VideoProperties.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/AvTranscoder/properties/VideoProperties.cpp b/src/AvTranscoder/properties/VideoProperties.cpp index 99b3bde2..69dd02da 100644 --- a/src/AvTranscoder/properties/VideoProperties.cpp +++ b/src/AvTranscoder/properties/VideoProperties.cpp @@ -1,6 +1,5 @@ #include "VideoProperties.hpp" -#include #include #include #include @@ -342,7 +341,7 @@ size_t VideoProperties::getBitRate() const // discard no frame type when decode _codecContext->skip_frame = AVDISCARD_NONE; - Frame frame; + AVFrame avFrame; AVPacket pkt; av_init_packet(&pkt); avcodec_open2(_codecContext, _codec, NULL); @@ -357,11 +356,10 @@ size_t VideoProperties::getBitRate() const { if(pkt.stream_index == (int)_streamIndex) { - avcodec_decode_video2(_codecContext, &frame.getAVFrame(), &gotFrame, &pkt); + avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt); if(gotFrame) { // check distance between key frames - AVFrame& avFrame = frame.getAVFrame(); if(avFrame.pict_type == AV_PICTURE_TYPE_I) { if(positionOfFirstKeyFrame == -1) @@ -375,7 +373,7 @@ size_t VideoProperties::getBitRate() const if(positionOfLastKeyFrame == -1) { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 7, 100) - gopFramesSize += frame.getEncodedSize(); + gopFramesSize += av_frame_get_pkt_size(&avFrame); #else gopFramesSize += pkt.size; #endif @@ -567,7 +565,7 @@ void VideoProperties::analyseGopStructure(IProgress& progress) // Initialize the AVCodecContext to use the given AVCodec avcodec_open2(_codecContext, _codec, NULL); - Frame frame; + AVFrame avFrame; size_t count = 0; int gotFrame = 0; int positionOfFirstKeyFrame = -1; @@ -577,13 +575,11 @@ void VideoProperties::analyseGopStructure(IProgress& progress) { if(pkt.stream_index == (int)_streamIndex) { - avcodec_decode_video2(_codecContext, &frame.getAVFrame(), &gotFrame, &pkt); + avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt); if(gotFrame) { - AVFrame& avFrame = frame.getAVFrame(); - _gopStructure.push_back( - std::make_pair(av_get_picture_type_char(avFrame.pict_type), frame.getEncodedSize())); + std::make_pair(av_get_picture_type_char(avFrame.pict_type), av_frame_get_pkt_size(&avFrame))); _isInterlaced = avFrame.interlaced_frame; _isTopFieldFirst = avFrame.top_field_first; if(avFrame.pict_type == AV_PICTURE_TYPE_I) From 6f874356f5b08b213c7b771b7537d0e9f3e9abb2 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 15:32:16 +0200 Subject: [PATCH 11/58] Video/Audio frames: data buffer needs to be allocated manually * A data buffer is not allocated by default in the constructor. * Frame is now an abstract class: easier to call allocateData and freeData methods. * Depending on the case, we could manipulate frame with data allocated elsewhere. For example, an empty frame is given to a decoder, which is responsible to allocate and free the data buffers. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 36 +++++++++--------- src/AvTranscoder/data/decoded/AudioFrame.hpp | 33 +++++------------ src/AvTranscoder/data/decoded/Frame.cpp | 21 +---------- src/AvTranscoder/data/decoded/Frame.hpp | 37 ++++++++++++------- src/AvTranscoder/data/decoded/VideoFrame.cpp | 35 +++++++++--------- src/AvTranscoder/data/decoded/VideoFrame.hpp | 32 +++++----------- src/AvTranscoder/decoder/AudioDecoder.cpp | 6 ++- src/AvTranscoder/decoder/AudioGenerator.cpp | 1 + .../transcoder/StreamTranscoder.cpp | 12 ++++++ 9 files changed, 97 insertions(+), 116 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index 82a1dfdb..aad5ccde 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -39,15 +39,15 @@ void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) _sampleFormat = getAVSampleFormat(profile.find(constants::avProfileSampleFormat)->second.c_str()); } -AudioFrame::AudioFrame(const AudioFrameDesc& ref) +AudioFrame::AudioFrame(const AudioFrameDesc& desc) : Frame() { - allocateAVSample(ref); -} - -AudioFrame::AudioFrame(const Frame& otherFrame) - : Frame(otherFrame) -{ + // Set Frame properties + av_frame_set_sample_rate(_frame, desc._sampleRate); + av_frame_set_channels(_frame, desc._nbChannels); + av_frame_set_channel_layout(_frame, av_get_default_channel_layout(desc._nbChannels)); + _frame->format = desc._sampleFormat; + _frame->nb_samples = desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 } std::string AudioFrame::getChannelLayoutDesc() const @@ -59,7 +59,8 @@ std::string AudioFrame::getChannelLayoutDesc() const AudioFrame::~AudioFrame() { - freeAVSample(); + if(_dataAllocated) + freeData(); } size_t AudioFrame::getSize() const @@ -83,19 +84,16 @@ size_t AudioFrame::getSize() const return size; } -void AudioFrame::allocateAVSample(const AudioFrameDesc& desc) +void AudioFrame::allocateData() { - // Set Frame properties - av_frame_set_sample_rate(_frame, desc._sampleRate); - av_frame_set_channels(_frame, desc._nbChannels); - av_frame_set_channel_layout(_frame, av_get_default_channel_layout(desc._nbChannels)); - _frame->format = desc._sampleFormat; - _frame->nb_samples = desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + if(_dataAllocated) + return; // Allocate data const int align = 0; + const AVSampleFormat format = static_cast(_frame->format); const int ret = - av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, desc._sampleFormat, align); + av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, format, align); if(ret < 0) { std::stringstream os; @@ -104,14 +102,16 @@ void AudioFrame::allocateAVSample(const AudioFrameDesc& desc) os << "nb channels = " << _frame->channels << ", "; os << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; os << "nb samples = " << _frame->nb_samples << ", "; - os << "sample format = " << getSampleFormatName(desc._sampleFormat); + os << "sample format = " << getSampleFormatName(format); throw std::runtime_error(os.str()); } + _dataAllocated = true; } -void AudioFrame::freeAVSample() +void AudioFrame::freeData() { av_freep(&_frame->data[0]); + _dataAllocated = false; } void AudioFrame::assign(const unsigned char value) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 0ab21935..f99f4f9b 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -36,12 +36,16 @@ struct AvExport AudioFrameDesc class AvExport AudioFrame : public Frame { public: + AudioFrame(const AudioFrameDesc& desc); + ~AudioFrame(); + /** - * @note Allocated data will be initialized to silence. + * @brief Allocated data will be initialized to silence. + * @warning The allocated data should be freed by the caller. + * @see freeData */ - AudioFrame(const AudioFrameDesc& ref); - AudioFrame(const Frame& otherFrame); - ~AudioFrame(); + void allocateData(); + void freeData(); size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } size_t getNbChannels() const { return av_frame_get_channels(_frame); } @@ -51,7 +55,7 @@ class AvExport AudioFrame : public Frame size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); } - size_t getSize() const; ///< in bytes + size_t getSize() const; void setNbSamplesPerChannel(const size_t nbSamples) { _frame->nb_samples = nbSamples; } @@ -66,25 +70,6 @@ class AvExport AudioFrame : public Frame * @see getSize */ void assign(const unsigned char* ptrValue); - -private: - /** - * @brief Allocate the audio buffer of the frame. - * @warning The allocated data should be freed by the caller. - * @see freeAVSample - */ - void allocateAVSample(const AudioFrameDesc& ref); - - /** - * @brief Free the audio buffer of the frame. - */ - void freeAVSample(); - - /** - * @note To allocate new audio buffer if needed. - * @see allocateAVSample - */ - friend class AudioGenerator; }; } diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index 6c585841..867786d2 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -7,35 +7,16 @@ namespace avtranscoder Frame::Frame() : _frame(NULL) + , _dataAllocated(false) { allocateAVFrame(); } -Frame::Frame(const Frame& otherFrame) - : _frame(NULL) -{ - allocateAVFrame(); - copyProperties(otherFrame); - copyData(otherFrame); -} - -void Frame::operator=(const Frame& otherFrame) -{ - allocateAVFrame(); - copyProperties(otherFrame); - copyData(otherFrame); -} - Frame::~Frame() { freeAVFrame(); } -int Frame::getEncodedSize() const -{ - return av_frame_get_pkt_size(_frame); -} - void Frame::copyData(const Frame& frameToRef) { const int ret = av_frame_copy(_frame, &frameToRef.getAVFrame()); diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/Frame.hpp index 1bda2ebc..ac2bb999 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/Frame.hpp @@ -12,24 +12,39 @@ namespace avtranscoder /** * @brief This class describes decoded (raw) audio or video data. + * This class is abstract. + * @see VideoFrame + * @see AudioFrame */ class AvExport Frame { public: /** * @brief Allocate an empty frame. - * @warn This only allocates the AVFrame itself, not the data buffers. + * @warning This only allocates the AVFrame itself, not the data buffers. + * Depending on the case, we could manipulate frame with data allocated elsewhere. + * For example, an empty frame is given to a decoder, which is responsible to allocate and free the data buffers. */ Frame(); - //@{ - // @brief Allocate a new frame that references the same data as the given frame. - Frame(const Frame& otherFrame); - void operator=(const Frame& otherFrame); - //@} - virtual ~Frame(); + /** + * @brief Allocate the buffer of the frame. + */ + virtual void allocateData() = 0; + + /** + * @brief Free the buffer of the frame. + */ + virtual void freeData() = 0; + + /** + * @brief Get the size in bytes that a video/audio buffer of the given frame properties would occupy if allocated. + * @warning This methods does not guarantee that the buffer is actually allocated. + */ + virtual size_t getSize() const = 0; + /** * @brief Get all the data of the frame. */ @@ -44,12 +59,6 @@ class AvExport Frame */ int* getLineSize() const { return _frame->linesize; } - /** - * @return Size of the corresponding packet containing the compressed frame (in bytes) - * @warning returns a negative value if the size is unknown - */ - int getEncodedSize() const; - /** * @brief Copy the data of the given Frame. * This function does not allocate anything: the current frame must be already initialized and @@ -61,6 +70,7 @@ class AvExport Frame /** * @brief Copy all the fields that do not affect the data layout in the buffers. + * @warning The info checked when copying data of an other frame (width/height, channels...) are not copied. */ void copyProperties(const Frame& otherFrame); @@ -88,6 +98,7 @@ class AvExport Frame protected: AVFrame* _frame; + bool _dataAllocated; }; } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index e67dfd3c..4e6e2f25 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -45,20 +45,18 @@ void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) _fps = atof(profile.find(constants::avProfileFrameRate)->second.c_str()); } -VideoFrame::VideoFrame(const VideoFrameDesc& ref) +VideoFrame::VideoFrame(const VideoFrameDesc& desc) : Frame() { - allocateAVPicture(ref); -} - -VideoFrame::VideoFrame(const Frame& otherFrame) - : Frame(otherFrame) -{ + _frame->width = desc._width; + _frame->height = desc._height; + _frame->format = desc._pixelFormat; } VideoFrame::~VideoFrame() { - freeAVPicture(); + if(_dataAllocated) + freeData(); } size_t VideoFrame::getSize() const @@ -75,26 +73,29 @@ size_t VideoFrame::getSize() const return size; } -void VideoFrame::allocateAVPicture(const VideoFrameDesc& desc) +void VideoFrame::allocateData() { - const int ret = avpicture_alloc(reinterpret_cast(_frame), desc._pixelFormat, desc._width, desc._height); + if(_dataAllocated) + return; + + const AVPixelFormat format = static_cast(_frame->format); + const int ret = avpicture_alloc(reinterpret_cast(_frame), format, _frame->width, _frame->height); if(ret < 0) { std::stringstream os; os << "Unable to allocate an image frame of "; - os << "width = " << desc._width << ", "; - os << "height = " << desc._height << ", "; - os << "pixel format = " << desc._pixelFormat; + os << "width = " << _frame->width << ", "; + os << "height = " << _frame->height << ", "; + os << "pixel format = " << getPixelFormatName(format); throw std::runtime_error(os.str()); } - _frame->width = desc._width; - _frame->height = desc._height; - _frame->format = desc._pixelFormat; + _dataAllocated = true; } -void VideoFrame::freeAVPicture() +void VideoFrame::freeData() { avpicture_free(reinterpret_cast(_frame)); + _dataAllocated = false; } void VideoFrame::assign(const unsigned char value) diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index b5e44524..01015215 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -43,16 +43,23 @@ struct AvExport VideoFrameDesc class AvExport VideoFrame : public Frame { public: - VideoFrame(const VideoFrameDesc& ref); - VideoFrame(const Frame& otherFrame); + VideoFrame(const VideoFrameDesc& desc); ~VideoFrame(); + /** + * @brief Allocate the image buffer of the frame. + * @warning The allocated data should be freed by the caller. + * @see freeData + */ + void allocateData(); + void freeData(); + size_t getWidth() const { return _frame->width; } size_t getHeight() const { return _frame->height; } AVPixelFormat getPixelFormat() const { return static_cast(_frame->format); } VideoFrameDesc desc() const { return VideoFrameDesc(getWidth(), getHeight(), getPixelFormat()); } - size_t getSize() const; ///< in bytes/** + size_t getSize() const; /** * @brief Assign the given value to all the data of the picture. @@ -65,25 +72,6 @@ class AvExport VideoFrame : public Frame * @see getSize */ void assign(const unsigned char* ptrValue); - -private: - /** - * @brief Allocate the image buffer of the frame. - * @warning The allocated data should be freed by the caller. - * @see freeAVPicture - */ - void allocateAVPicture(const VideoFrameDesc& desc); - - /** - * @brief Free the image buffer of the frame. - */ - void freeAVPicture(); - - /** - * @note To allocate new image buffer if needed. - * @see allocateAVPicture - */ - friend class VideoGenerator; }; } diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 6ea1afb6..100d9807 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -132,7 +132,8 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector return decodeNextFrame(frameBuffer); // else decode all data in an intermediate buffer - AudioFrame allDataOfNextFrame(frameBuffer); + AudioFrame& audioBuffer = static_cast(frameBuffer); + AudioFrame allDataOfNextFrame(AudioFrameDesc(audioBuffer.getSampleRate(), srcNbChannels, audioBuffer.getSampleFormat())); if(!decodeNextFrame(allDataOfNextFrame)) return false; @@ -158,9 +159,10 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector throw std::runtime_error(msg.str()); } } + // allocate data to store the subpart of the next frame + audioBuffer.allocateData(); // copy frame properties of decoded frame - AudioFrame& audioBuffer = static_cast(frameBuffer); audioBuffer.copyProperties(allDataOfNextFrame); av_frame_set_channels(&audioBuffer.getAVFrame(), channelIndexArray.size()); av_frame_set_channel_layout(&audioBuffer.getAVFrame(), av_get_default_channel_layout(channelIndexArray.size())); diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index 7181ec0b..c86b73eb 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -36,6 +36,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) if(!_silent) { _silent = new AudioFrame(_frameDesc); + _silent->allocateData(); std::stringstream msg; msg << "Generate a silence with the following features:" << std::endl; diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index f3eb4c36..6da7152a 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -59,7 +59,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu // buffers to process _decodedData.push_back(new VideoFrame(inputFrameDesc)); _filteredData = new VideoFrame(inputFrameDesc); + _filteredData->allocateData(); _transformedData = new VideoFrame(inputFrameDesc); + _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -95,7 +97,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu // buffers to process _decodedData.push_back(new AudioFrame(inputFrameDesc)); _filteredData = new AudioFrame(inputFrameDesc); + _filteredData->allocateData(); _transformedData = new AudioFrame(inputFrameDesc); + _transformedData->allocateData(); // transform _transform = new AudioTransform(); @@ -175,7 +179,9 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre // buffers to process _filteredData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); + _filteredData->allocateData(); _transformedData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); + _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -213,7 +219,9 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre inputFrameDesc._nbChannels = nbOutputChannels; _filteredData = new AudioFrame(inputFrameDesc); + _filteredData->allocateData(); _transformedData = new AudioFrame(outputAudio->getAudioCodec().getAudioFrameDesc()); + _transformedData->allocateData(); // transform _transform = new AudioTransform(); @@ -314,7 +322,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: outputFrameDesc.setParameters(profile); _decodedData.push_back(new VideoFrame(inputFrameDesc)); _filteredData = new VideoFrame(inputFrameDesc); + _filteredData->allocateData(); _transformedData = new VideoFrame(outputFrameDesc); + _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -347,7 +357,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: outputFrameDesc.setParameters(profile); _decodedData.push_back(new AudioFrame(inputFrameDesc)); _filteredData = new AudioFrame(inputFrameDesc); + _filteredData->allocateData(); _transformedData = new AudioFrame(outputFrameDesc); + _transformedData->allocateData(); // transform _transform = new AudioTransform(); From 5d0f51ffb554e0f620ac5373ccc8e00d5d3d43a5 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 15:40:30 +0200 Subject: [PATCH 12/58] Video/Audio transform: add check before converting the data * Remove protected method inherited from ITransform. * Add the same series of test for the video and the audio transform. --- src/AvTranscoder/transform/AudioTransform.cpp | 29 ++++++++++--------- src/AvTranscoder/transform/AudioTransform.hpp | 5 ++-- src/AvTranscoder/transform/ITransform.hpp | 3 -- src/AvTranscoder/transform/VideoTransform.cpp | 15 ++++------ src/AvTranscoder/transform/VideoTransform.hpp | 6 ++-- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/AvTranscoder/transform/AudioTransform.cpp b/src/AvTranscoder/transform/AudioTransform.cpp index 41194a06..77401f58 100644 --- a/src/AvTranscoder/transform/AudioTransform.cpp +++ b/src/AvTranscoder/transform/AudioTransform.cpp @@ -26,8 +26,9 @@ extern "C" { #endif } -#include #include +#include +#include namespace avtranscoder { @@ -43,7 +44,7 @@ AudioTransform::~AudioTransform() FreeResampleContext(&_audioConvertContext); } -bool AudioTransform::init(const Frame& srcFrame, const Frame& dstFrame) +bool AudioTransform::init(const AudioFrame& src, const AudioFrame& dst) { // Set convert context _audioConvertContext = AllocResampleContext(); @@ -52,9 +53,6 @@ bool AudioTransform::init(const Frame& srcFrame, const Frame& dstFrame) throw std::runtime_error("unable to create audio convert context"); } - const AudioFrame& src = static_cast(srcFrame); - const AudioFrame& dst = static_cast(dstFrame); - av_opt_set_int(_audioConvertContext, "in_channel_layout", src.getChannelLayout(), 0); av_opt_set_int(_audioConvertContext, "out_channel_layout", dst.getChannelLayout(), 0); av_opt_set_int(_audioConvertContext, "in_sample_rate", src.getSampleRate(), 0); @@ -92,8 +90,17 @@ bool AudioTransform::init(const Frame& srcFrame, const Frame& dstFrame) void AudioTransform::convert(const Frame& srcFrame, Frame& dstFrame) { + const AudioFrame& src = static_cast(srcFrame); + const AudioFrame& dst = static_cast(dstFrame); + + assert(src.getSampleRate() > 0); + assert(src.getNbChannels() > 0); + assert(src.getNbSamplesPerChannel() > 0); + assert(src.getSampleFormat() != AV_SAMPLE_FMT_NONE); + assert(dst.getSize() > 0); + if(!_isInit) - _isInit = init(srcFrame, dstFrame); + _isInit = init(src, dst); // if number of samples change from previous frame const size_t nbInputSamplesPerChannel = srcFrame.getAVFrame().nb_samples; @@ -110,13 +117,9 @@ void AudioTransform::convert(const Frame& srcFrame, Frame& dstFrame) swr_convert(_audioConvertContext, dstData, nbInputSamplesPerChannel, srcData, nbInputSamplesPerChannel); #endif + // update the number of samples of the output frame if(nbOutputSamplesPerChannel < 0) - { - throw std::runtime_error("unable to convert audio samples"); - } - else - { - dstFrame.getAVFrame().nb_samples = nbOutputSamplesPerChannel; - } + throw std::runtime_error("Unable to convert audio samples"); + dstFrame.getAVFrame().nb_samples = nbOutputSamplesPerChannel; } } diff --git a/src/AvTranscoder/transform/AudioTransform.hpp b/src/AvTranscoder/transform/AudioTransform.hpp index e670605c..2d1ae38f 100644 --- a/src/AvTranscoder/transform/AudioTransform.hpp +++ b/src/AvTranscoder/transform/AudioTransform.hpp @@ -3,8 +3,7 @@ #include "ITransform.hpp" -#include -#include +#include #ifdef AVTRANSCODER_LIBAV_DEPENDENCY #define ResampleContext AVAudioResampleContext @@ -30,7 +29,7 @@ class AvExport AudioTransform : public ITransform void convert(const Frame& srcFrame, Frame& dstFrame); private: - bool init(const Frame& srcFrame, const Frame& dstFrame); + bool init(const AudioFrame& src, const AudioFrame& dst); private: ResampleContext* _audioConvertContext; diff --git a/src/AvTranscoder/transform/ITransform.hpp b/src/AvTranscoder/transform/ITransform.hpp index 0dba77d4..8b044d1b 100644 --- a/src/AvTranscoder/transform/ITransform.hpp +++ b/src/AvTranscoder/transform/ITransform.hpp @@ -15,9 +15,6 @@ class AvExport ITransform virtual ~ITransform() {} virtual void convert(const Frame& src, Frame& dst) = 0; - -protected: - virtual bool init(const Frame& src, const Frame& dst) = 0; }; } diff --git a/src/AvTranscoder/transform/VideoTransform.cpp b/src/AvTranscoder/transform/VideoTransform.cpp index 90131b1d..38ec9c0f 100644 --- a/src/AvTranscoder/transform/VideoTransform.cpp +++ b/src/AvTranscoder/transform/VideoTransform.cpp @@ -1,7 +1,5 @@ #include "VideoTransform.hpp" -#include - extern "C" { #include #include @@ -31,12 +29,8 @@ VideoTransform::~VideoTransform() sws_freeContext(_imageConvertContext); } -bool VideoTransform::init(const Frame& srcFrame, const Frame& dstFrame) +bool VideoTransform::init(const VideoFrame& src, const VideoFrame& dst) { - // Set convert context - const VideoFrame& src = static_cast(srcFrame); - const VideoFrame& dst = static_cast(dstFrame); - const AVPixelFormat srcPixelFormat = src.getPixelFormat(); const AVPixelFormat dstPixelFormat = dst.getPixelFormat(); @@ -71,12 +65,13 @@ void VideoTransform::convert(const Frame& srcFrame, Frame& dstFrame) const VideoFrame& src = static_cast(srcFrame); VideoFrame& dst = static_cast(dstFrame); - assert(src.getWidth() != 0); - assert(src.getHeight() != 0); + assert(src.getWidth() > 0); + assert(src.getHeight() > 0); assert(src.getPixelFormat() != AV_PIX_FMT_NONE); + assert(dst.getSize() > 0); if(!_isInit) - _isInit = init(srcFrame, dstFrame); + _isInit = init(src, dst); if(!_imageConvertContext) { diff --git a/src/AvTranscoder/transform/VideoTransform.hpp b/src/AvTranscoder/transform/VideoTransform.hpp index 7d17d0f7..e1bd090e 100644 --- a/src/AvTranscoder/transform/VideoTransform.hpp +++ b/src/AvTranscoder/transform/VideoTransform.hpp @@ -1,11 +1,9 @@ #ifndef _AV_TRANSCODER_ESSENCE_TRANSFORM_VIDEO_ESSENCE_TRANSFORM_HPP #define _AV_TRANSCODER_ESSENCE_TRANSFORM_VIDEO_ESSENCE_TRANSFORM_HPP -#include - #include "ITransform.hpp" -#include +#include class SwsContext; @@ -25,7 +23,7 @@ class AvExport VideoTransform : public ITransform void convert(const Frame& srcFrame, Frame& dstFrame); private: - bool init(const Frame& srcFrame, const Frame& dstFrame); + bool init(const VideoFrame& src, const VideoFrame& dst); SwsContext* _imageConvertContext; bool _isInit; From fb44ea52c9b4d7e552589e53d187d97fc9bc4f4b Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 18:19:52 +0200 Subject: [PATCH 13/58] StreamTranscoder: fix process of generated streams In that case, the data buffer of the generated data should be allocated manually. --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 6da7152a..6dbc2fbb 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -320,7 +320,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: // buffers to process VideoFrameDesc outputFrameDesc = inputFrameDesc; outputFrameDesc.setParameters(profile); - _decodedData.push_back(new VideoFrame(inputFrameDesc)); + VideoFrame* decodedData = new VideoFrame(inputFrameDesc); + decodedData->allocateData(); + _decodedData.push_back(decodedData); _filteredData = new VideoFrame(inputFrameDesc); _filteredData->allocateData(); _transformedData = new VideoFrame(outputFrameDesc); @@ -355,7 +357,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: // buffers to process AudioFrameDesc outputFrameDesc = inputFrameDesc; outputFrameDesc.setParameters(profile); - _decodedData.push_back(new AudioFrame(inputFrameDesc)); + AudioFrame* decodedData = new AudioFrame(inputFrameDesc); + decodedData->allocateData(); + _decodedData.push_back(decodedData); _filteredData = new AudioFrame(inputFrameDesc); _filteredData->allocateData(); _transformedData = new AudioFrame(outputFrameDesc); From ebd558de34cefc69fde048d5ba21ccb2dd7782de Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 18:34:05 +0200 Subject: [PATCH 14/58] Video/Audio generators: fix data generated after a transcoded stream * Solution: keep the original description when the frame was created, and use it to reallocate it when needed. * No need to check and reallocate data in the generators. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 13 ++++++++++--- src/AvTranscoder/data/decoded/AudioFrame.hpp | 8 ++++++++ src/AvTranscoder/data/decoded/VideoFrame.cpp | 12 +++++++++--- src/AvTranscoder/data/decoded/VideoFrame.hpp | 8 ++++++++ src/AvTranscoder/decoder/AudioGenerator.cpp | 8 -------- src/AvTranscoder/decoder/VideoGenerator.cpp | 8 -------- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 3 +++ 7 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index aad5ccde..70c431f1 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -41,6 +41,7 @@ void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) AudioFrame::AudioFrame(const AudioFrameDesc& desc) : Frame() + , _desc(desc) { // Set Frame properties av_frame_set_sample_rate(_frame, desc._sampleRate); @@ -89,11 +90,17 @@ void AudioFrame::allocateData() if(_dataAllocated) return; + // Set Frame properties + av_frame_set_sample_rate(_frame, _desc._sampleRate); + av_frame_set_channels(_frame, _desc._nbChannels); + av_frame_set_channel_layout(_frame, av_get_default_channel_layout(_desc._nbChannels)); + _frame->format = _desc._sampleFormat; + _frame->nb_samples = _desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + // Allocate data const int align = 0; - const AVSampleFormat format = static_cast(_frame->format); const int ret = - av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, format, align); + av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, _desc._sampleFormat, align); if(ret < 0) { std::stringstream os; @@ -102,7 +109,7 @@ void AudioFrame::allocateData() os << "nb channels = " << _frame->channels << ", "; os << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; os << "nb samples = " << _frame->nb_samples << ", "; - os << "sample format = " << getSampleFormatName(format); + os << "sample format = " << getSampleFormatName(_desc._sampleFormat); throw std::runtime_error(os.str()); } _dataAllocated = true; diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index f99f4f9b..1242892b 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -70,6 +70,14 @@ class AvExport AudioFrame : public Frame * @see getSize */ void assign(const unsigned char* ptrValue); + +private: + /** + * @brief Description of the frame to allocate. + * @warning This description could be different from the current frame (a decoder could have reseted it). + * We need to keep this description to allocate again the frame even if it was reseted. + */ + const AudioFrameDesc _desc; }; } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 4e6e2f25..3ddc0263 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -47,6 +47,7 @@ void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) VideoFrame::VideoFrame(const VideoFrameDesc& desc) : Frame() + , _desc(desc) { _frame->width = desc._width; _frame->height = desc._height; @@ -78,15 +79,20 @@ void VideoFrame::allocateData() if(_dataAllocated) return; - const AVPixelFormat format = static_cast(_frame->format); - const int ret = avpicture_alloc(reinterpret_cast(_frame), format, _frame->width, _frame->height); + // Set Frame properties + _frame->width = _desc._width; + _frame->height = _desc._height; + _frame->format = _desc._pixelFormat; + + // Allocate data + const int ret = avpicture_alloc(reinterpret_cast(_frame), _desc._pixelFormat, _desc._width, _desc._height); if(ret < 0) { std::stringstream os; os << "Unable to allocate an image frame of "; os << "width = " << _frame->width << ", "; os << "height = " << _frame->height << ", "; - os << "pixel format = " << getPixelFormatName(format); + os << "pixel format = " << getPixelFormatName(_desc._pixelFormat); throw std::runtime_error(os.str()); } _dataAllocated = true; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index 01015215..fa580307 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -72,6 +72,14 @@ class AvExport VideoFrame : public Frame * @see getSize */ void assign(const unsigned char* ptrValue); + +private: + /** + * @brief Description of the frame to allocate. + * @warning This description could be different from the current frame (a decoder could have reseted it). + * We need to keep this description to allocate again the frame even if it was reseted. + */ + const VideoFrameDesc _desc; }; } diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index c86b73eb..d1c91b77 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -21,14 +21,6 @@ AudioGenerator::~AudioGenerator() bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) { - // check the given frame - if(! frameBuffer.isAudioFrame()) - { - LOG_WARN("The given frame to put data is not a valid audio frame: try to reallocate it.") - static_cast(frameBuffer).freeAVSample(); - static_cast(frameBuffer).allocateAVSample(_frameDesc); - } - // Generate silent if(!_inputFrame) { diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index 5ee63f4a..22872ecd 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -21,14 +21,6 @@ VideoGenerator::~VideoGenerator() bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) { - // check the given frame - if(! frameBuffer.isVideoFrame()) - { - LOG_WARN("The given frame to put data is not a valid video frame: try to reallocate it.") - static_cast(frameBuffer).freeAVPicture(); - static_cast(frameBuffer).allocateAVPicture(_frameDesc); - } - // Generate black image if(!_inputFrame) { diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 6dbc2fbb..8f5d94b5 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -588,6 +588,9 @@ bool StreamTranscoder::processTranscode() if(_needToSwitchToGenerator) { switchToGeneratorDecoder(); + // force reallocation of the buffers since de decoders have cleared them + for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) + (*it)->allocateData(); return processTranscode(); } return false; From 53eff1d5f9cd9f585070fcab4e2334662e1c3681 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 18:46:36 +0200 Subject: [PATCH 15/58] Video/Audio frames: refactor how to construct frames Because the common case is to allocate the data at the same time as the frame itself, this commit adds a default parameter to the constructor of Video/Audio frames. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 8 +++--- src/AvTranscoder/data/decoded/AudioFrame.hpp | 2 +- src/AvTranscoder/data/decoded/VideoFrame.cpp | 8 +++--- src/AvTranscoder/data/decoded/VideoFrame.hpp | 2 +- src/AvTranscoder/decoder/AudioDecoder.cpp | 2 +- src/AvTranscoder/decoder/AudioGenerator.cpp | 1 - .../transcoder/StreamTranscoder.cpp | 28 ++++--------------- 7 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index 70c431f1..ac5cf46b 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -39,7 +39,7 @@ void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) _sampleFormat = getAVSampleFormat(profile.find(constants::avProfileSampleFormat)->second.c_str()); } -AudioFrame::AudioFrame(const AudioFrameDesc& desc) +AudioFrame::AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocation) : Frame() , _desc(desc) { @@ -49,6 +49,9 @@ AudioFrame::AudioFrame(const AudioFrameDesc& desc) av_frame_set_channel_layout(_frame, av_get_default_channel_layout(desc._nbChannels)); _frame->format = desc._sampleFormat; _frame->nb_samples = desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + + if(forceDataAllocation) + allocateData(); } std::string AudioFrame::getChannelLayoutDesc() const @@ -87,9 +90,6 @@ size_t AudioFrame::getSize() const void AudioFrame::allocateData() { - if(_dataAllocated) - return; - // Set Frame properties av_frame_set_sample_rate(_frame, _desc._sampleRate); av_frame_set_channels(_frame, _desc._nbChannels); diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 1242892b..6f30d4e7 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -36,7 +36,7 @@ struct AvExport AudioFrameDesc class AvExport AudioFrame : public Frame { public: - AudioFrame(const AudioFrameDesc& desc); + AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocation = true); ~AudioFrame(); /** diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 3ddc0263..d72e693d 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -45,13 +45,16 @@ void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) _fps = atof(profile.find(constants::avProfileFrameRate)->second.c_str()); } -VideoFrame::VideoFrame(const VideoFrameDesc& desc) +VideoFrame::VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation) : Frame() , _desc(desc) { _frame->width = desc._width; _frame->height = desc._height; _frame->format = desc._pixelFormat; + + if(forceDataAllocation) + allocateData(); } VideoFrame::~VideoFrame() @@ -76,9 +79,6 @@ size_t VideoFrame::getSize() const void VideoFrame::allocateData() { - if(_dataAllocated) - return; - // Set Frame properties _frame->width = _desc._width; _frame->height = _desc._height; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index fa580307..b6ee39ab 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -43,7 +43,7 @@ struct AvExport VideoFrameDesc class AvExport VideoFrame : public Frame { public: - VideoFrame(const VideoFrameDesc& desc); + VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation = true); ~VideoFrame(); /** diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 100d9807..f5a1f66d 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -133,7 +133,7 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector // else decode all data in an intermediate buffer AudioFrame& audioBuffer = static_cast(frameBuffer); - AudioFrame allDataOfNextFrame(AudioFrameDesc(audioBuffer.getSampleRate(), srcNbChannels, audioBuffer.getSampleFormat())); + AudioFrame allDataOfNextFrame(AudioFrameDesc(audioBuffer.getSampleRate(), srcNbChannels, audioBuffer.getSampleFormat()), false); if(!decodeNextFrame(allDataOfNextFrame)) return false; diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index d1c91b77..93eb5fa6 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -28,7 +28,6 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) if(!_silent) { _silent = new AudioFrame(_frameDesc); - _silent->allocateData(); std::stringstream msg; msg << "Generate a silence with the following features:" << std::endl; diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 8f5d94b5..c357a9fc 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -57,11 +57,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu _generators.push_back(new VideoGenerator(inputFrameDesc)); // buffers to process - _decodedData.push_back(new VideoFrame(inputFrameDesc)); + _decodedData.push_back(new VideoFrame(inputFrameDesc, false)); _filteredData = new VideoFrame(inputFrameDesc); - _filteredData->allocateData(); _transformedData = new VideoFrame(inputFrameDesc); - _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -95,11 +93,9 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu _generators.push_back(new AudioGenerator(inputFrameDesc)); // buffers to process - _decodedData.push_back(new AudioFrame(inputFrameDesc)); + _decodedData.push_back(new AudioFrame(inputFrameDesc, false)); _filteredData = new AudioFrame(inputFrameDesc); - _filteredData->allocateData(); _transformedData = new AudioFrame(inputFrameDesc); - _transformedData->allocateData(); // transform _transform = new AudioTransform(); @@ -179,9 +175,7 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre // buffers to process _filteredData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); - _filteredData->allocateData(); _transformedData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); - _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -219,9 +213,7 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre inputFrameDesc._nbChannels = nbOutputChannels; _filteredData = new AudioFrame(inputFrameDesc); - _filteredData->allocateData(); _transformedData = new AudioFrame(outputAudio->getAudioCodec().getAudioFrameDesc()); - _transformedData->allocateData(); // transform _transform = new AudioTransform(); @@ -251,7 +243,7 @@ void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInput _currentDecoder = inputVideo; // buffers to get the decoded data - VideoFrame* inputFrame = new VideoFrame(inputStream.getVideoCodec().getVideoFrameDesc()); + VideoFrame* inputFrame = new VideoFrame(inputStream.getVideoCodec().getVideoFrameDesc(), false); _decodedData.push_back(inputFrame); // generator decoder @@ -271,7 +263,7 @@ void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInput AudioFrameDesc inputFrameDesc(inputStream.getAudioCodec().getAudioFrameDesc()); if(inputStreamDesc.demultiplexing()) inputFrameDesc._nbChannels = inputStreamDesc._channelIndexArray.size(); - _decodedData.push_back(new AudioFrame(inputFrameDesc)); + _decodedData.push_back(new AudioFrame(inputFrameDesc, false)); // generator decoder _generators.push_back(new AudioGenerator(inputFrameDesc)); @@ -320,13 +312,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: // buffers to process VideoFrameDesc outputFrameDesc = inputFrameDesc; outputFrameDesc.setParameters(profile); - VideoFrame* decodedData = new VideoFrame(inputFrameDesc); - decodedData->allocateData(); - _decodedData.push_back(decodedData); + _decodedData.push_back(new VideoFrame(inputFrameDesc)); _filteredData = new VideoFrame(inputFrameDesc); - _filteredData->allocateData(); _transformedData = new VideoFrame(outputFrameDesc); - _transformedData->allocateData(); // transform _transform = new VideoTransform(); @@ -357,13 +345,9 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: // buffers to process AudioFrameDesc outputFrameDesc = inputFrameDesc; outputFrameDesc.setParameters(profile); - AudioFrame* decodedData = new AudioFrame(inputFrameDesc); - decodedData->allocateData(); - _decodedData.push_back(decodedData); + _decodedData.push_back(new AudioFrame(inputFrameDesc)); _filteredData = new AudioFrame(inputFrameDesc); - _filteredData->allocateData(); _transformedData = new AudioFrame(outputFrameDesc); - _transformedData->allocateData(); // transform _transform = new AudioTransform(); From 7e20933378d7c0a551d206c8ae6d093c71dba571 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 27 Oct 2016 18:54:56 +0200 Subject: [PATCH 16/58] Video/Audio frames: remove desc method This info can be found from elsewhere, and the method is not so used. --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 1 - src/AvTranscoder/data/decoded/VideoFrame.hpp | 1 - src/AvTranscoder/reader/AudioReader.cpp | 5 +++-- src/AvTranscoder/reader/VideoReader.cpp | 5 +++-- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 6f30d4e7..fc11c80c 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -53,7 +53,6 @@ class AvExport AudioFrame : public Frame std::string getChannelLayoutDesc() const; ///< Get a description of a channel layout (example: '5.1'). AVSampleFormat getSampleFormat() const { return static_cast(_frame->format); } size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } - AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); } size_t getSize() const; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index b6ee39ab..58b2f57a 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -57,7 +57,6 @@ class AvExport VideoFrame : public Frame size_t getWidth() const { return _frame->width; } size_t getHeight() const { return _frame->height; } AVPixelFormat getPixelFormat() const { return static_cast(_frame->format); } - VideoFrameDesc desc() const { return VideoFrameDesc(getWidth(), getHeight(), getPixelFormat()); } size_t getSize() const; diff --git a/src/AvTranscoder/reader/AudioReader.cpp b/src/AvTranscoder/reader/AudioReader.cpp index ea154447..8873ac6c 100644 --- a/src/AvTranscoder/reader/AudioReader.cpp +++ b/src/AvTranscoder/reader/AudioReader.cpp @@ -45,7 +45,8 @@ void AudioReader::init() _currentDecoder = _decoder; // create src frame - _srcFrame = new AudioFrame(_inputFile->getStream(_streamIndex).getAudioCodec().getAudioFrameDesc()); + const AudioFrameDesc srcFrameDesc = _inputFile->getStream(_streamIndex).getAudioCodec().getAudioFrameDesc(); + _srcFrame = new AudioFrame(srcFrameDesc); AudioFrame* srcFrame = static_cast(_srcFrame); // create dst frame _outputSampleRate = srcFrame->getSampleRate(); @@ -53,7 +54,7 @@ void AudioReader::init() _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, _outputSampleFormat)); // generator - _generator = new AudioGenerator(srcFrame->desc()); + _generator = new AudioGenerator(srcFrameDesc); // create transform _transform = new AudioTransform(); diff --git a/src/AvTranscoder/reader/VideoReader.cpp b/src/AvTranscoder/reader/VideoReader.cpp index 98feff89..5871a8e3 100644 --- a/src/AvTranscoder/reader/VideoReader.cpp +++ b/src/AvTranscoder/reader/VideoReader.cpp @@ -44,7 +44,8 @@ void VideoReader::init() _currentDecoder = _decoder; // create src frame - _srcFrame = new VideoFrame(_inputFile->getStream(_streamIndex).getVideoCodec().getVideoFrameDesc()); + const VideoFrameDesc srcFrameDesc = _inputFile->getStream(_streamIndex).getVideoCodec().getVideoFrameDesc(); + _srcFrame = new VideoFrame(srcFrameDesc); VideoFrame* srcFrame = static_cast(_srcFrame); // create dst frame _outputWidth = srcFrame->getWidth(); @@ -52,7 +53,7 @@ void VideoReader::init() _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getOutputPixelFormat())); // generator - _generator = new VideoGenerator(srcFrame->desc()); + _generator = new VideoGenerator(srcFrameDesc); // create transform _transform = new VideoTransform(); diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index c357a9fc..b8e835a8 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -247,7 +247,7 @@ void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInput _decodedData.push_back(inputFrame); // generator decoder - _generators.push_back(new VideoGenerator(inputFrame->desc())); + _generators.push_back(new VideoGenerator(inputStream.getVideoCodec().getVideoFrameDesc())); break; } From 5c788e31788e91822cd68869497f13d217478f29 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 10:36:32 +0200 Subject: [PATCH 17/58] decoded data: raise a bad_alloc when faling to allocate the requested space --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 3 ++- src/AvTranscoder/data/decoded/Frame.cpp | 3 ++- src/AvTranscoder/data/decoded/VideoFrame.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index ac5cf46b..ab9f660b 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -110,7 +110,8 @@ void AudioFrame::allocateData() os << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; os << "nb samples = " << _frame->nb_samples << ", "; os << "sample format = " << getSampleFormatName(_desc._sampleFormat); - throw std::runtime_error(os.str()); + LOG_ERROR(os.str()) + throw std::bad_alloc(); } _dataAllocated = true; } diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/Frame.cpp index 867786d2..4e8b8fc6 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/Frame.cpp @@ -40,7 +40,8 @@ void Frame::allocateAVFrame() #endif if(_frame == NULL) { - throw std::runtime_error("Unable to allocate an empty Frame."); + LOG_ERROR("Unable to allocate an empty Frame.") + throw std::bad_alloc(); } } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index d72e693d..a18c1f3c 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -93,7 +93,8 @@ void VideoFrame::allocateData() os << "width = " << _frame->width << ", "; os << "height = " << _frame->height << ", "; os << "pixel format = " << getPixelFormatName(_desc._pixelFormat); - throw std::runtime_error(os.str()); + LOG_ERROR(os.str()) + throw std::bad_alloc(); } _dataAllocated = true; } From 8d4cce5ce741e4ba408f3de2396a15723db03311 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 10:36:45 +0200 Subject: [PATCH 18/58] coded data: raise a bad_alloc when faling to allocate the requested space --- src/AvTranscoder/data/coded/CodedData.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/coded/CodedData.cpp b/src/AvTranscoder/data/coded/CodedData.cpp index 44eeaa6a..8c556b69 100644 --- a/src/AvTranscoder/data/coded/CodedData.cpp +++ b/src/AvTranscoder/data/coded/CodedData.cpp @@ -14,7 +14,12 @@ CodedData::CodedData() CodedData::CodedData(const size_t dataSize) : _avStream(NULL) { - av_new_packet(&_packet, dataSize); + const int err = av_new_packet(&_packet, dataSize); + if(err != 0) + { + LOG_ERROR("Unable to allocate the payload of a packet and initialize its fields with default values: " << getDescriptionFromErrorCode(err)) + throw std::bad_alloc(); + } } CodedData::CodedData(const AVPacket& avPacket) From c662a7054d3281875ff6699de4d34a5cdcdaee16 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 10:36:55 +0200 Subject: [PATCH 19/58] ICodec: raise a bad_alloc when faling to allocate the requested space --- src/AvTranscoder/codec/ICodec.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/codec/ICodec.cpp b/src/AvTranscoder/codec/ICodec.cpp index d5f66e1e..3e64cd76 100644 --- a/src/AvTranscoder/codec/ICodec.cpp +++ b/src/AvTranscoder/codec/ICodec.cpp @@ -148,7 +148,8 @@ void ICodec::allocateContext() _avCodecContext = avcodec_alloc_context3(_avCodec); if(!_avCodecContext) { - throw std::runtime_error("Unable to allocate the codecContext and set its fields to default values"); + LOG_ERROR("Unable to allocate the codecContext and set its fields to default values.") + throw std::bad_alloc(); } _avCodecContext->codec = _avCodec; } From 6847185e009af42ef3865d6ebf035103f35601a9 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 10:45:00 +0200 Subject: [PATCH 20/58] decoded data: rename class Frame to IFrame * IFrame is an abstract class. * To keep the naming convention of the project, rename it to IFrame. --- src/AvTranscoder/data/data.i | 4 ++-- src/AvTranscoder/data/decoded/AudioFrame.cpp | 2 +- src/AvTranscoder/data/decoded/AudioFrame.hpp | 4 ++-- .../data/decoded/{Frame.cpp => IFrame.cpp} | 18 +++++++++--------- .../data/decoded/{Frame.hpp => IFrame.hpp} | 10 +++++----- src/AvTranscoder/data/decoded/VideoFrame.cpp | 2 +- src/AvTranscoder/data/decoded/VideoFrame.hpp | 4 ++-- src/AvTranscoder/decoder/AudioDecoder.cpp | 4 ++-- src/AvTranscoder/decoder/AudioDecoder.hpp | 4 ++-- src/AvTranscoder/decoder/AudioGenerator.cpp | 4 ++-- src/AvTranscoder/decoder/AudioGenerator.hpp | 8 ++++---- src/AvTranscoder/decoder/IDecoder.hpp | 8 ++++---- src/AvTranscoder/decoder/VideoDecoder.cpp | 4 ++-- src/AvTranscoder/decoder/VideoDecoder.hpp | 4 ++-- src/AvTranscoder/decoder/VideoGenerator.cpp | 4 ++-- src/AvTranscoder/decoder/VideoGenerator.hpp | 8 ++++---- src/AvTranscoder/encoder/AudioEncoder.cpp | 2 +- src/AvTranscoder/encoder/AudioEncoder.hpp | 2 +- src/AvTranscoder/encoder/IEncoder.hpp | 4 ++-- src/AvTranscoder/encoder/VideoEncoder.cpp | 2 +- src/AvTranscoder/encoder/VideoEncoder.hpp | 2 +- src/AvTranscoder/filter/FilterGraph.cpp | 10 +++++----- src/AvTranscoder/filter/FilterGraph.hpp | 10 +++++----- src/AvTranscoder/reader/IReader.cpp | 6 +++--- src/AvTranscoder/reader/IReader.hpp | 12 ++++++------ .../transcoder/StreamTranscoder.cpp | 6 +++--- .../transcoder/StreamTranscoder.hpp | 6 +++--- src/AvTranscoder/transform/AudioTransform.cpp | 2 +- src/AvTranscoder/transform/AudioTransform.hpp | 2 +- src/AvTranscoder/transform/ITransform.hpp | 4 ++-- src/AvTranscoder/transform/VideoTransform.cpp | 2 +- src/AvTranscoder/transform/VideoTransform.hpp | 2 +- 32 files changed, 83 insertions(+), 83 deletions(-) rename src/AvTranscoder/data/decoded/{Frame.cpp => IFrame.cpp} (80%) rename src/AvTranscoder/data/decoded/{Frame.hpp => IFrame.hpp} (94%) diff --git a/src/AvTranscoder/data/data.i b/src/AvTranscoder/data/data.i index 117c1f74..ceaf92cb 100644 --- a/src/AvTranscoder/data/data.i +++ b/src/AvTranscoder/data/data.i @@ -2,12 +2,12 @@ %{ #include -#include +#include #include #include %} %include -%include +%include %include %include diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index ab9f660b..d3bf4481 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -40,7 +40,7 @@ void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) } AudioFrame::AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocation) - : Frame() + : IFrame() , _desc(desc) { // Set Frame properties diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index fc11c80c..fa24a3fb 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -1,7 +1,7 @@ #ifndef _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ #define _AV_TRANSCODER_FRAME_AUDIO_FRAME_HPP_ -#include "Frame.hpp" +#include "IFrame.hpp" #include namespace avtranscoder @@ -33,7 +33,7 @@ struct AvExport AudioFrameDesc /** * @brief This class describes decoded audio data. */ -class AvExport AudioFrame : public Frame +class AvExport AudioFrame : public IFrame { public: AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocation = true); diff --git a/src/AvTranscoder/data/decoded/Frame.cpp b/src/AvTranscoder/data/decoded/IFrame.cpp similarity index 80% rename from src/AvTranscoder/data/decoded/Frame.cpp rename to src/AvTranscoder/data/decoded/IFrame.cpp index 4e8b8fc6..ba346736 100644 --- a/src/AvTranscoder/data/decoded/Frame.cpp +++ b/src/AvTranscoder/data/decoded/IFrame.cpp @@ -1,23 +1,23 @@ -#include "Frame.hpp" +#include "IFrame.hpp" #include namespace avtranscoder { -Frame::Frame() +IFrame::IFrame() : _frame(NULL) , _dataAllocated(false) { allocateAVFrame(); } -Frame::~Frame() +IFrame::~IFrame() { freeAVFrame(); } -void Frame::copyData(const Frame& frameToRef) +void IFrame::copyData(const IFrame& frameToRef) { const int ret = av_frame_copy(_frame, &frameToRef.getAVFrame()); if(ret < 0) @@ -26,12 +26,12 @@ void Frame::copyData(const Frame& frameToRef) } } -void Frame::copyProperties(const Frame& otherFrame) +void IFrame::copyProperties(const IFrame& otherFrame) { av_frame_copy_props(_frame, &otherFrame.getAVFrame()); } -void Frame::allocateAVFrame() +void IFrame::allocateAVFrame() { #if LIBAVCODEC_VERSION_MAJOR > 54 _frame = av_frame_alloc(); @@ -45,7 +45,7 @@ void Frame::allocateAVFrame() } } -void Frame::freeAVFrame() +void IFrame::freeAVFrame() { if(_frame != NULL) { @@ -62,14 +62,14 @@ void Frame::freeAVFrame() } } -bool Frame::isAudioFrame() const +bool IFrame::isAudioFrame() const { if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1) return true; return false; } -bool Frame::isVideoFrame() const +bool IFrame::isVideoFrame() const { if(_frame->width && _frame->height && _frame->format != -1) return true; diff --git a/src/AvTranscoder/data/decoded/Frame.hpp b/src/AvTranscoder/data/decoded/IFrame.hpp similarity index 94% rename from src/AvTranscoder/data/decoded/Frame.hpp rename to src/AvTranscoder/data/decoded/IFrame.hpp index ac2bb999..6edfc1cc 100644 --- a/src/AvTranscoder/data/decoded/Frame.hpp +++ b/src/AvTranscoder/data/decoded/IFrame.hpp @@ -16,7 +16,7 @@ namespace avtranscoder * @see VideoFrame * @see AudioFrame */ -class AvExport Frame +class AvExport IFrame { public: /** @@ -25,9 +25,9 @@ class AvExport Frame * Depending on the case, we could manipulate frame with data allocated elsewhere. * For example, an empty frame is given to a decoder, which is responsible to allocate and free the data buffers. */ - Frame(); + IFrame(); - virtual ~Frame(); + virtual ~IFrame(); /** * @brief Allocate the buffer of the frame. @@ -66,13 +66,13 @@ class AvExport Frame * @note It copies the frame data (i.e. the contents of the data / extended data arrays), not any other properties. * @see copyProperties */ - void copyData(const Frame& frameToRef); + void copyData(const IFrame& frameToRef); /** * @brief Copy all the fields that do not affect the data layout in the buffers. * @warning The info checked when copying data of an other frame (width/height, channels...) are not copied. */ - void copyProperties(const Frame& otherFrame); + void copyProperties(const IFrame& otherFrame); /** * @return If it corresponds to a valid audio frame. diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index a18c1f3c..b2d268d8 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -46,7 +46,7 @@ void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) } VideoFrame::VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation) - : Frame() + : IFrame() , _desc(desc) { _frame->width = desc._width; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index 58b2f57a..f3b867bf 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -1,7 +1,7 @@ #ifndef _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ #define _AV_TRANSCODER_FRAME_VIDEO_FRAME_HPP_ -#include "Frame.hpp" +#include "IFrame.hpp" #include extern "C" { @@ -40,7 +40,7 @@ struct AvExport VideoFrameDesc /** * @brief This class describes decoded video data. */ -class AvExport VideoFrame : public Frame +class AvExport VideoFrame : public IFrame { public: VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation = true); diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index f5a1f66d..8854f9b7 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -76,7 +76,7 @@ void AudioDecoder::setupDecoder(const ProfileLoader::Profile& profile) _isSetup = true; } -bool AudioDecoder::decodeNextFrame(Frame& frameBuffer) +bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer) { bool decodeNextFrame = false; const size_t channelLayout = frameBuffer.getAVFrame().channel_layout; @@ -121,7 +121,7 @@ bool AudioDecoder::decodeNextFrame(Frame& frameBuffer) return decodeNextFrame; } -bool AudioDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray) +bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray) { AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext(); const size_t srcNbChannels = avCodecContext.channels; diff --git a/src/AvTranscoder/decoder/AudioDecoder.hpp b/src/AvTranscoder/decoder/AudioDecoder.hpp index b5e5ac57..92063449 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.hpp +++ b/src/AvTranscoder/decoder/AudioDecoder.hpp @@ -16,8 +16,8 @@ class AvExport AudioDecoder : public IDecoder void setupDecoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool decodeNextFrame(Frame& frameBuffer); - bool decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray); + bool decodeNextFrame(IFrame& frameBuffer); + bool decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray); void flushDecoder(); diff --git a/src/AvTranscoder/decoder/AudioGenerator.cpp b/src/AvTranscoder/decoder/AudioGenerator.cpp index 93eb5fa6..63b1a4b1 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.cpp +++ b/src/AvTranscoder/decoder/AudioGenerator.cpp @@ -19,7 +19,7 @@ AudioGenerator::~AudioGenerator() delete _silent; } -bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) +bool AudioGenerator::decodeNextFrame(IFrame& frameBuffer) { // Generate silent if(!_inputFrame) @@ -53,7 +53,7 @@ bool AudioGenerator::decodeNextFrame(Frame& frameBuffer) return true; } -bool AudioGenerator::decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray) +bool AudioGenerator::decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray) { return decodeNextFrame(frameBuffer); } diff --git a/src/AvTranscoder/decoder/AudioGenerator.hpp b/src/AvTranscoder/decoder/AudioGenerator.hpp index de8eccd9..9c4a7348 100644 --- a/src/AvTranscoder/decoder/AudioGenerator.hpp +++ b/src/AvTranscoder/decoder/AudioGenerator.hpp @@ -19,18 +19,18 @@ class AvExport AudioGenerator : public IDecoder ~AudioGenerator(); - bool decodeNextFrame(Frame& frameBuffer); - bool decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray); + bool decodeNextFrame(IFrame& frameBuffer); + bool decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray); /** * @brief Force to return this frame when calling the decoding methods. * @param inputFrame: could have other properties than the given frame when decoding (will be converted). * @see decodeNextFrame */ - void setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; } + void setNextFrame(IFrame& inputFrame) { _inputFrame = &inputFrame; } private: - Frame* _inputFrame; ///< Has link (no ownership) + IFrame* _inputFrame; ///< Has link (no ownership) AudioFrame* _silent; ///< The generated silent (has ownership) const AudioFrameDesc _frameDesc; ///< The description of the given frame buffer when decoding. AudioTransform _audioTransform; ///< To transform the specified data when decoding. diff --git a/src/AvTranscoder/decoder/IDecoder.hpp b/src/AvTranscoder/decoder/IDecoder.hpp index ca28bf15..d9acf8d1 100644 --- a/src/AvTranscoder/decoder/IDecoder.hpp +++ b/src/AvTranscoder/decoder/IDecoder.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_ESSENCE_STREAM_IDECODER_HPP_ #include -#include +#include #include namespace avtranscoder @@ -28,7 +28,7 @@ class AvExport IDecoder * decoder. The caller may not write to it. * @return status of decoding */ - virtual bool decodeNextFrame(Frame& frameBuffer) = 0; + virtual bool decodeNextFrame(IFrame& frameBuffer) = 0; /** * @brief Decode substream of next frame @@ -36,14 +36,14 @@ class AvExport IDecoder * @param channelIndexArray: list of channels to extract * @return status of decoding */ - virtual bool decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray) = 0; + virtual bool decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray) = 0; /** * @brief Set the next frame of the input stream (which bypass the work of decoding) * @note Not yet implemented for VideoDecoder and AudioDecoder * @param inputFrame: the new next frame */ - virtual void setNextFrame(Frame& inputFrame) {} + virtual void setNextFrame(IFrame& inputFrame) {} /** * @brief Reset the internal decoder state / flush internal buffers. diff --git a/src/AvTranscoder/decoder/VideoDecoder.cpp b/src/AvTranscoder/decoder/VideoDecoder.cpp index 082d1dc2..02a39f8d 100644 --- a/src/AvTranscoder/decoder/VideoDecoder.cpp +++ b/src/AvTranscoder/decoder/VideoDecoder.cpp @@ -74,7 +74,7 @@ void VideoDecoder::setupDecoder(const ProfileLoader::Profile& profile) _isSetup = true; } -bool VideoDecoder::decodeNextFrame(Frame& frameBuffer) +bool VideoDecoder::decodeNextFrame(IFrame& frameBuffer) { bool decodeNextFrame = false; @@ -115,7 +115,7 @@ bool VideoDecoder::decodeNextFrame(Frame& frameBuffer) return decodeNextFrame; } -bool VideoDecoder::decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray) +bool VideoDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray) { return false; } diff --git a/src/AvTranscoder/decoder/VideoDecoder.hpp b/src/AvTranscoder/decoder/VideoDecoder.hpp index 3065f484..88303d00 100644 --- a/src/AvTranscoder/decoder/VideoDecoder.hpp +++ b/src/AvTranscoder/decoder/VideoDecoder.hpp @@ -16,8 +16,8 @@ class AvExport VideoDecoder : public IDecoder void setupDecoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool decodeNextFrame(Frame& frameBuffer); - bool decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray); + bool decodeNextFrame(IFrame& frameBuffer); + bool decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray); void flushDecoder(); diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index 22872ecd..98840f40 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -19,7 +19,7 @@ VideoGenerator::~VideoGenerator() delete _blackImage; } -bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) +bool VideoGenerator::decodeNextFrame(IFrame& frameBuffer) { // Generate black image if(!_inputFrame) @@ -54,7 +54,7 @@ bool VideoGenerator::decodeNextFrame(Frame& frameBuffer) return true; } -bool VideoGenerator::decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray) +bool VideoGenerator::decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray) { return false; } diff --git a/src/AvTranscoder/decoder/VideoGenerator.hpp b/src/AvTranscoder/decoder/VideoGenerator.hpp index 6f689e31..f8f03489 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.hpp +++ b/src/AvTranscoder/decoder/VideoGenerator.hpp @@ -18,18 +18,18 @@ class AvExport VideoGenerator : public IDecoder VideoGenerator(const VideoFrameDesc& frameDesc); ~VideoGenerator(); - bool decodeNextFrame(Frame& frameBuffer); - bool decodeNextFrame(Frame& frameBuffer, const std::vector channelIndexArray); + bool decodeNextFrame(IFrame& frameBuffer); + bool decodeNextFrame(IFrame& frameBuffer, const std::vector channelIndexArray); /** * @brief Force to return this frame when calling the decoding methods. * @param inputFrame: could have other properties than the given frame when decoding (will be converted). * @see decodeNextFrame */ - void setNextFrame(Frame& inputFrame) { _inputFrame = &inputFrame; } + void setNextFrame(IFrame& inputFrame) { _inputFrame = &inputFrame; } private: - Frame* _inputFrame; ///< A frame given from outside (has link, no ownership) + IFrame* _inputFrame; ///< A frame given from outside (has link, no ownership) 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. diff --git a/src/AvTranscoder/encoder/AudioEncoder.cpp b/src/AvTranscoder/encoder/AudioEncoder.cpp index 66fa14e1..06410299 100644 --- a/src/AvTranscoder/encoder/AudioEncoder.cpp +++ b/src/AvTranscoder/encoder/AudioEncoder.cpp @@ -91,7 +91,7 @@ void AudioEncoder::setupEncoder(const ProfileLoader::Profile& profile) } } -bool AudioEncoder::encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) +bool AudioEncoder::encodeFrame(const IFrame& sourceFrame, CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); diff --git a/src/AvTranscoder/encoder/AudioEncoder.hpp b/src/AvTranscoder/encoder/AudioEncoder.hpp index 35ea2bef..0c8e0a55 100644 --- a/src/AvTranscoder/encoder/AudioEncoder.hpp +++ b/src/AvTranscoder/encoder/AudioEncoder.hpp @@ -18,7 +18,7 @@ class AvExport AudioEncoder : public IEncoder const ProfileLoader::Profile& profile = ProfileLoader::Profile()); void setupEncoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame); + bool encodeFrame(const IFrame& sourceFrame, CodedData& codedFrame); bool encodeFrame(CodedData& codedFrame); ICodec& getCodec() { return _codec; } diff --git a/src/AvTranscoder/encoder/IEncoder.hpp b/src/AvTranscoder/encoder/IEncoder.hpp index 33ef1f38..e9727938 100644 --- a/src/AvTranscoder/encoder/IEncoder.hpp +++ b/src/AvTranscoder/encoder/IEncoder.hpp @@ -1,7 +1,7 @@ #ifndef _AV_TRANSCODER_ESSENCE_STREAM_IENCODER_HPP_ #define _AV_TRANSCODER_ESSENCE_STREAM_IENCODER_HPP_ -#include +#include #include #include #include @@ -28,7 +28,7 @@ class AvExport IEncoder * @return status of encoding * @throw runtime_error if the encoded process failed. */ - virtual bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) = 0; + virtual bool encodeFrame(const IFrame& sourceFrame, CodedData& codedFrame) = 0; /** * @brief Get the frames remaining into the encoder diff --git a/src/AvTranscoder/encoder/VideoEncoder.cpp b/src/AvTranscoder/encoder/VideoEncoder.cpp index fef4ab37..c6d43b66 100644 --- a/src/AvTranscoder/encoder/VideoEncoder.cpp +++ b/src/AvTranscoder/encoder/VideoEncoder.cpp @@ -104,7 +104,7 @@ void VideoEncoder::setupEncoder(const ProfileLoader::Profile& profile) } } -bool VideoEncoder::encodeFrame(const Frame& sourceFrame, CodedData& codedFrame) +bool VideoEncoder::encodeFrame(const IFrame& sourceFrame, CodedData& codedFrame) { AVCodecContext& avCodecContext = _codec.getAVCodecContext(); diff --git a/src/AvTranscoder/encoder/VideoEncoder.hpp b/src/AvTranscoder/encoder/VideoEncoder.hpp index 0a45b7e8..d12035e6 100644 --- a/src/AvTranscoder/encoder/VideoEncoder.hpp +++ b/src/AvTranscoder/encoder/VideoEncoder.hpp @@ -18,7 +18,7 @@ class AvExport VideoEncoder : public IEncoder const ProfileLoader::Profile& profile = ProfileLoader::Profile()); void setupEncoder(const ProfileLoader::Profile& profile = ProfileLoader::Profile()); - bool encodeFrame(const Frame& sourceFrame, CodedData& codedFrame); + bool encodeFrame(const IFrame& sourceFrame, CodedData& codedFrame); bool encodeFrame(CodedData& codedFrame); ICodec& getCodec() { return _codec; } diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index b1674460..1b2e4954 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -35,7 +35,7 @@ FilterGraph::~FilterGraph() avfilter_graph_free(&_graph); } -void FilterGraph::process(const std::vector& inputs, Frame& output) +void FilterGraph::process(const std::vector& inputs, IFrame& output) { // init filter graph if(!_isInit) @@ -74,7 +74,7 @@ Filter& FilterGraph::addFilter(const std::string& filterName, const std::string& return *_filters.back(); } -void FilterGraph::init(const std::vector& inputs, Frame& output) +void FilterGraph::init(const std::vector& inputs, IFrame& output) { // push filters to the graph addInBuffer(inputs); @@ -134,9 +134,9 @@ void FilterGraph::pushFilter(Filter& filter) } } -void FilterGraph::addInBuffer(const std::vector& inputs) +void FilterGraph::addInBuffer(const std::vector& inputs) { - for(std::vector::const_reverse_iterator it = inputs.rbegin(); it != inputs.rend(); ++it) + for(std::vector::const_reverse_iterator it = inputs.rbegin(); it != inputs.rend(); ++it) { std::string filterName; std::stringstream filterOptions; @@ -174,7 +174,7 @@ void FilterGraph::addInBuffer(const std::vector& inputs) } } -void FilterGraph::addOutBuffer(const Frame& output) +void FilterGraph::addOutBuffer(const IFrame& output) { std::string filterName; diff --git a/src/AvTranscoder/filter/FilterGraph.hpp b/src/AvTranscoder/filter/FilterGraph.hpp index ee993f0a..287f6e9f 100644 --- a/src/AvTranscoder/filter/FilterGraph.hpp +++ b/src/AvTranscoder/filter/FilterGraph.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -55,7 +55,7 @@ class AvExport FilterGraph * @warning the output frame must be cleared once it has been used * @see the av_buffersink_get_frame function documentation */ - void process(const std::vector& inputs, Frame& output); + void process(const std::vector& inputs, IFrame& output); /** * @return If at least one filter has been added to the filter graph @@ -69,7 +69,7 @@ class AvExport FilterGraph * @see pushInBuffer * @see pushOutBuffer */ - void init(const std::vector& inputs, Frame& output); + void init(const std::vector& inputs, IFrame& output); /** * @brief Push the given Filter to the graph. @@ -78,8 +78,8 @@ class AvExport FilterGraph ///@{ /// @brief Add the input and output buffers at the beginning and the end of the list of filters. - void addInBuffer(const std::vector& inputs); - void addOutBuffer(const Frame& output); + void addInBuffer(const std::vector& inputs); + void addOutBuffer(const IFrame& output); //@} private: diff --git a/src/AvTranscoder/reader/IReader.cpp b/src/AvTranscoder/reader/IReader.cpp index 3e40fd16..a0a5c303 100644 --- a/src/AvTranscoder/reader/IReader.cpp +++ b/src/AvTranscoder/reader/IReader.cpp @@ -46,17 +46,17 @@ IReader::~IReader() delete _inputFile; } -Frame* IReader::readNextFrame() +IFrame* IReader::readNextFrame() { return readFrameAt(_currentFrame + 1); } -Frame* IReader::readPrevFrame() +IFrame* IReader::readPrevFrame() { return readFrameAt(_currentFrame - 1); } -Frame* IReader::readFrameAt(const size_t frame) +IFrame* IReader::readFrameAt(const size_t frame) { assert(_currentDecoder != NULL); assert(_transform != NULL); diff --git a/src/AvTranscoder/reader/IReader.hpp b/src/AvTranscoder/reader/IReader.hpp index e73e3068..b8a8a9df 100644 --- a/src/AvTranscoder/reader/IReader.hpp +++ b/src/AvTranscoder/reader/IReader.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include namespace avtranscoder @@ -37,20 +37,20 @@ class AvExport IReader * @return Get next frame after decoding * @see readFrameAt */ - Frame* readNextFrame(); + IFrame* readNextFrame(); /** * @return Get previous frame after decoding * @see readFrameAt */ - Frame* readPrevFrame(); + IFrame* readPrevFrame(); /** * @return Get indicated frame after decoding * @warn Returns NULL if there is no more frame to read. * @see continueWithGenerator */ - Frame* readFrameAt(const size_t frame); + IFrame* readFrameAt(const size_t frame); /** * @brief Get the properties of the source stream read. @@ -70,8 +70,8 @@ class AvExport IReader IDecoder* _generator; IDecoder* _currentDecoder; ///< Link to _inputDecoder or _generator - Frame* _srcFrame; - Frame* _dstFrame; + IFrame* _srcFrame; + IFrame* _dstFrame; ITransform* _transform; diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index b8e835a8..6627cb2b 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -368,7 +368,7 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: StreamTranscoder::~StreamTranscoder() { - for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) + for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) { delete(*it); } @@ -546,7 +546,7 @@ bool StreamTranscoder::processTranscode() CodedData data; if(decodingStatus) { - Frame* dataToTransform = NULL; + IFrame* dataToTransform = NULL; if(_filterGraph->hasFilters()) { LOG_DEBUG("Filtering") @@ -573,7 +573,7 @@ bool StreamTranscoder::processTranscode() { switchToGeneratorDecoder(); // force reallocation of the buffers since de decoders have cleared them - for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) + for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) (*it)->allocateData(); return processTranscode(); } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 4dceff17..c916b8cc 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -134,9 +134,9 @@ class AvExport StreamTranscoder std::vector _inputStreams; ///< List of input stream to read next packet (has link, no ownership) IOutputStream* _outputStream; ///< Output stream to wrap next packet (has link, no ownership) - std::vector _decodedData; ///< List of buffers of decoded data (has ownership). - Frame* _filteredData; ///< Buffer of filtered data (has ownership). - Frame* _transformedData; ///< Buffer of transformed data (has ownership). + std::vector _decodedData; ///< List of buffers of decoded data (has ownership). + IFrame* _filteredData; ///< Buffer of filtered data (has ownership). + IFrame* _transformedData; ///< Buffer of transformed data (has ownership). std::vector _inputDecoders; ///< Decoders of packets read from _inputStream (has ownership) std::vector _generators; ///< Generators of audio or video packets (has ownership) diff --git a/src/AvTranscoder/transform/AudioTransform.cpp b/src/AvTranscoder/transform/AudioTransform.cpp index 77401f58..22dde253 100644 --- a/src/AvTranscoder/transform/AudioTransform.cpp +++ b/src/AvTranscoder/transform/AudioTransform.cpp @@ -88,7 +88,7 @@ bool AudioTransform::init(const AudioFrame& src, const AudioFrame& dst) return true; } -void AudioTransform::convert(const Frame& srcFrame, Frame& dstFrame) +void AudioTransform::convert(const IFrame& srcFrame, IFrame& dstFrame) { const AudioFrame& src = static_cast(srcFrame); const AudioFrame& dst = static_cast(dstFrame); diff --git a/src/AvTranscoder/transform/AudioTransform.hpp b/src/AvTranscoder/transform/AudioTransform.hpp index 2d1ae38f..834be433 100644 --- a/src/AvTranscoder/transform/AudioTransform.hpp +++ b/src/AvTranscoder/transform/AudioTransform.hpp @@ -26,7 +26,7 @@ class AvExport AudioTransform : public ITransform AudioTransform(); ~AudioTransform(); - void convert(const Frame& srcFrame, Frame& dstFrame); + void convert(const IFrame& srcFrame, IFrame& dstFrame); private: bool init(const AudioFrame& src, const AudioFrame& dst); diff --git a/src/AvTranscoder/transform/ITransform.hpp b/src/AvTranscoder/transform/ITransform.hpp index 8b044d1b..e2a637c5 100644 --- a/src/AvTranscoder/transform/ITransform.hpp +++ b/src/AvTranscoder/transform/ITransform.hpp @@ -2,7 +2,7 @@ #define _AV_TRANSCODER_ESSENCE_TRANSFORM_ESSENCE_TRANSFORM_HPP_ #include -#include +#include namespace avtranscoder { @@ -14,7 +14,7 @@ class AvExport ITransform virtual ~ITransform() {} - virtual void convert(const Frame& src, Frame& dst) = 0; + virtual void convert(const IFrame& src, IFrame& dst) = 0; }; } diff --git a/src/AvTranscoder/transform/VideoTransform.cpp b/src/AvTranscoder/transform/VideoTransform.cpp index 38ec9c0f..bceb3d6b 100644 --- a/src/AvTranscoder/transform/VideoTransform.cpp +++ b/src/AvTranscoder/transform/VideoTransform.cpp @@ -60,7 +60,7 @@ bool VideoTransform::init(const VideoFrame& src, const VideoFrame& dst) return true; } -void VideoTransform::convert(const Frame& srcFrame, Frame& dstFrame) +void VideoTransform::convert(const IFrame& srcFrame, IFrame& dstFrame) { const VideoFrame& src = static_cast(srcFrame); VideoFrame& dst = static_cast(dstFrame); diff --git a/src/AvTranscoder/transform/VideoTransform.hpp b/src/AvTranscoder/transform/VideoTransform.hpp index e1bd090e..1b5b63f6 100644 --- a/src/AvTranscoder/transform/VideoTransform.hpp +++ b/src/AvTranscoder/transform/VideoTransform.hpp @@ -20,7 +20,7 @@ class AvExport VideoTransform : public ITransform VideoTransform(); ~VideoTransform(); - void convert(const Frame& srcFrame, Frame& dstFrame); + void convert(const IFrame& srcFrame, IFrame& dstFrame); private: bool init(const VideoFrame& src, const VideoFrame& dst); From 2a61683f5749235956e766ac4eab316669a0b37f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 11:03:28 +0200 Subject: [PATCH 21/58] frames: define assign method in the IFrame based class * Less code to maintain. * Rename assign methods to assignBuffer and assignValue (to avoid implicite C++ cast and so calling the wrong method). --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 14 +------------- src/AvTranscoder/data/decoded/AudioFrame.hpp | 12 +----------- src/AvTranscoder/data/decoded/IFrame.cpp | 11 +++++++++++ src/AvTranscoder/data/decoded/IFrame.hpp | 12 ++++++++++++ src/AvTranscoder/data/decoded/VideoFrame.cpp | 14 +------------- src/AvTranscoder/data/decoded/VideoFrame.hpp | 12 +----------- src/AvTranscoder/decoder/VideoGenerator.cpp | 2 +- 7 files changed, 28 insertions(+), 49 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index d3bf4481..21771eef 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -122,19 +122,7 @@ void AudioFrame::freeData() _dataAllocated = false; } -void AudioFrame::assign(const unsigned char value) -{ - // Create the audio buffer - // The buffer will be freed in destructor of based class - const int audioSize = getSize(); - unsigned char* audioBuffer = new unsigned char[audioSize]; - memset(audioBuffer, value, audioSize); - - // Fill the picture - assign(audioBuffer); -} - -void AudioFrame::assign(const unsigned char* ptrValue) +void AudioFrame::assignBuffer(const unsigned char* ptrValue) { const int align = 0; const int ret = av_samples_fill_arrays(_frame->data, _frame->linesize, ptrValue, getNbChannels(), diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index fa24a3fb..5c049650 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -58,17 +58,7 @@ class AvExport AudioFrame : public IFrame void setNbSamplesPerChannel(const size_t nbSamples) { _frame->nb_samples = nbSamples; } - /** - * @brief Assign the given value to all the data of the audio frame. - */ - void assign(const unsigned char value); - - /** - * @brief Assign the given ptr of data to the data of the audio frame. - * @warning the given ptr should have the size of the audio frame.. - * @see getSize - */ - void assign(const unsigned char* ptrValue); + void assignBuffer(const unsigned char* ptrValue); private: /** diff --git a/src/AvTranscoder/data/decoded/IFrame.cpp b/src/AvTranscoder/data/decoded/IFrame.cpp index ba346736..2f10502a 100644 --- a/src/AvTranscoder/data/decoded/IFrame.cpp +++ b/src/AvTranscoder/data/decoded/IFrame.cpp @@ -62,6 +62,17 @@ void IFrame::freeAVFrame() } } +void IFrame::assignValue(const unsigned char value) +{ + // Create the buffer + const int bufferSize = getSize(); + unsigned char* dataBuffer = new unsigned char[bufferSize]; + memset(dataBuffer, value, bufferSize); + + // Fill the frame + assignBuffer(dataBuffer); +} + bool IFrame::isAudioFrame() const { if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1) diff --git a/src/AvTranscoder/data/decoded/IFrame.hpp b/src/AvTranscoder/data/decoded/IFrame.hpp index 6edfc1cc..8644faa1 100644 --- a/src/AvTranscoder/data/decoded/IFrame.hpp +++ b/src/AvTranscoder/data/decoded/IFrame.hpp @@ -45,6 +45,18 @@ class AvExport IFrame */ virtual size_t getSize() const = 0; + /** + * @brief Assign the given ptr of data to the data of the frame. + * @warning the given ptr should have the size of the frame.. + * @see getSize + */ + virtual void assignBuffer(const unsigned char* ptrValue) = 0; + + /** + * @brief Assign the given value to all the data of the frame. + */ + void assignValue(const unsigned char value); + /** * @brief Get all the data of the frame. */ diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index b2d268d8..7472fe1c 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -105,19 +105,7 @@ void VideoFrame::freeData() _dataAllocated = false; } -void VideoFrame::assign(const unsigned char value) -{ - // Create the image buffer - // The buffer will be freed in destructor of based class - const int imageSize = getSize(); - unsigned char* imageBuffer = new unsigned char[imageSize]; - memset(imageBuffer, value, imageSize); - - // Fill the picture - assign(imageBuffer); -} - -void VideoFrame::assign(const unsigned char* ptrValue) +void VideoFrame::assignBuffer(const unsigned char* ptrValue) { const int ret = avpicture_fill(reinterpret_cast(_frame), ptrValue, getPixelFormat(), getWidth(), getHeight()); diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index f3b867bf..72307806 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -60,17 +60,7 @@ class AvExport VideoFrame : public IFrame size_t getSize() const; - /** - * @brief Assign the given value to all the data of the picture. - */ - void assign(const unsigned char value); - - /** - * @brief Assign the given ptr of data to the data of the picture. - * @warning the given ptr should have the size of the picture. - * @see getSize - */ - void assign(const unsigned char* ptrValue); + void assignBuffer(const unsigned char* ptrValue); private: /** diff --git a/src/AvTranscoder/decoder/VideoGenerator.cpp b/src/AvTranscoder/decoder/VideoGenerator.cpp index 98840f40..ad89c9ac 100644 --- a/src/AvTranscoder/decoder/VideoGenerator.cpp +++ b/src/AvTranscoder/decoder/VideoGenerator.cpp @@ -31,7 +31,7 @@ bool VideoGenerator::decodeNextFrame(IFrame& frameBuffer) VideoFrameDesc blackDesc(_frameDesc._width, _frameDesc._height, "rgb24"); _blackImage = new VideoFrame(blackDesc); const unsigned char fillChar = 0; - _blackImage->assign(fillChar); + _blackImage->assignValue(fillChar); std::stringstream msg; msg << "Generate a black image with the following features:" << std::endl; From a92879d620b27341363bd45ce8a29d12ceceb983 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 11:18:52 +0200 Subject: [PATCH 22/58] IFrame: fix memory leak when calling assignValue * The data buffer of the frame should be freed before allocate a new buffer. * The new buffer should be allocated using malloc instead of free, since ffmpeg/libav uses free to deallocate the data. The valgrind error was: "Mismatched free() / delete / delete []". --- src/AvTranscoder/data/decoded/IFrame.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/IFrame.cpp b/src/AvTranscoder/data/decoded/IFrame.cpp index 2f10502a..5fe4be19 100644 --- a/src/AvTranscoder/data/decoded/IFrame.cpp +++ b/src/AvTranscoder/data/decoded/IFrame.cpp @@ -64,13 +64,19 @@ void IFrame::freeAVFrame() void IFrame::assignValue(const unsigned char value) { + // Free the existing data + freeData(); + // Create the buffer const int bufferSize = getSize(); - unsigned char* dataBuffer = new unsigned char[bufferSize]; + unsigned char* dataBuffer = static_cast(malloc(bufferSize * sizeof(unsigned char))); memset(dataBuffer, value, bufferSize); // Fill the frame assignBuffer(dataBuffer); + + // Prepare to free the data + _dataAllocated = true; } bool IFrame::isAudioFrame() const From 8e2818f62280e62cb7cd15dd51194b34bb8635e6 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 11:37:40 +0200 Subject: [PATCH 23/58] StreamTranscoder: fix seg fault when using a decoder to lengthen a rewrap stream --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 6627cb2b..3c06a43e 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -57,7 +57,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu _generators.push_back(new VideoGenerator(inputFrameDesc)); // buffers to process - _decodedData.push_back(new VideoFrame(inputFrameDesc, false)); + _decodedData.push_back(new VideoFrame(inputFrameDesc)); _filteredData = new VideoFrame(inputFrameDesc); _transformedData = new VideoFrame(inputFrameDesc); @@ -93,7 +93,7 @@ StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outpu _generators.push_back(new AudioGenerator(inputFrameDesc)); // buffers to process - _decodedData.push_back(new AudioFrame(inputFrameDesc, false)); + _decodedData.push_back(new AudioFrame(inputFrameDesc)); _filteredData = new AudioFrame(inputFrameDesc); _transformedData = new AudioFrame(inputFrameDesc); From 7ff35fcdd03c2f2e1f619edf1e40529e16ecff41 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 11:39:21 +0200 Subject: [PATCH 24/58] AudioDecoder: fix memory leak in demultiplexing cases In a demultiplexing case, the buffer should be allocated by us, but only once (not in the decodeNextFrame method)! --- src/AvTranscoder/decoder/AudioDecoder.cpp | 2 -- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 8854f9b7..8a88f669 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -159,8 +159,6 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector Date: Fri, 28 Oct 2016 11:54:26 +0200 Subject: [PATCH 25/58] AudioDecoder: refactor how to decodeNextFrame in case of demultiplexing The number of channels and the channel layout is already correctly set by the StreamTranscoder. --- src/AvTranscoder/decoder/AudioDecoder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 8a88f669..9b3e5256 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -162,8 +162,6 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector Date: Fri, 28 Oct 2016 11:59:05 +0200 Subject: [PATCH 26/58] AudioFrame: add doc to warn about setNbSamplesPerChannel method --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 5c049650..37ba8c1e 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -56,6 +56,10 @@ class AvExport AudioFrame : public IFrame size_t getSize() const; + /** + * @brief This methods dynamically updates the size that the data buffer would occupy if allocated. + * @warning If the data buffer is already allocated, this could lead to memory leaks or seg fault. + */ void setNbSamplesPerChannel(const size_t nbSamples) { _frame->nb_samples = nbSamples; } void assignBuffer(const unsigned char* ptrValue); From 87d11c45fe57c235054758ec18c38472616093b3 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 16:59:24 +0200 Subject: [PATCH 27/58] StreamTranscoder: fix video frame to get filtered data when transcoding --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 7697eb1a..a79d4576 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -174,7 +174,7 @@ StreamTranscoder::StreamTranscoder(const std::vector& inputStre _outputStream = &outputFile.addVideoStream(outputVideo->getVideoCodec()); // buffers to process - _filteredData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); + _filteredData = new VideoFrame(inputStream.getVideoCodec().getVideoFrameDesc()); _transformedData = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc()); // transform From d86ccf3eac629e641c06ddea8e7470c889b22b94 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 17:01:14 +0200 Subject: [PATCH 28/58] StreamTranscoder: fix process with a positive offset In this case, the buffer of decoded data should be manually allocated, and freed when it is the decoder's turn. --- src/AvTranscoder/data/decoded/IFrame.hpp | 5 +++++ .../transcoder/StreamTranscoder.cpp | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/IFrame.hpp b/src/AvTranscoder/data/decoded/IFrame.hpp index 8644faa1..4b11330e 100644 --- a/src/AvTranscoder/data/decoded/IFrame.hpp +++ b/src/AvTranscoder/data/decoded/IFrame.hpp @@ -98,6 +98,11 @@ class AvExport IFrame */ bool isVideoFrame() const; + /** + * @return If the data buffer is allocated and hold by the frame. + */ + bool isDataAllocated() const { return _dataAllocated; } + #ifndef SWIG AVFrame& getAVFrame() { return *_frame; } const AVFrame& getAVFrame() const { return *_frame; } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index a79d4576..2c3a2ebb 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -452,6 +452,13 @@ bool StreamTranscoder::processFrame() { LOG_INFO("End of positive offset") + // free our frame data since some new buffers will be allocated by the decoders in the next step + for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) + { + if((*it)->isDataAllocated()) + (*it)->freeData(); + } + // switch the decoder if(! _inputDecoders.empty()) switchToInputDecoder(); else @@ -576,7 +583,7 @@ bool StreamTranscoder::processTranscode() if(_needToSwitchToGenerator) { switchToGeneratorDecoder(); - // force reallocation of the buffers since de decoders have cleared them + LOG_INFO("Force reallocation of the decoded data buffers since the decoders could have cleared them.") for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) (*it)->allocateData(); return processTranscode(); @@ -666,7 +673,15 @@ void StreamTranscoder::setOffset(const float offset) { _offset = offset; if(_offset > 0) + { needToSwitchToGenerator(); + // allocate the frame since the process will start with some generated data + for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) + { + if(! (*it)->isDataAllocated()) + (*it)->allocateData(); + } + } } StreamTranscoder::EProcessCase StreamTranscoder::getProcessCase() const From aa9bba3fdb03a5221e13708a85cb7df699eba2b6 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 17:14:24 +0200 Subject: [PATCH 29/58] VideoProperties: fix seg fault when analysing the first GOP The AVFrame should be allocated. --- src/AvTranscoder/properties/VideoProperties.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/properties/VideoProperties.cpp b/src/AvTranscoder/properties/VideoProperties.cpp index 69dd02da..91f5c454 100644 --- a/src/AvTranscoder/properties/VideoProperties.cpp +++ b/src/AvTranscoder/properties/VideoProperties.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { #include @@ -341,11 +342,14 @@ size_t VideoProperties::getBitRate() const // discard no frame type when decode _codecContext->skip_frame = AVDISCARD_NONE; - AVFrame avFrame; AVPacket pkt; av_init_packet(&pkt); + avcodec_open2(_codecContext, _codec, NULL); + VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getAVPixelFormat()), false); + AVFrame& avFrame = frame.getAVFrame(); + int gotFrame = 0; size_t nbDecodedFrames = 0; int gopFramesSize = 0; @@ -565,7 +569,9 @@ void VideoProperties::analyseGopStructure(IProgress& progress) // Initialize the AVCodecContext to use the given AVCodec avcodec_open2(_codecContext, _codec, NULL); - AVFrame avFrame; + VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getAVPixelFormat()), false); + AVFrame& avFrame = frame.getAVFrame(); + size_t count = 0; int gotFrame = 0; int positionOfFirstKeyFrame = -1; From 9203a714f94ec66632704032ea267013aa874d83 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 17:15:38 +0200 Subject: [PATCH 30/58] filters: refactor how to allocate/free the objects --- src/AvTranscoder/filter/Filter.hpp | 4 ++-- src/AvTranscoder/filter/FilterGraph.cpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/AvTranscoder/filter/Filter.hpp b/src/AvTranscoder/filter/Filter.hpp index 680a923a..5b6658e0 100644 --- a/src/AvTranscoder/filter/Filter.hpp +++ b/src/AvTranscoder/filter/Filter.hpp @@ -30,8 +30,8 @@ class AvExport Filter #endif private: - AVFilter* _filter; - AVFilterContext* _context; + AVFilter* _filter; ///< Has ownership + AVFilterContext* _context; ///< Has link (no ownership) std::string _options; std::string _instanceName; }; diff --git a/src/AvTranscoder/filter/FilterGraph.cpp b/src/AvTranscoder/filter/FilterGraph.cpp index 1b2e4954..11b7346e 100644 --- a/src/AvTranscoder/filter/FilterGraph.cpp +++ b/src/AvTranscoder/filter/FilterGraph.cpp @@ -30,7 +30,7 @@ FilterGraph::~FilterGraph() { for(std::vector::iterator it = _filters.begin(); it < _filters.end(); ++it) { - it = _filters.erase(it); + delete(*it); } avfilter_graph_free(&_graph); } @@ -168,9 +168,8 @@ void FilterGraph::addInBuffer(const std::vector& inputs) 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); + _filters.insert(_filters.begin(), new Filter(filterName, filterOptions.str(), "in")); } } From a5ad30ed75cce1d6d0ac169432179b76f6f8b1b4 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Fri, 28 Oct 2016 17:17:02 +0200 Subject: [PATCH 31/58] Video/Audio frames: fix seg fault when process a graph of filters We should unref the frames before delete their data. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 2 ++ src/AvTranscoder/data/decoded/VideoFrame.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index 21771eef..b0a5d887 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -63,6 +63,8 @@ std::string AudioFrame::getChannelLayoutDesc() const AudioFrame::~AudioFrame() { + if(_frame->buf[0]) + av_frame_unref(_frame); if(_dataAllocated) freeData(); } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 7472fe1c..868cb796 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -59,6 +59,8 @@ VideoFrame::VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocatio VideoFrame::~VideoFrame() { + if(_frame->buf[0]) + av_frame_unref(_frame); if(_dataAllocated) freeData(); } From 9c02a02be09a60ab9101e0fb91c975352df544a4 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 10:40:55 +0100 Subject: [PATCH 32/58] readers: fix allocation of buffer of decoded data --- src/AvTranscoder/reader/AudioReader.cpp | 5 ++++- src/AvTranscoder/reader/IReader.cpp | 3 +++ src/AvTranscoder/reader/VideoReader.cpp | 2 +- test/pyTest/testReader.py | 24 +++++++++++------------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/AvTranscoder/reader/AudioReader.cpp b/src/AvTranscoder/reader/AudioReader.cpp index 8873ac6c..cabb49bd 100644 --- a/src/AvTranscoder/reader/AudioReader.cpp +++ b/src/AvTranscoder/reader/AudioReader.cpp @@ -46,7 +46,10 @@ void AudioReader::init() // create src frame const AudioFrameDesc srcFrameDesc = _inputFile->getStream(_streamIndex).getAudioCodec().getAudioFrameDesc(); - _srcFrame = new AudioFrame(srcFrameDesc); + bool allocateDecodedData = false; + if(_channelIndex != -1) + allocateDecodedData = true; + _srcFrame = new AudioFrame(srcFrameDesc, allocateDecodedData); AudioFrame* srcFrame = static_cast(_srcFrame); // create dst frame _outputSampleRate = srcFrame->getSampleRate(); diff --git a/src/AvTranscoder/reader/IReader.cpp b/src/AvTranscoder/reader/IReader.cpp index a0a5c303..b6f2bf78 100644 --- a/src/AvTranscoder/reader/IReader.cpp +++ b/src/AvTranscoder/reader/IReader.cpp @@ -86,6 +86,9 @@ IFrame* IReader::readFrameAt(const size_t frame) // generate data (ie silence or black) if(_continueWithGenerator) { + // allocate the frame since the process will continue with some generated data + if(! _srcFrame->isDataAllocated()) + _srcFrame->allocateData(); _currentDecoder = _generator; return readFrameAt(frame); } diff --git a/src/AvTranscoder/reader/VideoReader.cpp b/src/AvTranscoder/reader/VideoReader.cpp index 5871a8e3..e5c0f931 100644 --- a/src/AvTranscoder/reader/VideoReader.cpp +++ b/src/AvTranscoder/reader/VideoReader.cpp @@ -45,7 +45,7 @@ void VideoReader::init() // create src frame const VideoFrameDesc srcFrameDesc = _inputFile->getStream(_streamIndex).getVideoCodec().getVideoFrameDesc(); - _srcFrame = new VideoFrame(srcFrameDesc); + _srcFrame = new VideoFrame(srcFrameDesc, false); VideoFrame* srcFrame = static_cast(_srcFrame); // create dst frame _outputWidth = srcFrame->getWidth(); diff --git a/test/pyTest/testReader.py b/test/pyTest/testReader.py index 7c347c46..1fcf4965 100644 --- a/test/pyTest/testReader.py +++ b/test/pyTest/testReader.py @@ -23,7 +23,7 @@ def testVideoReaderCreateNewInputFile(): # read all frames and check their size for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): - frame = av.VideoFrame(reader.readNextFrame()) + frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) @@ -43,7 +43,7 @@ def testVideoReaderReferenceInputFile(): # read all frames and check their size for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): - frame = av.VideoFrame(reader.readNextFrame()) + frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) @@ -65,13 +65,13 @@ def testAudioReaderChannelsExtraction(): readerOfAllChannels = av.AudioReader(inputFile, streamIndex) nbChannels = readerOfAllChannels.getOutputNbChannels() # read first frame - frame = av.AudioFrame(readerOfAllChannels.readNextFrame()) + frame = readerOfAllChannels.readNextFrame() sizeOfFrameWithAllChannels = frame.getSize() # create reader to read one channel of the audio stream readerOfOneChannel = av.AudioReader(inputFile, streamIndex, channelIndex) # read first frame - frame = av.AudioFrame(readerOfOneChannel.readNextFrame()) + frame = readerOfOneChannel.readNextFrame() sizeOfFrameWithOneChannels = frame.getSize() assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels ) @@ -87,7 +87,7 @@ def testVideoReaderWithGenerator(): # read all frames and check their size for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): - frame = av.VideoFrame(reader.readNextFrame()) + frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) @@ -97,7 +97,7 @@ def testVideoReaderWithGenerator(): # generate 10 frames of black reader.continueWithGenerator() for i in xrange(0, 9): - frame = av.VideoFrame(reader.readNextFrame()) + frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) @@ -116,10 +116,7 @@ def testAudioReaderWithGenerator(): frame = reader.readNextFrame() if not frame: break - frame = av.AudioFrame(frame) - nbSamplesPerChannel = frame.getNbSamplesPerChannel() - bytesPerSample = 2 - assert_equals( frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) + assert_greater(frame.getSize(), 0) # check if there is no next frame assert_equals( reader.readNextFrame(), None ) @@ -127,7 +124,8 @@ def testAudioReaderWithGenerator(): # generate 10 frames of silence reader.continueWithGenerator() for i in xrange(0, 9): - frame = av.AudioFrame(reader.readNextFrame()) - nbSamplesPerChannel = frame.getNbSamplesPerChannel() + frame = reader.readNextFrame() + # assuming we generate data of 1920 samples of 2 bytes + nbSamplesPerChannel = 1920 bytesPerSample = 2 - assert_equals( frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) + assert_equals(frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) From 64d74cf119f1186fb28b800da4ec3d09569cec56 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 10:41:20 +0100 Subject: [PATCH 33/58] AudioFrame: add getBytesPerSample method --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 9 +++++++-- src/AvTranscoder/data/decoded/AudioFrame.hpp | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index b0a5d887..2d0404c4 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -69,6 +69,11 @@ AudioFrame::~AudioFrame() freeData(); } +size_t AudioFrame::getBytesPerSample() const +{ + return av_get_bytes_per_sample(getSampleFormat()); +} + size_t AudioFrame::getSize() const { if(getSampleFormat() == AV_SAMPLE_FMT_NONE) @@ -77,14 +82,14 @@ size_t AudioFrame::getSize() const return 0; } - const size_t size = getNbSamplesPerChannel() * getNbChannels() * av_get_bytes_per_sample(getSampleFormat()); + const size_t size = getNbSamplesPerChannel() * getNbChannels() * getBytesPerSample(); if(size == 0) { std::stringstream msg; msg << "Unable to determine audio buffer size:" << std::endl; msg << "nb sample per channel = " << getNbSamplesPerChannel() << std::endl; msg << "channels = " << getNbChannels() << std::endl; - msg << "bytes per sample = " << av_get_bytes_per_sample(getSampleFormat()) << std::endl; + msg << "bytes per sample = " << getBytesPerSample() << std::endl; throw std::runtime_error(msg.str()); } return size; diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 37ba8c1e..54ad8798 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -52,6 +52,7 @@ class AvExport AudioFrame : public IFrame size_t getChannelLayout() const { return av_frame_get_channel_layout(_frame); } std::string getChannelLayoutDesc() const; ///< Get a description of a channel layout (example: '5.1'). AVSampleFormat getSampleFormat() const { return static_cast(_frame->format); } + size_t getBytesPerSample() const; ///< 0 if unknown sample format size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } size_t getSize() const; From abb78c0303721f6d6428e11c6bf374ea125375fc Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 10:43:02 +0100 Subject: [PATCH 34/58] frames: remove default copy constructors and assignment operator --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 4 ++++ src/AvTranscoder/data/decoded/IFrame.hpp | 4 ++++ src/AvTranscoder/data/decoded/VideoFrame.hpp | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 54ad8798..f86d84eb 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -35,6 +35,10 @@ struct AvExport AudioFrameDesc */ class AvExport AudioFrame : public IFrame { +private: + AudioFrame(const AudioFrame& otherFrame); + AudioFrame& operator=(const AudioFrame& otherFrame); + public: AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocation = true); ~AudioFrame(); diff --git a/src/AvTranscoder/data/decoded/IFrame.hpp b/src/AvTranscoder/data/decoded/IFrame.hpp index 4b11330e..50e33cfa 100644 --- a/src/AvTranscoder/data/decoded/IFrame.hpp +++ b/src/AvTranscoder/data/decoded/IFrame.hpp @@ -18,6 +18,10 @@ namespace avtranscoder */ class AvExport IFrame { +private: + IFrame(const IFrame& otherFrame); + IFrame& operator=(const IFrame& otherFrame); + public: /** * @brief Allocate an empty frame. diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index 72307806..f600dc4f 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -42,6 +42,11 @@ struct AvExport VideoFrameDesc */ class AvExport VideoFrame : public IFrame { +private: + VideoFrame(const VideoFrame& otherFrame); + VideoFrame& operator=(const VideoFrame& otherFrame); + + public: VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation = true); ~VideoFrame(); From a95aa1874a615e2e9647d2778cd2e14c7554877f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 10:50:38 +0100 Subject: [PATCH 35/58] pyTest: fix set video/audio frame "assign" method was renamed to "assignValue". --- test/pyTest/testSetFrame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pyTest/testSetFrame.py b/test/pyTest/testSetFrame.py index ec04f562..b9428a5e 100644 --- a/test/pyTest/testSetFrame.py +++ b/test/pyTest/testSetFrame.py @@ -39,7 +39,7 @@ def testSetVideoFrame(): # set video frame frame = av.VideoFrame(av.VideoFrameDesc(1920, 1080, "rgb24")) - frame.assign(i) + frame.assignValue(i) videoDecoder.setNextFrame( frame ) # end process @@ -85,7 +85,7 @@ def testSetAudioFrame(): # set video frame frame = av.AudioFrame(av.AudioFrameDesc(44100, 1, "s16")) - frame.assign(i) + frame.assignValue(i) audioDecoder.setNextFrame( frame ) # end process From ccdbbeaf48fba3ae3f8440823b7979751d1b88f6 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 11:06:10 +0100 Subject: [PATCH 36/58] FormatContext: fix memory leak when adding new streams to an OutputFile See the ffmpeg/libav documentation of avformat_new_stream function: https://www.ffmpeg.org/doxygen/2.7/group__lavf__core.html#gadcb0fd3e507d9b58fe78f61f8ad39827 --- src/AvTranscoder/file/FormatContext.cpp | 7 +++++-- src/AvTranscoder/file/FormatContext.hpp | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/file/FormatContext.cpp b/src/AvTranscoder/file/FormatContext.cpp index 7e1f1a05..51d78f4e 100644 --- a/src/AvTranscoder/file/FormatContext.cpp +++ b/src/AvTranscoder/file/FormatContext.cpp @@ -8,6 +8,7 @@ namespace avtranscoder FormatContext::FormatContext(const std::string& filename, int req_flags, AVDictionary** options) : _avFormatContext(NULL) + , _avStreamAllocated() , _flags(req_flags) , _options() , _isOpen(false) @@ -31,6 +32,7 @@ FormatContext::FormatContext(const std::string& filename, int req_flags, AVDicti FormatContext::FormatContext(int req_flags) : _avFormatContext(NULL) + , _avStreamAllocated() , _flags(req_flags) , _options() , _isOpen(false) @@ -45,8 +47,8 @@ FormatContext::~FormatContext() return; // free the streams added - for(unsigned int i = 0; i < _avFormatContext->nb_streams; ++i) - avcodec_close(_avFormatContext->streams[i]->codec); + for(std::vector::iterator it = _avStreamAllocated.begin(); it != _avStreamAllocated.end(); ++it) + avcodec_close((*it)->codec); // free the format context if(_isOpen) @@ -143,6 +145,7 @@ AVStream& FormatContext::addAVStream(const AVCodec& avCodec) { throw std::runtime_error("Unable to add new video stream"); } + _avStreamAllocated.push_back(stream); return *stream; } diff --git a/src/AvTranscoder/file/FormatContext.hpp b/src/AvTranscoder/file/FormatContext.hpp index cce31442..0a33bbe1 100644 --- a/src/AvTranscoder/file/FormatContext.hpp +++ b/src/AvTranscoder/file/FormatContext.hpp @@ -122,6 +122,7 @@ class AvExport FormatContext private: AVFormatContext* _avFormatContext; ///< Has ownership + std::vector _avStreamAllocated; ///< Has link (no ownership) const int _flags; ///< Flags with which the options are loaded (see AV_OPT_FLAG_xxx) OptionMap _options; bool _isOpen; ///< Is the AVFormatContext open (in constructor with a filename) From 50d46ad39d2036e87b21e7b65e1e5fe821de2072 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 11:36:38 +0100 Subject: [PATCH 37/58] Video/Audio frames: fix message logged when failing to allocate data * "os" is the name of the variable in LOG_* defines, so rename it to "stream". * Print "none" when the format is no found. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 17 +++++++++-------- src/AvTranscoder/data/decoded/VideoFrame.cpp | 13 +++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index 2d0404c4..a829da10 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -110,14 +110,15 @@ void AudioFrame::allocateData() av_samples_alloc(_frame->data, _frame->linesize, _frame->channels, _frame->nb_samples, _desc._sampleFormat, align); if(ret < 0) { - std::stringstream os; - os << "Unable to allocate an audio frame of "; - os << "sample rate = " << _frame->sample_rate << ", "; - os << "nb channels = " << _frame->channels << ", "; - os << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; - os << "nb samples = " << _frame->nb_samples << ", "; - os << "sample format = " << getSampleFormatName(_desc._sampleFormat); - LOG_ERROR(os.str()) + const std::string formatName = getSampleFormatName(_desc._sampleFormat); + std::stringstream stream; + stream << "Unable to allocate an audio frame of "; + stream << "sample rate = " << _frame->sample_rate << ", "; + stream << "nb channels = " << _frame->channels << ", "; + stream << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; + stream << "nb samples = " << _frame->nb_samples << ", "; + stream << "sample format = " << (formatName.empty() ? "none" : formatName); + LOG_ERROR(stream.str()) throw std::bad_alloc(); } _dataAllocated = true; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 868cb796..f9254d20 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -90,12 +90,13 @@ void VideoFrame::allocateData() const int ret = avpicture_alloc(reinterpret_cast(_frame), _desc._pixelFormat, _desc._width, _desc._height); if(ret < 0) { - std::stringstream os; - os << "Unable to allocate an image frame of "; - os << "width = " << _frame->width << ", "; - os << "height = " << _frame->height << ", "; - os << "pixel format = " << getPixelFormatName(_desc._pixelFormat); - LOG_ERROR(os.str()) + const std::string formatName = getPixelFormatName(_desc._pixelFormat); + std::stringstream stream; + stream << "Unable to allocate an image frame of "; + stream << "width = " << _frame->width << ", "; + stream << "height = " << _frame->height << ", "; + stream << "pixel format = " << (formatName.empty() ? "none" : formatName); + LOG_ERROR(stream.str()) throw std::bad_alloc(); } _dataAllocated = true; From ca3c386d242f261fe5dd8d2820c17ee295b3d57f Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 11:56:15 +0100 Subject: [PATCH 38/58] VideoFrame: update constructors * Remove default constructor. * Add a constructor from a profile. --- src/AvTranscoder/codec/VideoCodec.cpp | 4 +++- src/AvTranscoder/data/decoded/VideoFrame.cpp | 14 ++++++++------ src/AvTranscoder/data/decoded/VideoFrame.hpp | 3 +-- src/AvTranscoder/properties/VideoProperties.cpp | 4 ++-- src/AvTranscoder/reader/VideoReader.cpp | 5 +++-- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 3 +-- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/AvTranscoder/codec/VideoCodec.cpp b/src/AvTranscoder/codec/VideoCodec.cpp index 081aba82..f12e7fb9 100644 --- a/src/AvTranscoder/codec/VideoCodec.cpp +++ b/src/AvTranscoder/codec/VideoCodec.cpp @@ -1,5 +1,7 @@ #include "VideoCodec.hpp" +#include + #include #include @@ -24,7 +26,7 @@ VideoCodec::VideoCodec(const ECodecType type, AVCodecContext& avCodecContext) VideoFrameDesc VideoCodec::getVideoFrameDesc() const { assert(_avCodecContext != NULL); - VideoFrameDesc videoFrameDesc(_avCodecContext->width, _avCodecContext->height, _avCodecContext->pix_fmt); + VideoFrameDesc videoFrameDesc(_avCodecContext->width, _avCodecContext->height, getPixelFormatName(_avCodecContext->pix_fmt)); double fps = 1.0 * _avCodecContext->time_base.den / (_avCodecContext->time_base.num * _avCodecContext->ticks_per_frame); if(!std::isinf(fps)) videoFrameDesc._fps = fps; diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index f9254d20..adf2cf75 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -13,22 +13,24 @@ extern "C" { namespace avtranscoder { -VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const AVPixelFormat pixelFormat) +VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormatName) : _width(width) , _height(height) - , _pixelFormat(pixelFormat) + , _pixelFormat(getAVPixelFormat(pixelFormatName)) , _fps(1.0) { } -VideoFrameDesc::VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormatName) - : _width(width) - , _height(height) - , _pixelFormat(getAVPixelFormat(pixelFormatName)) +VideoFrameDesc::VideoFrameDesc(const ProfileLoader::Profile& profile) + : _width(0) + , _height(0) + , _pixelFormat(AV_PIX_FMT_NONE) , _fps(1.0) { + setParameters(profile); } + void VideoFrameDesc::setParameters(const ProfileLoader::Profile& profile) { // width diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index f600dc4f..4fdae037 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -21,8 +21,8 @@ namespace avtranscoder struct AvExport VideoFrameDesc { public: - VideoFrameDesc(const size_t width = 0, const size_t height = 0, const AVPixelFormat pixelFormat = AV_PIX_FMT_NONE); VideoFrameDesc(const size_t width, const size_t height, const std::string& pixelFormatName); + VideoFrameDesc(const ProfileLoader::Profile& profile); /** * @brief Set the attributes from the given profile. @@ -46,7 +46,6 @@ class AvExport VideoFrame : public IFrame VideoFrame(const VideoFrame& otherFrame); VideoFrame& operator=(const VideoFrame& otherFrame); - public: VideoFrame(const VideoFrameDesc& desc, const bool forceDataAllocation = true); ~VideoFrame(); diff --git a/src/AvTranscoder/properties/VideoProperties.cpp b/src/AvTranscoder/properties/VideoProperties.cpp index 91f5c454..a3f83a19 100644 --- a/src/AvTranscoder/properties/VideoProperties.cpp +++ b/src/AvTranscoder/properties/VideoProperties.cpp @@ -347,7 +347,7 @@ size_t VideoProperties::getBitRate() const avcodec_open2(_codecContext, _codec, NULL); - VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getAVPixelFormat()), false); + VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false); AVFrame& avFrame = frame.getAVFrame(); int gotFrame = 0; @@ -569,7 +569,7 @@ void VideoProperties::analyseGopStructure(IProgress& progress) // Initialize the AVCodecContext to use the given AVCodec avcodec_open2(_codecContext, _codec, NULL); - VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getAVPixelFormat()), false); + VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false); AVFrame& avFrame = frame.getAVFrame(); size_t count = 0; diff --git a/src/AvTranscoder/reader/VideoReader.cpp b/src/AvTranscoder/reader/VideoReader.cpp index e5c0f931..b6fd245c 100644 --- a/src/AvTranscoder/reader/VideoReader.cpp +++ b/src/AvTranscoder/reader/VideoReader.cpp @@ -1,5 +1,6 @@ #include "VideoReader.hpp" +#include #include #include #include @@ -50,7 +51,7 @@ void VideoReader::init() // create dst frame _outputWidth = srcFrame->getWidth(); _outputHeight = srcFrame->getHeight(); - _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getOutputPixelFormat())); + _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getPixelFormatName(getOutputPixelFormat()))); // generator _generator = new VideoGenerator(srcFrameDesc); @@ -75,6 +76,6 @@ void VideoReader::updateOutput(const size_t width, const size_t height, const st _outputPixelProperties = PixelProperties(pixelFormat); // update dst frame delete _dstFrame; - _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getOutputPixelFormat())); + _dstFrame = new VideoFrame(VideoFrameDesc(_outputWidth, _outputHeight, getPixelFormatName(getOutputPixelFormat()))); } } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 2c3a2ebb..b294ef2c 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -301,8 +301,7 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: if(profile.find(constants::avProfileType)->second == constants::avProfileTypeVideo) { VideoCodec inputVideoCodec(eCodecTypeEncoder, profile.find(constants::avProfileCodec)->second); - VideoFrameDesc inputFrameDesc; - inputFrameDesc.setParameters(profile); + VideoFrameDesc inputFrameDesc(profile); inputVideoCodec.setImageParameters(inputFrameDesc); // generator decoder From e5ee8527f47751948e8ff08a6dd72bd89c02d893 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:01:53 +0100 Subject: [PATCH 39/58] AudioFrame: update constructors * Remove default constructor. * Add a constructor from a profile. --- src/AvTranscoder/codec/AudioCodec.cpp | 4 +++- src/AvTranscoder/data/decoded/AudioFrame.cpp | 13 +++++++------ src/AvTranscoder/data/decoded/AudioFrame.hpp | 3 +-- src/AvTranscoder/decoder/AudioDecoder.cpp | 3 ++- src/AvTranscoder/reader/AudioReader.cpp | 4 ++-- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 3 +-- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/AvTranscoder/codec/AudioCodec.cpp b/src/AvTranscoder/codec/AudioCodec.cpp index f5460f5e..ba4a349f 100644 --- a/src/AvTranscoder/codec/AudioCodec.cpp +++ b/src/AvTranscoder/codec/AudioCodec.cpp @@ -1,5 +1,7 @@ #include "AudioCodec.hpp" +#include + #include #include @@ -24,7 +26,7 @@ AudioCodec::AudioCodec(const ECodecType type, AVCodecContext& avCodecContext) AudioFrameDesc AudioCodec::getAudioFrameDesc() const { assert(_avCodecContext != NULL); - return AudioFrameDesc(_avCodecContext->sample_rate, _avCodecContext->channels, _avCodecContext->sample_fmt); + return AudioFrameDesc(_avCodecContext->sample_rate, _avCodecContext->channels, getSampleFormatName(_avCodecContext->sample_fmt)); } void AudioCodec::setAudioParameters(const AudioFrameDesc& audioFrameDesc) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index a829da10..ea53e818 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -12,18 +12,19 @@ extern "C" { namespace avtranscoder { -AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t nbChannels, const AVSampleFormat sampleFormat) +AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t nbChannels, const std::string& sampleFormatName) : _sampleRate(sampleRate) , _nbChannels(nbChannels) - , _sampleFormat(sampleFormat) + , _sampleFormat(getAVSampleFormat(sampleFormatName)) { } -AudioFrameDesc::AudioFrameDesc(const size_t sampleRate, const size_t nbChannels, const std::string& sampleFormatName) - : _sampleRate(sampleRate) - , _nbChannels(nbChannels) - , _sampleFormat(getAVSampleFormat(sampleFormatName)) +AudioFrameDesc::AudioFrameDesc(const ProfileLoader::Profile& profile) + : _sampleRate(0) + , _nbChannels(0) + , _sampleFormat(AV_SAMPLE_FMT_NONE) { + setParameters(profile); } void AudioFrameDesc::setParameters(const ProfileLoader::Profile& profile) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index f86d84eb..573e1986 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -14,9 +14,8 @@ namespace avtranscoder struct AvExport AudioFrameDesc { public: - AudioFrameDesc(const size_t sampleRate = 0, const size_t channels = 0, - const AVSampleFormat sampleFormat = AV_SAMPLE_FMT_NONE); AudioFrameDesc(const size_t sampleRate, const size_t channels, const std::string& sampleFormatName); + AudioFrameDesc(const ProfileLoader::Profile& profile); /** * @brief Set the attributes from the given profile. diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 9b3e5256..69b6afa5 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -1,5 +1,6 @@ #include "AudioDecoder.hpp" +#include #include #include #include @@ -133,7 +134,7 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector(frameBuffer); - AudioFrame allDataOfNextFrame(AudioFrameDesc(audioBuffer.getSampleRate(), srcNbChannels, audioBuffer.getSampleFormat()), false); + AudioFrame allDataOfNextFrame(AudioFrameDesc(audioBuffer.getSampleRate(), srcNbChannels, getSampleFormatName(audioBuffer.getSampleFormat())), false); if(!decodeNextFrame(allDataOfNextFrame)) return false; diff --git a/src/AvTranscoder/reader/AudioReader.cpp b/src/AvTranscoder/reader/AudioReader.cpp index cabb49bd..09b5bb11 100644 --- a/src/AvTranscoder/reader/AudioReader.cpp +++ b/src/AvTranscoder/reader/AudioReader.cpp @@ -54,7 +54,7 @@ void AudioReader::init() // create dst frame _outputSampleRate = srcFrame->getSampleRate(); _outputNbChannels = (_channelIndex == -1) ? srcFrame->getNbChannels() : 1; - _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, _outputSampleFormat)); + _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, getSampleFormatName(_outputSampleFormat))); // generator _generator = new AudioGenerator(srcFrameDesc); @@ -79,6 +79,6 @@ void AudioReader::updateOutput(const size_t sampleRate, const size_t nbChannels, _outputSampleFormat = getAVSampleFormat(sampleFormat); // update dst frame delete _dstFrame; - _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, _outputSampleFormat)); + _dstFrame = new AudioFrame(AudioFrameDesc(_outputSampleRate, _outputNbChannels, getSampleFormatName(_outputSampleFormat))); } } diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index b294ef2c..072762a0 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -333,8 +333,7 @@ StreamTranscoder::StreamTranscoder(IOutputFile& outputFile, const ProfileLoader: else if(profile.find(constants::avProfileType)->second == constants::avProfileTypeAudio) { AudioCodec inputAudioCodec(eCodecTypeEncoder, profile.find(constants::avProfileCodec)->second); - AudioFrameDesc inputFrameDesc; - inputFrameDesc.setParameters(profile); + AudioFrameDesc inputFrameDesc(profile); inputAudioCodec.setAudioParameters(inputFrameDesc); // generator decoder From eb3f708e8fdd8956ea87f02558ada971a0ccf047 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:15:19 +0100 Subject: [PATCH 40/58] Video/Audio frames: reorder methods "getSize" is about the data buffer. --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 3 +-- src/AvTranscoder/data/decoded/VideoFrame.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 573e1986..22d7c5a9 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -49,6 +49,7 @@ class AvExport AudioFrame : public IFrame */ void allocateData(); void freeData(); + size_t getSize() const; size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } size_t getNbChannels() const { return av_frame_get_channels(_frame); } @@ -58,8 +59,6 @@ class AvExport AudioFrame : public IFrame size_t getBytesPerSample() const; ///< 0 if unknown sample format size_t getNbSamplesPerChannel() const { return _frame->nb_samples; } - size_t getSize() const; - /** * @brief This methods dynamically updates the size that the data buffer would occupy if allocated. * @warning If the data buffer is already allocated, this could lead to memory leaks or seg fault. diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index 4fdae037..4b93133a 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -57,13 +57,12 @@ class AvExport VideoFrame : public IFrame */ void allocateData(); void freeData(); + size_t getSize() const; size_t getWidth() const { return _frame->width; } size_t getHeight() const { return _frame->height; } AVPixelFormat getPixelFormat() const { return static_cast(_frame->format); } - size_t getSize() const; - void assignBuffer(const unsigned char* ptrValue); private: From 9c90c2d579abdae6be409608ac9934b2cf0bdf1e Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:16:21 +0100 Subject: [PATCH 41/58] pyTest: add tests to check Video/Audio frames --- test/pyTest/testAudioFrame.py | 52 +++++++++++++++++++++++++++++++++++ test/pyTest/testVideoFrame.py | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 test/pyTest/testAudioFrame.py create mode 100644 test/pyTest/testVideoFrame.py diff --git a/test/pyTest/testAudioFrame.py b/test/pyTest/testAudioFrame.py new file mode 100644 index 00000000..1f5606a2 --- /dev/null +++ b/test/pyTest/testAudioFrame.py @@ -0,0 +1,52 @@ +from nose.tools import * + +from pyAvTranscoder import avtranscoder as av + + +@raises(MemoryError) +def testInvalidAudioFrameAutoAllocated(): + """ + Try to create an invalid AudioFrame automatically allocated. + """ + desc = av.AudioFrameDesc(4800, 1, "toto") + av.AudioFrame(desc) + + +@raises(MemoryError) +def testInvalidAudioFrameManualAllocated(): + """ + Try to create an invalid AudioFrame manually allocated. + """ + sampleRate = 48000 + nbChannels = 1 + sampleFormat = "titi" + desc = av.AudioFrameDesc(sampleRate, nbChannels, sampleFormat) + frame = av.AudioFrame(desc, False) + + assert_equals(frame.isDataAllocated(), False) + assert_equals(frame.getSize(), 0) + assert_equals(frame.getSampleRate(), sampleRate) + assert_equals(frame.getNbChannels(), nbChannels) + assert_equals(frame.getChannelLayoutDesc(), "mono") + assert_equals(frame.getNbSamplesPerChannel(), 1920) + assert_equals(frame.getBytesPerSample(), 0) + assert_equals(av.getSampleFormatName(frame.getSampleFormat()), "") + + frame.allocateData() + +def testAudioFrame(): + """ + Check the size and the data buffer of a AudioFrame. + """ + sampleRate = 48000 + nbChannels = 1 + sampleFormat = "s32" + desc = av.AudioFrameDesc(sampleRate, nbChannels, sampleFormat) + frame = av.AudioFrame(desc) + + assert_equals(frame.isDataAllocated(), True) + assert_equals(frame.isAudioFrame(), True) + assert_equals(frame.getSize(), frame.getNbSamplesPerChannel() * frame.getBytesPerSample() * nbChannels) + + frame.freeData() + assert_equals(frame.isDataAllocated(), False) diff --git a/test/pyTest/testVideoFrame.py b/test/pyTest/testVideoFrame.py new file mode 100644 index 00000000..7b165f1c --- /dev/null +++ b/test/pyTest/testVideoFrame.py @@ -0,0 +1,49 @@ +from nose.tools import * + +from pyAvTranscoder import avtranscoder as av + + +@raises(MemoryError) +def testInvalidVideoFrameAutoAllocated(): + """ + Try to create an invalid VideoFrame automatically allocated. + """ + desc = av.VideoFrameDesc(1920, 1080, "toto") + av.VideoFrame(desc) + + +@raises(MemoryError) +def testInvalidVideoFrameManualAllocated(): + """ + Try to create an invalid VideoFrame manually allocated. + """ + width = 1920 + height = 1080 + pixelFormat = "titi" + desc = av.VideoFrameDesc(width, height, pixelFormat) + frame = av.VideoFrame(desc, False) + + assert_equals(frame.isDataAllocated(), False) + assert_equals(frame.getSize(), 0) + assert_equals(frame.getWidth(), width) + assert_equals(frame.getHeight(), height) + assert_equals(av.getPixelFormatName(frame.getPixelFormat()), "") + + frame.allocateData() + +def testVideoFrame(): + """ + Check the size and the data buffer of a VideoFrame. + """ + width = 1920 + height = 1080 + pixelFormat = "yuv420p" + desc = av.VideoFrameDesc(width, height, pixelFormat) + frame = av.VideoFrame(desc) + + assert_equals(frame.isDataAllocated(), True) + assert_equals(frame.isVideoFrame(), True) + assert_equals(frame.getSize(), width * height * 1.5) + + frame.freeData() + assert_equals(frame.isDataAllocated(), False) From 4f56f1ab35b30e21ea1306f98441b5a07dbbc918 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:22:31 +0100 Subject: [PATCH 42/58] VideoFrame: get error description when failing to get AVPicture size --- src/AvTranscoder/data/decoded/VideoFrame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index adf2cf75..e5cc56a4 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -77,7 +77,7 @@ size_t VideoFrame::getSize() const const size_t size = avpicture_get_size(getPixelFormat(), getWidth(), getHeight()); if(size == 0) - throw std::runtime_error("unable to determine image buffer size"); + throw std::runtime_error("Unable to determine image buffer size: " + getDescriptionFromErrorCode(size)); return size; } From 5cd30bcd50d20157c8139d762227bb4f33db7054 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:27:36 +0100 Subject: [PATCH 43/58] pyTest: split reader tests to testVideo/AudioReader.py --- test/pyTest/testAudioReader.py | 65 +++++++++++++++++++ .../{testReader.py => testVideoReader.py} | 62 +----------------- 2 files changed, 67 insertions(+), 60 deletions(-) create mode 100644 test/pyTest/testAudioReader.py rename test/pyTest/{testReader.py => testVideoReader.py} (52%) diff --git a/test/pyTest/testAudioReader.py b/test/pyTest/testAudioReader.py new file mode 100644 index 00000000..7f7fa88d --- /dev/null +++ b/test/pyTest/testAudioReader.py @@ -0,0 +1,65 @@ +import os + +# Check if environment is setup to run the tests +if 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_AUDIO_WAVE_FILE") + +from nose.tools import * + +from pyAvTranscoder import avtranscoder as av + + +def testAudioReaderChannelsExtraction(): + """ + Read the same audio stream with several AudioReaders. + Compare decoded frames from reader of all channels, and of one channel. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + inputFile = av.InputFile(inputFileName) + streamIndex = inputFile.getProperties().getAudioProperties()[0].getStreamIndex() + channelIndex = 0 + + # create reader to read all channels of the audio stream + readerOfAllChannels = av.AudioReader(inputFile, streamIndex) + nbChannels = readerOfAllChannels.getOutputNbChannels() + # read first frame + frame = readerOfAllChannels.readNextFrame() + sizeOfFrameWithAllChannels = frame.getSize() + + # create reader to read one channel of the audio stream + readerOfOneChannel = av.AudioReader(inputFile, streamIndex, channelIndex) + # read first frame + frame = readerOfOneChannel.readNextFrame() + sizeOfFrameWithOneChannels = frame.getSize() + + assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels ) + + +def testAudioReaderWithGenerator(): + """ + Read an audio stream with the AudioReader. + When there is no more data to decode, switch to a generator and process some frames. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + inputFile = av.InputFile(inputFileName) + reader = av.AudioReader(inputFile) + + # read all frames and check their size + while True: + frame = reader.readNextFrame() + if not frame: + break + assert_greater(frame.getSize(), 0) + + # check if there is no next frame + assert_equals( reader.readNextFrame(), None ) + + # generate 10 frames of silence + reader.continueWithGenerator() + for i in xrange(0, 9): + frame = reader.readNextFrame() + # assuming we generate data of 1920 samples of 2 bytes + nbSamplesPerChannel = 1920 + bytesPerSample = 2 + assert_equals(frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) diff --git a/test/pyTest/testReader.py b/test/pyTest/testVideoReader.py similarity index 52% rename from test/pyTest/testReader.py rename to test/pyTest/testVideoReader.py index 1fcf4965..46686e78 100644 --- a/test/pyTest/testReader.py +++ b/test/pyTest/testVideoReader.py @@ -1,12 +1,9 @@ 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: +if os.environ.get('AVTRANSCODER_TEST_VIDEO_AVI_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") + raise SkipTest("Need to define environment variables AVTRANSCODER_TEST_VIDEO_AVI_FILE") from nose.tools import * @@ -51,32 +48,6 @@ def testVideoReaderReferenceInputFile(): assert_equals( reader.readNextFrame(), None ) -def testAudioReaderChannelsExtraction(): - """ - Read the same audio stream with several AudioReaders. - Compare decoded frames from reader of all channels, and of one channel. - """ - inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] - inputFile = av.InputFile(inputFileName) - streamIndex = inputFile.getProperties().getAudioProperties()[0].getStreamIndex() - channelIndex = 0 - - # create reader to read all channels of the audio stream - readerOfAllChannels = av.AudioReader(inputFile, streamIndex) - nbChannels = readerOfAllChannels.getOutputNbChannels() - # read first frame - frame = readerOfAllChannels.readNextFrame() - sizeOfFrameWithAllChannels = frame.getSize() - - # create reader to read one channel of the audio stream - readerOfOneChannel = av.AudioReader(inputFile, streamIndex, channelIndex) - # read first frame - frame = readerOfOneChannel.readNextFrame() - sizeOfFrameWithOneChannels = frame.getSize() - - assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels ) - - def testVideoReaderWithGenerator(): """ Read a video stream with the VideoReader. @@ -100,32 +71,3 @@ def testVideoReaderWithGenerator(): frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) - - -def testAudioReaderWithGenerator(): - """ - Read an audio stream with the AudioReader. - When there is no more data to decode, switch to a generator and process some frames. - """ - inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] - inputFile = av.InputFile(inputFileName) - reader = av.AudioReader(inputFile) - - # read all frames and check their size - while True: - frame = reader.readNextFrame() - if not frame: - break - assert_greater(frame.getSize(), 0) - - # check if there is no next frame - assert_equals( reader.readNextFrame(), None ) - - # generate 10 frames of silence - reader.continueWithGenerator() - for i in xrange(0, 9): - frame = reader.readNextFrame() - # assuming we generate data of 1920 samples of 2 bytes - nbSamplesPerChannel = 1920 - bytesPerSample = 2 - assert_equals(frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) From 86502f4906d2c4f966d44cb76005a54e87eee521 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 12:32:59 +0100 Subject: [PATCH 44/58] pyTest: added test to create an InputFile inside an AudioReader --- test/pyTest/testAudioReader.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/pyTest/testAudioReader.py b/test/pyTest/testAudioReader.py index 7f7fa88d..1579b053 100644 --- a/test/pyTest/testAudioReader.py +++ b/test/pyTest/testAudioReader.py @@ -10,6 +10,26 @@ from pyAvTranscoder import avtranscoder as av +def testAudioReaderCreateNewInputFile(): + """ + Read a audio stream with the AudioReader. + The InputFile is created inside the reader. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + reader = av.AudioReader(inputFileName) + + # read all frames and check their size + while True: + frame = reader.readNextFrame() + if not frame: + break + assert_greater(frame.getSize(), 0) + + # check if there is no next frame + frame = reader.readNextFrame() + assert_equals( reader.readNextFrame(), None ) + + def testAudioReaderChannelsExtraction(): """ Read the same audio stream with several AudioReaders. From 173538bc78bf692106eb934cbd717299a53cbec8 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 16:42:27 +0100 Subject: [PATCH 45/58] AudioDecoder: check if each extracted channel exists before decoding Add a pyTest to validate this case. --- src/AvTranscoder/decoder/AudioDecoder.cpp | 32 +++++++++++------------ test/pyTest/testTranscoderAdd.py | 19 ++++++++++++++ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 69b6afa5..ed2d8af7 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -128,6 +128,22 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector::const_iterator channelIndex = channelIndexArray.begin(); + channelIndex != channelIndexArray.end(); ++channelIndex) + { + if((*channelIndex) > srcNbChannels - 1) + { + std::stringstream msg; + msg << "The channel at index "; + msg << (*channelIndex); + msg << " doesn't exist (srcNbChannels = "; + msg << srcNbChannels; + msg << ")."; + throw std::runtime_error(msg.str()); + } + } + // if all channels of the stream are extracted if(srcNbChannels == channelIndexArray.size()) return decodeNextFrame(frameBuffer); @@ -145,22 +161,6 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector::const_iterator channelIndex = channelIndexArray.begin(); - channelIndex != channelIndexArray.end(); ++channelIndex) - { - if((*channelIndex) > srcNbChannels - 1) - { - std::stringstream msg; - msg << "The channel at index "; - msg << (*channelIndex); - msg << " doesn't exist (srcNbChannels = "; - msg << srcNbChannels; - msg << ")."; - throw std::runtime_error(msg.str()); - } - } - // copy frame properties of decoded frame audioBuffer.copyProperties(allDataOfNextFrame); audioBuffer.setNbSamplesPerChannel(allDataOfNextFrame.getNbSamplesPerChannel()); diff --git a/test/pyTest/testTranscoderAdd.py b/test/pyTest/testTranscoderAdd.py index bb0efbc5..5edb9e8f 100644 --- a/test/pyTest/testTranscoderAdd.py +++ b/test/pyTest/testTranscoderAdd.py @@ -69,6 +69,25 @@ def testEmptyListOfInputs(): transcoder.addStream(inputs) +@raises(RuntimeError) +def testAddOneChannelWhichDoesNotExist(): + """ + Extract one audio channel from an input stream. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_FILE'] + outputFileName = "testAddOneChannelWhichDoesNotExist.wav" + + ouputFile = av.OutputFile(outputFileName) + transcoder = av.Transcoder(ouputFile) + + inputFile = av.InputFile(inputFileName) + src_audioStream = inputFile.getProperties().getAudioProperties()[0] + audioStreamIndex = src_audioStream.getStreamIndex() + transcoder.addStream(av.InputStreamDesc(inputFileName, audioStreamIndex, 15)) + + transcoder.process() + + @raises(RuntimeError) def testAllSeveralInputsWithDifferentType(): """ From 8e5b9ef165f790cd95d3832157a243bd4698de63 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 18:00:22 +0100 Subject: [PATCH 46/58] AudioDecoder: fix seg fault when trying to extract one channel of a mono stream * In that case, we don't need to manually allocate the data buffer. * Solution: the "decodeNextFrame" method manages to allocate the data buffer if needed. It's seem to be easier to maintain. --- src/AvTranscoder/decoder/AudioDecoder.cpp | 4 +++- src/AvTranscoder/reader/AudioReader.cpp | 5 +---- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 6 +----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index ed2d8af7..9b258e5b 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -161,9 +161,11 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vectorgetStream(_streamIndex).getAudioCodec().getAudioFrameDesc(); - bool allocateDecodedData = false; - if(_channelIndex != -1) - allocateDecodedData = true; - _srcFrame = new AudioFrame(srcFrameDesc, allocateDecodedData); + _srcFrame = new AudioFrame(srcFrameDesc, false); AudioFrame* srcFrame = static_cast(_srcFrame); // create dst frame _outputSampleRate = srcFrame->getSampleRate(); diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 072762a0..d9db5cb5 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -261,13 +261,9 @@ void StreamTranscoder::addDecoder(const InputStreamDesc& inputStreamDesc, IInput // buffers to get the decoded data AudioFrameDesc inputFrameDesc(inputStream.getAudioCodec().getAudioFrameDesc()); - bool allocateDecodedData = false; if(inputStreamDesc.demultiplexing()) - { inputFrameDesc._nbChannels = inputStreamDesc._channelIndexArray.size(); - allocateDecodedData = true; - } - _decodedData.push_back(new AudioFrame(inputFrameDesc, allocateDecodedData)); + _decodedData.push_back(new AudioFrame(inputFrameDesc, false)); // generator decoder _generators.push_back(new AudioGenerator(inputFrameDesc)); From d9b2f41e61d874a4fd4bb773077e5a5d21e779a9 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 31 Oct 2016 18:27:42 +0100 Subject: [PATCH 47/58] avprocessor app: fix conditional jump which depends on uninitialised value --- app/avProcessor/avProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/avProcessor/avProcessor.cpp b/app/avProcessor/avProcessor.cpp index 2c367dcb..cf046635 100644 --- a/app/avProcessor/avProcessor.cpp +++ b/app/avProcessor/avProcessor.cpp @@ -28,7 +28,7 @@ void parseConfigFile(const std::string& configFilename, avtranscoder::Transcoder std::stringstream ss(streamId); size_t streamIndex = 0; - char separator; + char separator = 'x'; std::vector channelIndexArray; ss >> streamIndex; ss >> separator; From 249bcfbc67b458bf0dd9d3abac1c44af49a5f1a3 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 2 Nov 2016 09:39:33 +0100 Subject: [PATCH 48/58] AudioFrame: refactor how to get a default number of samples * Add a private method. * Add doc about this tricky case. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 10 ++++++++-- src/AvTranscoder/data/decoded/AudioFrame.hpp | 11 +++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index ea53e818..bac615c5 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -49,7 +49,7 @@ AudioFrame::AudioFrame(const AudioFrameDesc& desc, const bool forceDataAllocatio av_frame_set_channels(_frame, desc._nbChannels); av_frame_set_channel_layout(_frame, av_get_default_channel_layout(desc._nbChannels)); _frame->format = desc._sampleFormat; - _frame->nb_samples = desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + _frame->nb_samples = getDefaultNbSamples(); if(forceDataAllocation) allocateData(); @@ -103,7 +103,7 @@ void AudioFrame::allocateData() av_frame_set_channels(_frame, _desc._nbChannels); av_frame_set_channel_layout(_frame, av_get_default_channel_layout(_desc._nbChannels)); _frame->format = _desc._sampleFormat; - _frame->nb_samples = _desc._sampleRate / 25.; // cannot be known before calling avcodec_decode_audio4 + _frame->nb_samples = getDefaultNbSamples(); // Allocate data const int align = 0; @@ -143,4 +143,10 @@ void AudioFrame::assignBuffer(const unsigned char* ptrValue) throw std::runtime_error(os.str()); } } + +size_t AudioFrame::getDefaultNbSamples() const +{ + return _desc._sampleRate / 25.; +} + } diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 22d7c5a9..23d338e2 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -67,6 +67,17 @@ class AvExport AudioFrame : public IFrame void assignBuffer(const unsigned char* ptrValue); +private: + /** + * @brief The number of samples of a frame cannot be known before calling avcodec_decode_audio4, + * and can be variable in a same stream. Because we need to allocate some frames without knowing this parameter, + * we access here a default number of samples. + * @note This value depends on the sample rate (excample: 1920 samples at 48kHz). + * @return the number of samples of our default AudioFrame. + * @see setNbSamplesPerChannel + */ + size_t getDefaultNbSamples() const; + private: /** * @brief Description of the frame to allocate. From da013d4f47d5021edc0701a899a0fc590f3669f4 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 2 Nov 2016 09:51:34 +0100 Subject: [PATCH 49/58] StreamTranscoder: check the next data buffers in case of audio frames * If the buffer of filtered data is too small: reallocate it. * If the buffer of transformed data is too small: reallocate it. --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 3 ++- .../transcoder/StreamTranscoder.cpp | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index bac615c5..b01b4bcd 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -103,7 +103,8 @@ void AudioFrame::allocateData() av_frame_set_channels(_frame, _desc._nbChannels); av_frame_set_channel_layout(_frame, av_get_default_channel_layout(_desc._nbChannels)); _frame->format = _desc._sampleFormat; - _frame->nb_samples = getDefaultNbSamples(); + if(_frame->nb_samples == 0) + _frame->nb_samples = getDefaultNbSamples(); // Allocate data const int align = 0; diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index d9db5cb5..5d940462 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -547,6 +547,26 @@ bool StreamTranscoder::processTranscode() decodingStatus = decodingStatus && _currentDecoder->decodeNextFrame(*_decodedData.at(index)); } + // check the next data buffers in case of audio frames + if(_decodedData.at(0)->isAudioFrame()) + { + const int nbInputSamplesPerChannel = _decodedData.at(0)->getAVFrame().nb_samples; + if(nbInputSamplesPerChannel > _filteredData->getAVFrame().nb_samples) + { + LOG_WARN("The buffer of filtered data is too small: reallocate it.") + _filteredData->freeData(); + _filteredData->getAVFrame().nb_samples = nbInputSamplesPerChannel; + _filteredData->allocateData(); + } + if(nbInputSamplesPerChannel > _transformedData->getAVFrame().nb_samples) + { + LOG_WARN("The buffer of transformed data is too small: reallocate it.") + _transformedData->freeData(); + _transformedData->getAVFrame().nb_samples = nbInputSamplesPerChannel; + _transformedData->allocateData(); + } + } + // Transform CodedData data; if(decodingStatus) From 5d4888f4835df1c3348b2416bd44c396a73c2426 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 2 Nov 2016 09:52:25 +0100 Subject: [PATCH 50/58] pyTest: add a test to extract one audio channel from a stream of one channel --- .../testTranscoderTranscodeAudioWave.py | 40 ++++++++++++++++++- tools/appveyor/python.nosetests.bat | 1 + tools/travis/python.nosetests.sh | 1 + 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/test/pyTest/testTranscoderTranscodeAudioWave.py b/test/pyTest/testTranscoderTranscodeAudioWave.py index acc1c108..de640613 100644 --- a/test/pyTest/testTranscoderTranscodeAudioWave.py +++ b/test/pyTest/testTranscoderTranscodeAudioWave.py @@ -1,15 +1,51 @@ import os # Check if environment is setup to run the tests -if os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_FILE') is None: +if os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_FILE') is None or \ + os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_MONO_FILE') is None: from nose.plugins.skip import SkipTest - raise SkipTest("Need to define environment variable AVTRANSCODER_TEST_AUDIO_WAVE_FILE") + raise SkipTest("Need to define environment variables " + "AVTRANSCODER_TEST_AUDIO_WAVE_FILE and" + "AVTRANSCODER_TEST_AUDIO_WAVE_MONO_FILE") from nose.tools import * from pyAvTranscoder import avtranscoder as av +def testTranscodeExtractOneChannelFromMono(): + """ + Extract one audio channel from a stream of one channel. + """ + inputFileName = os.environ['AVTRANSCODER_TEST_AUDIO_WAVE_MONO_FILE'] + outputFileName = "testTranscodeExtractOneChannelFromMono.wav" + + ouputFile = av.OutputFile(outputFileName) + transcoder = av.Transcoder(ouputFile) + + inputFile = av.InputFile(inputFileName) + src_audioStream = inputFile.getProperties().getAudioProperties()[0] + audioStreamIndex = src_audioStream.getStreamIndex() + transcoder.addStream(av.InputStreamDesc(inputFileName, audioStreamIndex, 0)) + + processStat = transcoder.process() + + # 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(dst_audioStream.getCodecName(), src_audioStream.getCodecName()) + assert_equals(dst_audioStream.getSampleFormatName(), src_audioStream.getSampleFormatName()) + assert_equals(dst_audioStream.getSampleFormatLongName(), src_audioStream.getSampleFormatLongName()) + assert_equals(dst_audioStream.getSampleRate(), src_audioStream.getSampleRate()) + assert_equals(dst_audioStream.getNbChannels(), 1) + + def testTranscodeWave24b48k5_1(): """ Transcode one audio stream (profile wave24b48k5_1). diff --git a/tools/appveyor/python.nosetests.bat b/tools/appveyor/python.nosetests.bat index 7deef392..eb2692a9 100755 --- a/tools/appveyor/python.nosetests.bat +++ b/tools/appveyor/python.nosetests.bat @@ -15,6 +15,7 @@ set AVTRANSCODER_TEST_VIDEO_AVI_FILE=%PWD%\avTranscoder-data/video\BigBuckBunny\ set AVTRANSCODER_TEST_VIDEO_MP4_FILE=%PWD%\avTranscoder-data\video\BigBuckBunny\BigBuckBunny_HD.mp4 set AVTRANSCODER_TEST_VIDEO_MOV_FILE=%PWD%\avTranscoder-data\video\BigBuckBunny\BigBuckBunny_640p_h264.mov set AVTRANSCODER_TEST_AUDIO_WAVE_FILE=%PWD%\avTranscoder-data\audio\frequenciesPerChannel.wav +set AVTRANSCODER_TEST_AUDIO_WAVE_MONO_FILE=%PWD%\avTranscoder-data\audio\frequenciesOneChannel.wav set AVTRANSCODER_TEST_AUDIO_MOV_FILE=%PWD%\avTranscoder-data\video\BigBuckBunny\BigBuckBunny_1080p_5_1.mov set AVTRANSCODER_TEST_IMAGE_PNG_FILE=%PWD%\avTranscoder-data\image\BigBuckBunny\bbb-splash.thumbnail.png set AVTRANSCODER_TEST_IMAGE_JPG_FILE=%PWD%\avTranscoder-data\image\BigBuckBunny\title_anouncement.thumbnail.jpg diff --git a/tools/travis/python.nosetests.sh b/tools/travis/python.nosetests.sh index 277a1dc6..d23c402e 100755 --- a/tools/travis/python.nosetests.sh +++ b/tools/travis/python.nosetests.sh @@ -14,6 +14,7 @@ export AVTRANSCODER_TEST_VIDEO_MP4_FILE=`pwd`/avTranscoder-data/video/BigBuckBun export AVTRANSCODER_TEST_VIDEO_MOV_FILE=`pwd`/avTranscoder-data/video/BigBuckBunny/BigBuckBunny_640p_h264.mov export AVTRANSCODER_TEST_VIDEO_RAW_FILE=`pwd`/avTranscoder-data/video/Intro/Intro_raw_1080p.h264 export AVTRANSCODER_TEST_AUDIO_WAVE_FILE=`pwd`/avTranscoder-data/audio/frequenciesPerChannel.wav +export AVTRANSCODER_TEST_AUDIO_WAVE_MONO_FILE=`pwd`/avTranscoder-data/audio/frequenciesOneChannel.wav export AVTRANSCODER_TEST_AUDIO_MOV_FILE=`pwd`/avTranscoder-data/video/BigBuckBunny/BigBuckBunny_1080p_5_1.mov export AVTRANSCODER_TEST_IMAGE_PNG_FILE=`pwd`/avTranscoder-data/image/BigBuckBunny/bbb-splash.thumbnail.png export AVTRANSCODER_TEST_IMAGE_JPG_FILE=`pwd`/avTranscoder-data/image/BigBuckBunny/title_anouncement.thumbnail.jpg From 3a03cbf8972d806a8e08da21ab610e337bd9dc8d Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 2 Nov 2016 09:54:32 +0100 Subject: [PATCH 51/58] AudioDecoder: refactor how to get bytes per sample --- src/AvTranscoder/decoder/AudioDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index 9b258e5b..bd4f6332 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -126,7 +126,6 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vectorgetAudioCodec().getAVCodecContext(); const size_t srcNbChannels = avCodecContext.channels; - const size_t bytePerSample = av_get_bytes_per_sample((AVSampleFormat)frameBuffer.getAVFrame().format); // check if each expected channel exists for(std::vector::const_iterator channelIndex = channelIndexArray.begin(); @@ -155,6 +154,7 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector Date: Wed, 2 Nov 2016 09:56:46 +0100 Subject: [PATCH 52/58] AudioDecoder: fix the number of channels used to compute the decoded size No real impact in the process, because we only check that "decodedSize" is not equal to 0. But it is more accurate :) --- src/AvTranscoder/decoder/AudioDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp index bd4f6332..4b7f6101 100644 --- a/src/AvTranscoder/decoder/AudioDecoder.cpp +++ b/src/AvTranscoder/decoder/AudioDecoder.cpp @@ -153,8 +153,8 @@ bool AudioDecoder::decodeNextFrame(IFrame& frameBuffer, const std::vector Date: Wed, 2 Nov 2016 11:04:32 +0100 Subject: [PATCH 53/58] StreamTranscoder: update log message when checking the next audio buffers --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 5d940462..2a05a7b4 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -553,14 +553,14 @@ bool StreamTranscoder::processTranscode() const int nbInputSamplesPerChannel = _decodedData.at(0)->getAVFrame().nb_samples; if(nbInputSamplesPerChannel > _filteredData->getAVFrame().nb_samples) { - LOG_WARN("The buffer of filtered data is too small: reallocate it.") + LOG_WARN("The buffer of filtered data corresponds to a frame of " << _filteredData->getAVFrame().nb_samples << " samples. The decoded buffer contains " << nbInputSamplesPerChannel << " samples. Reallocate it.") _filteredData->freeData(); _filteredData->getAVFrame().nb_samples = nbInputSamplesPerChannel; _filteredData->allocateData(); } if(nbInputSamplesPerChannel > _transformedData->getAVFrame().nb_samples) { - LOG_WARN("The buffer of transformed data is too small: reallocate it.") + LOG_WARN("The buffer of transformed data corresponds to a frame of " << _transformedData->getAVFrame().nb_samples << " samples. The decoded buffer contains " << nbInputSamplesPerChannel << " samples. Reallocate it.") _transformedData->freeData(); _transformedData->getAVFrame().nb_samples = nbInputSamplesPerChannel; _transformedData->allocateData(); From 93ce32889d33a6f1f38ed1b624659bc62646bb19 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Wed, 2 Nov 2016 11:33:05 +0100 Subject: [PATCH 54/58] StreamTranscoder: fix memory leak in case of generator after a demultiplexing In case of audio demultiplexing, we allocate the data buffer, so we should not reallocate it when we switch to a generator. --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 2a05a7b4..e3133342 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -599,7 +599,10 @@ bool StreamTranscoder::processTranscode() switchToGeneratorDecoder(); LOG_INFO("Force reallocation of the decoded data buffers since the decoders could have cleared them.") for(std::vector::iterator it = _decodedData.begin(); it != _decodedData.end(); ++it) - (*it)->allocateData(); + { + if(! (*it)->isDataAllocated()) + (*it)->allocateData(); + } return processTranscode(); } return false; From 11789bbc7d9e3381f4fcd2b399d29a55cfcc93a2 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 3 Nov 2016 12:13:53 +0100 Subject: [PATCH 55/58] frames: rename local variables to log messages --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 22 ++++++++++---------- src/AvTranscoder/data/decoded/VideoFrame.cpp | 18 ++++++++-------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index b01b4bcd..b1f1f2d9 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -113,14 +113,14 @@ void AudioFrame::allocateData() if(ret < 0) { const std::string formatName = getSampleFormatName(_desc._sampleFormat); - std::stringstream stream; - stream << "Unable to allocate an audio frame of "; - stream << "sample rate = " << _frame->sample_rate << ", "; - stream << "nb channels = " << _frame->channels << ", "; - stream << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; - stream << "nb samples = " << _frame->nb_samples << ", "; - stream << "sample format = " << (formatName.empty() ? "none" : formatName); - LOG_ERROR(stream.str()) + std::stringstream msg; + msg << "Unable to allocate an audio frame of "; + msg << "sample rate = " << _frame->sample_rate << ", "; + msg << "nb channels = " << _frame->channels << ", "; + msg << "channel layout = " << av_get_channel_name(_frame->channels) << ", "; + msg << "nb samples = " << _frame->nb_samples << ", "; + msg << "sample format = " << (formatName.empty() ? "none" : formatName); + LOG_ERROR(msg.str()) throw std::bad_alloc(); } _dataAllocated = true; @@ -139,9 +139,9 @@ void AudioFrame::assignBuffer(const unsigned char* ptrValue) getNbSamplesPerChannel(), getSampleFormat(), align); if(ret < 0) { - std::stringstream os; - os << "Unable to assign an audio buffer: " << getDescriptionFromErrorCode(ret); - throw std::runtime_error(os.str()); + std::stringstream msg; + msg << "Unable to assign an audio buffer: " << getDescriptionFromErrorCode(ret); + throw std::runtime_error(msg.str()); } } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index e5cc56a4..3c915227 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -93,12 +93,12 @@ void VideoFrame::allocateData() if(ret < 0) { const std::string formatName = getPixelFormatName(_desc._pixelFormat); - std::stringstream stream; - stream << "Unable to allocate an image frame of "; - stream << "width = " << _frame->width << ", "; - stream << "height = " << _frame->height << ", "; - stream << "pixel format = " << (formatName.empty() ? "none" : formatName); - LOG_ERROR(stream.str()) + std::stringstream msg; + msg << "Unable to allocate an image frame of "; + msg << "width = " << _frame->width << ", "; + msg << "height = " << _frame->height << ", "; + msg << "pixel format = " << (formatName.empty() ? "none" : formatName); + LOG_ERROR(msg.str()) throw std::bad_alloc(); } _dataAllocated = true; @@ -116,9 +116,9 @@ void VideoFrame::assignBuffer(const unsigned char* ptrValue) avpicture_fill(reinterpret_cast(_frame), ptrValue, getPixelFormat(), getWidth(), getHeight()); if(ret < 0) { - std::stringstream os; - os << "Unable to assign an image buffer of " << getSize() << " bytes: " << getDescriptionFromErrorCode(ret); - throw std::runtime_error(os.str()); + std::stringstream msg; + msg << "Unable to assign an image buffer of " << getSize() << " bytes: " << getDescriptionFromErrorCode(ret); + throw std::runtime_error(msg.str()); } } } From dcf650918683bbacdeadac42a17236426e9f89e0 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 3 Nov 2016 12:15:19 +0100 Subject: [PATCH 56/58] AudioFrame: fix spelling in the doc of getDefaultNbSamples method --- src/AvTranscoder/data/decoded/AudioFrame.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 23d338e2..385adea0 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -72,7 +72,7 @@ class AvExport AudioFrame : public IFrame * @brief The number of samples of a frame cannot be known before calling avcodec_decode_audio4, * and can be variable in a same stream. Because we need to allocate some frames without knowing this parameter, * we access here a default number of samples. - * @note This value depends on the sample rate (excample: 1920 samples at 48kHz). + * @note This value depends on the sample rate (example: 1920 samples at 48kHz). * @return the number of samples of our default AudioFrame. * @see setNbSamplesPerChannel */ From 1cea0441b3770dfaf6d3cc545fffab6ee3d57a31 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 3 Nov 2016 12:19:17 +0100 Subject: [PATCH 57/58] IFrame: rename getSize to getDataSize --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 2 +- src/AvTranscoder/data/decoded/AudioFrame.hpp | 2 +- src/AvTranscoder/data/decoded/IFrame.cpp | 2 +- src/AvTranscoder/data/decoded/IFrame.hpp | 2 +- src/AvTranscoder/data/decoded/VideoFrame.cpp | 4 ++-- src/AvTranscoder/data/decoded/VideoFrame.hpp | 2 +- src/AvTranscoder/transform/AudioTransform.cpp | 2 +- src/AvTranscoder/transform/VideoTransform.cpp | 2 +- test/pyTest/testAudioFrame.py | 4 ++-- test/pyTest/testAudioReader.py | 10 +++++----- test/pyTest/testVideoFrame.py | 4 ++-- test/pyTest/testVideoReader.py | 8 ++++---- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index b1f1f2d9..bf0ed9da 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -75,7 +75,7 @@ size_t AudioFrame::getBytesPerSample() const return av_get_bytes_per_sample(getSampleFormat()); } -size_t AudioFrame::getSize() const +size_t AudioFrame::getDataSize() const { if(getSampleFormat() == AV_SAMPLE_FMT_NONE) { diff --git a/src/AvTranscoder/data/decoded/AudioFrame.hpp b/src/AvTranscoder/data/decoded/AudioFrame.hpp index 385adea0..6bf00175 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.hpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.hpp @@ -49,7 +49,7 @@ class AvExport AudioFrame : public IFrame */ void allocateData(); void freeData(); - size_t getSize() const; + size_t getDataSize() const; size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); } size_t getNbChannels() const { return av_frame_get_channels(_frame); } diff --git a/src/AvTranscoder/data/decoded/IFrame.cpp b/src/AvTranscoder/data/decoded/IFrame.cpp index 5fe4be19..608f90e4 100644 --- a/src/AvTranscoder/data/decoded/IFrame.cpp +++ b/src/AvTranscoder/data/decoded/IFrame.cpp @@ -68,7 +68,7 @@ void IFrame::assignValue(const unsigned char value) freeData(); // Create the buffer - const int bufferSize = getSize(); + const int bufferSize = getDataSize(); unsigned char* dataBuffer = static_cast(malloc(bufferSize * sizeof(unsigned char))); memset(dataBuffer, value, bufferSize); diff --git a/src/AvTranscoder/data/decoded/IFrame.hpp b/src/AvTranscoder/data/decoded/IFrame.hpp index 50e33cfa..95394974 100644 --- a/src/AvTranscoder/data/decoded/IFrame.hpp +++ b/src/AvTranscoder/data/decoded/IFrame.hpp @@ -47,7 +47,7 @@ class AvExport IFrame * @brief Get the size in bytes that a video/audio buffer of the given frame properties would occupy if allocated. * @warning This methods does not guarantee that the buffer is actually allocated. */ - virtual size_t getSize() const = 0; + virtual size_t getDataSize() const = 0; /** * @brief Assign the given ptr of data to the data of the frame. diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 3c915227..1467c3fe 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -67,7 +67,7 @@ VideoFrame::~VideoFrame() freeData(); } -size_t VideoFrame::getSize() const +size_t VideoFrame::getDataSize() const { if(getPixelFormat() == AV_PIX_FMT_NONE) { @@ -117,7 +117,7 @@ void VideoFrame::assignBuffer(const unsigned char* ptrValue) if(ret < 0) { std::stringstream msg; - msg << "Unable to assign an image buffer of " << getSize() << " bytes: " << getDescriptionFromErrorCode(ret); + msg << "Unable to assign an image buffer of " << getDataSize() << " bytes: " << getDescriptionFromErrorCode(ret); throw std::runtime_error(msg.str()); } } diff --git a/src/AvTranscoder/data/decoded/VideoFrame.hpp b/src/AvTranscoder/data/decoded/VideoFrame.hpp index 4b93133a..0339e03b 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.hpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.hpp @@ -57,7 +57,7 @@ class AvExport VideoFrame : public IFrame */ void allocateData(); void freeData(); - size_t getSize() const; + size_t getDataSize() const; size_t getWidth() const { return _frame->width; } size_t getHeight() const { return _frame->height; } diff --git a/src/AvTranscoder/transform/AudioTransform.cpp b/src/AvTranscoder/transform/AudioTransform.cpp index 22dde253..96ffacf4 100644 --- a/src/AvTranscoder/transform/AudioTransform.cpp +++ b/src/AvTranscoder/transform/AudioTransform.cpp @@ -97,7 +97,7 @@ void AudioTransform::convert(const IFrame& srcFrame, IFrame& dstFrame) assert(src.getNbChannels() > 0); assert(src.getNbSamplesPerChannel() > 0); assert(src.getSampleFormat() != AV_SAMPLE_FMT_NONE); - assert(dst.getSize() > 0); + assert(dst.getDataSize() > 0); if(!_isInit) _isInit = init(src, dst); diff --git a/src/AvTranscoder/transform/VideoTransform.cpp b/src/AvTranscoder/transform/VideoTransform.cpp index bceb3d6b..2ea8eb3d 100644 --- a/src/AvTranscoder/transform/VideoTransform.cpp +++ b/src/AvTranscoder/transform/VideoTransform.cpp @@ -68,7 +68,7 @@ void VideoTransform::convert(const IFrame& srcFrame, IFrame& dstFrame) assert(src.getWidth() > 0); assert(src.getHeight() > 0); assert(src.getPixelFormat() != AV_PIX_FMT_NONE); - assert(dst.getSize() > 0); + assert(dst.getDataSize() > 0); if(!_isInit) _isInit = init(src, dst); diff --git a/test/pyTest/testAudioFrame.py b/test/pyTest/testAudioFrame.py index 1f5606a2..6028c5f3 100644 --- a/test/pyTest/testAudioFrame.py +++ b/test/pyTest/testAudioFrame.py @@ -24,7 +24,7 @@ def testInvalidAudioFrameManualAllocated(): frame = av.AudioFrame(desc, False) assert_equals(frame.isDataAllocated(), False) - assert_equals(frame.getSize(), 0) + assert_equals(frame.getDataSize(), 0) assert_equals(frame.getSampleRate(), sampleRate) assert_equals(frame.getNbChannels(), nbChannels) assert_equals(frame.getChannelLayoutDesc(), "mono") @@ -46,7 +46,7 @@ def testAudioFrame(): assert_equals(frame.isDataAllocated(), True) assert_equals(frame.isAudioFrame(), True) - assert_equals(frame.getSize(), frame.getNbSamplesPerChannel() * frame.getBytesPerSample() * nbChannels) + assert_equals(frame.getDataSize(), frame.getNbSamplesPerChannel() * frame.getBytesPerSample() * nbChannels) frame.freeData() assert_equals(frame.isDataAllocated(), False) diff --git a/test/pyTest/testAudioReader.py b/test/pyTest/testAudioReader.py index 1579b053..30a9b080 100644 --- a/test/pyTest/testAudioReader.py +++ b/test/pyTest/testAudioReader.py @@ -23,7 +23,7 @@ def testAudioReaderCreateNewInputFile(): frame = reader.readNextFrame() if not frame: break - assert_greater(frame.getSize(), 0) + assert_greater(frame.getDataSize(), 0) # check if there is no next frame frame = reader.readNextFrame() @@ -45,13 +45,13 @@ def testAudioReaderChannelsExtraction(): nbChannels = readerOfAllChannels.getOutputNbChannels() # read first frame frame = readerOfAllChannels.readNextFrame() - sizeOfFrameWithAllChannels = frame.getSize() + sizeOfFrameWithAllChannels = frame.getDataSize() # create reader to read one channel of the audio stream readerOfOneChannel = av.AudioReader(inputFile, streamIndex, channelIndex) # read first frame frame = readerOfOneChannel.readNextFrame() - sizeOfFrameWithOneChannels = frame.getSize() + sizeOfFrameWithOneChannels = frame.getDataSize() assert_equals( sizeOfFrameWithAllChannels / nbChannels, sizeOfFrameWithOneChannels ) @@ -70,7 +70,7 @@ def testAudioReaderWithGenerator(): frame = reader.readNextFrame() if not frame: break - assert_greater(frame.getSize(), 0) + assert_greater(frame.getDataSize(), 0) # check if there is no next frame assert_equals( reader.readNextFrame(), None ) @@ -82,4 +82,4 @@ def testAudioReaderWithGenerator(): # assuming we generate data of 1920 samples of 2 bytes nbSamplesPerChannel = 1920 bytesPerSample = 2 - assert_equals(frame.getSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) + assert_equals(frame.getDataSize(), reader.getOutputNbChannels() * nbSamplesPerChannel * bytesPerSample ) diff --git a/test/pyTest/testVideoFrame.py b/test/pyTest/testVideoFrame.py index 7b165f1c..90e6edea 100644 --- a/test/pyTest/testVideoFrame.py +++ b/test/pyTest/testVideoFrame.py @@ -24,7 +24,7 @@ def testInvalidVideoFrameManualAllocated(): frame = av.VideoFrame(desc, False) assert_equals(frame.isDataAllocated(), False) - assert_equals(frame.getSize(), 0) + assert_equals(frame.getDataSize(), 0) assert_equals(frame.getWidth(), width) assert_equals(frame.getHeight(), height) assert_equals(av.getPixelFormatName(frame.getPixelFormat()), "") @@ -43,7 +43,7 @@ def testVideoFrame(): assert_equals(frame.isDataAllocated(), True) assert_equals(frame.isVideoFrame(), True) - assert_equals(frame.getSize(), width * height * 1.5) + assert_equals(frame.getDataSize(), width * height * 1.5) frame.freeData() assert_equals(frame.isDataAllocated(), False) diff --git a/test/pyTest/testVideoReader.py b/test/pyTest/testVideoReader.py index 46686e78..b4e782a7 100644 --- a/test/pyTest/testVideoReader.py +++ b/test/pyTest/testVideoReader.py @@ -22,7 +22,7 @@ def testVideoReaderCreateNewInputFile(): for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 - assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) + assert_equals( frame.getDataSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) # check if there is no next frame frame = reader.readNextFrame() @@ -42,7 +42,7 @@ def testVideoReaderReferenceInputFile(): for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 - assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) + assert_equals( frame.getDataSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) # check if there is no next frame assert_equals( reader.readNextFrame(), None ) @@ -60,7 +60,7 @@ def testVideoReaderWithGenerator(): for i in xrange(0, reader.getSourceVideoProperties().getNbFrames()): frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 - assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) + assert_equals( frame.getDataSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) # check if there is no next frame assert_equals( reader.readNextFrame(), None ) @@ -70,4 +70,4 @@ def testVideoReaderWithGenerator(): for i in xrange(0, 9): frame = reader.readNextFrame() bytesPerPixel = reader.getOutputBitDepth() / 8 - assert_equals( frame.getSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) + assert_equals( frame.getDataSize(), reader.getOutputWidth() * reader.getOutputHeight() * bytesPerPixel ) From cd2db7b94a29da90dbdca03fe3cf268482821ffe Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Thu, 3 Nov 2016 13:56:52 +0100 Subject: [PATCH 58/58] frames: add log to warn about possible data already allocated --- src/AvTranscoder/data/decoded/AudioFrame.cpp | 3 +++ src/AvTranscoder/data/decoded/VideoFrame.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/AvTranscoder/data/decoded/AudioFrame.cpp b/src/AvTranscoder/data/decoded/AudioFrame.cpp index bf0ed9da..8dfd3bd8 100644 --- a/src/AvTranscoder/data/decoded/AudioFrame.cpp +++ b/src/AvTranscoder/data/decoded/AudioFrame.cpp @@ -98,6 +98,9 @@ size_t AudioFrame::getDataSize() const void AudioFrame::allocateData() { + if(_dataAllocated) + LOG_WARN("The AudioFrame seems to already have allocated data. This could lead to memory leaks.") + // Set Frame properties av_frame_set_sample_rate(_frame, _desc._sampleRate); av_frame_set_channels(_frame, _desc._nbChannels); diff --git a/src/AvTranscoder/data/decoded/VideoFrame.cpp b/src/AvTranscoder/data/decoded/VideoFrame.cpp index 1467c3fe..4da08b00 100644 --- a/src/AvTranscoder/data/decoded/VideoFrame.cpp +++ b/src/AvTranscoder/data/decoded/VideoFrame.cpp @@ -83,6 +83,9 @@ size_t VideoFrame::getDataSize() const void VideoFrame::allocateData() { + if(_dataAllocated) + LOG_WARN("The VideoFrame seems to already have allocated data. This could lead to memory leaks.") + // Set Frame properties _frame->width = _desc._width; _frame->height = _desc._height;