diff --git a/src/AvTranscoder/file/InputFile.cpp b/src/AvTranscoder/file/InputFile.cpp index 30e9efd5..41eb8ddb 100644 --- a/src/AvTranscoder/file/InputFile.cpp +++ b/src/AvTranscoder/file/InputFile.cpp @@ -30,7 +30,7 @@ InputFile::InputFile(const std::string& filename) _formatContext.findStreamInfo(); // Get the stream information as properties - _properties = new FileProperties(_formatContext); + _properties = new FileProperties(*this); // Create streams for(size_t streamIndex = 0; streamIndex < _formatContext.getNbStreams(); ++streamIndex) diff --git a/src/AvTranscoder/file/InputFile.hpp b/src/AvTranscoder/file/InputFile.hpp index 2440edbc..60423485 100644 --- a/src/AvTranscoder/file/InputFile.hpp +++ b/src/AvTranscoder/file/InputFile.hpp @@ -79,7 +79,7 @@ class AvExport InputFile std::string getFilename() const { return _filename; } - FormatContext& getFormatContext() { return _formatContext; } + const FormatContext& getFormatContext() const { return _formatContext; } /** * @brief Set the format of the input file diff --git a/src/AvTranscoder/properties/FileProperties.cpp b/src/AvTranscoder/properties/FileProperties.cpp index 8d78d23c..15b03d61 100644 --- a/src/AvTranscoder/properties/FileProperties.cpp +++ b/src/AvTranscoder/properties/FileProperties.cpp @@ -11,9 +11,10 @@ namespace avtranscoder { -FileProperties::FileProperties(const FormatContext& formatContext) - : _formatContext(&formatContext) - , _avFormatContext(&formatContext.getAVFormatContext()) +FileProperties::FileProperties(const InputFile& file) + : _file(file) + , _formatContext(&file.getFormatContext()) + , _avFormatContext(&file.getFormatContext().getAVFormatContext()) , _videoStreams() , _audioStreams() , _dataStreams() @@ -31,8 +32,8 @@ FileProperties::FileProperties(const FormatContext& formatContext) void FileProperties::extractStreamProperties(IProgress& progress, const EAnalyseLevel level) { // Returns at the beginning of the stream before any deep analysis - if(level > eAnalyseLevelHeader && !isRawFormat()) - const_cast(_formatContext)->seek(0, AVSEEK_FLAG_BACKWARD); + if(level > eAnalyseLevelHeader && ! isRawFormat()) + const_cast(_file).seekAtFrame(0, AVSEEK_FLAG_BACKWARD); // clear properties clearStreamProperties(); @@ -123,8 +124,8 @@ void FileProperties::extractStreamProperties(IProgress& progress, const EAnalyse } // Returns at the beginning of the stream after any deep analysis - if(level > eAnalyseLevelHeader && !isRawFormat()) - const_cast(_formatContext)->seek(0, AVSEEK_FLAG_BACKWARD); + if(level > eAnalyseLevelHeader && ! isRawFormat()) + const_cast(_file).seekAtFrame(0, AVSEEK_FLAG_BACKWARD); } std::string FileProperties::getFilename() const diff --git a/src/AvTranscoder/properties/FileProperties.hpp b/src/AvTranscoder/properties/FileProperties.hpp index f873e3f8..d926339f 100644 --- a/src/AvTranscoder/properties/FileProperties.hpp +++ b/src/AvTranscoder/properties/FileProperties.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include @@ -29,7 +29,7 @@ class AvExport FileProperties * @note The default streams analyse level is eAnalyseLevelHeader * @see FormatContext */ - FileProperties(const FormatContext& formatContext); + FileProperties(const InputFile& file); /** * @brief Relaunch streams analysis with a specific level. @@ -62,7 +62,7 @@ class AvExport FileProperties size_t getNbAttachementStreams() const { return _attachementStreams.size(); } size_t getNbUnknownStreams() const { return _unknownStreams.size(); } - const FormatContext& getFormatContext() const { return *_formatContext; } + const InputFile& getInputFile() const { return _file; } //@{ // @brief Get the properties at the indicated stream index @@ -110,6 +110,7 @@ class AvExport FileProperties void clearStreamProperties(); ///< Clear all array of stream properties private: + const InputFile& _file; ///< Has link (no ownership) const FormatContext* _formatContext; ///< Has link (no ownership) const AVFormatContext* _avFormatContext; ///< Has link (no ownership) diff --git a/src/AvTranscoder/properties/VideoProperties.cpp b/src/AvTranscoder/properties/VideoProperties.cpp index a3f83a19..9d8e2378 100644 --- a/src/AvTranscoder/properties/VideoProperties.cpp +++ b/src/AvTranscoder/properties/VideoProperties.cpp @@ -1,9 +1,11 @@ #include "VideoProperties.hpp" +#include +#include +#include #include #include #include -#include extern "C" { #include @@ -328,78 +330,20 @@ size_t VideoProperties::getBitRate() const if(_codecContext->bit_rate || _codecContext->rc_max_rate) return _codecContext->bit_rate; - LOG_WARN("The bitrate of the stream '" << _streamIndex << "' of file '" << _formatContext->filename << "' is unknown.") - LOG_INFO("Compute the video bitrate by decoding the first GOP.") - - if(!_codecContext->width || !_codecContext->height) - throw std::runtime_error("cannot compute bit rate: invalid frame size"); - - if(!_formatContext || !_codec) - throw std::runtime_error("cannot compute bit rate: unknown format or codec"); - if(!_codecContext->width || !_codecContext->height) - throw std::runtime_error("cannot compute bit rate: invalid frame size"); - - // discard no frame type when decode - _codecContext->skip_frame = AVDISCARD_NONE; - - AVPacket pkt; - av_init_packet(&pkt); - - avcodec_open2(_codecContext, _codec, NULL); - - VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false); - AVFrame& avFrame = frame.getAVFrame(); - - int gotFrame = 0; - size_t nbDecodedFrames = 0; - int gopFramesSize = 0; - int positionOfFirstKeyFrame = -1; - int positionOfLastKeyFrame = -1; - - while(!av_read_frame(const_cast(_formatContext), &pkt)) + if(_levelAnalysis == eAnalyseLevelHeader) { - if(pkt.stream_index == (int)_streamIndex) - { - avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt); - if(gotFrame) - { - // check distance between key frames - if(avFrame.pict_type == AV_PICTURE_TYPE_I) - { - if(positionOfFirstKeyFrame == -1) - positionOfFirstKeyFrame = nbDecodedFrames; - else - positionOfLastKeyFrame = nbDecodedFrames; - } - ++nbDecodedFrames; - - // added size of all frames of the same gop - if(positionOfLastKeyFrame == -1) - { -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 7, 100) - gopFramesSize += av_frame_get_pkt_size(&avFrame); -#else - gopFramesSize += pkt.size; -#endif - } - } - } - av_free_packet(&pkt); - if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1) - break; + LOG_WARN("The bitrate of the stream '" << _streamIndex << "' of file '" << _formatContext->filename << "' is unknown. " + "Need a deeper analysis: see eAnalyseLevelFirstGop.") + return 0; } - // Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself) - avcodec_close(_codecContext); - // Returns at the beginning of the stream - const_cast(&_fileProperties->getFormatContext())->seek(0, AVSEEK_FLAG_BYTE); - const size_t gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame; - if(gopSize > 0) + LOG_INFO("Estimate the video bitrate from the first GOP.") + size_t gopFramesSize = 0; + for(size_t picture = 0; picture < _gopStructure.size(); ++picture) { - const float fps = av_q2d(_formatContext->streams[_streamIndex]->avg_frame_rate); - return (gopFramesSize / gopSize) * 8 * fps; + gopFramesSize += _gopStructure.at(picture).second; } - return 0; + return (gopFramesSize / getGopSize()) * 8 * getFps(); } size_t VideoProperties::getMaxBitRate() const @@ -556,69 +500,59 @@ std::vector > VideoProperties::getGopStructure() const void VideoProperties::analyseGopStructure(IProgress& progress) { - if(_formatContext && _codecContext && _codec) + if(! _formatContext || ! _codecContext || ! _codec) + return; + if(! _codecContext->width || ! _codecContext->height) + return; + + InputFile& file = const_cast(_fileProperties->getInputFile()); + // Get the stream + IInputStream& stream = file.getStream(_streamIndex); + stream.activate(); + // Create a decoder + VideoDecoder decoder(static_cast(stream)); + + size_t count = 0; + int positionOfFirstKeyFrame = -1; + int positionOfLastKeyFrame = -1; + VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelFormatName(getPixelProperties().getAVPixelFormat())), false); + while(decoder.decodeNextFrame(frame)) { - if(_codecContext->width && _codecContext->height) + AVFrame& avFrame = frame.getAVFrame(); + + _gopStructure.push_back( + 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) { - // Discard no frame type when decode - _codecContext->skip_frame = AVDISCARD_NONE; - - AVPacket pkt; - av_init_packet(&pkt); - - // Initialize the AVCodecContext to use the given AVCodec - avcodec_open2(_codecContext, _codec, NULL); - - VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false); - AVFrame& avFrame = frame.getAVFrame(); - - size_t count = 0; - int gotFrame = 0; - int positionOfFirstKeyFrame = -1; - int positionOfLastKeyFrame = -1; - - while(!av_read_frame(const_cast(_formatContext), &pkt)) - { - if(pkt.stream_index == (int)_streamIndex) - { - avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt); - if(gotFrame) - { - _gopStructure.push_back( - 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) - { - if(positionOfFirstKeyFrame == -1) - positionOfFirstKeyFrame = count; - else - positionOfLastKeyFrame = count; - } - - _gopSize = ++count; - } - } - av_free_packet(&pkt); - - // If the first 2 key frames are found - if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1) - { - // Set gop size as distance between these 2 key frames - _gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame; - // Update gop structure to keep only one gop - while(_gopStructure.size() > _gopSize) - _gopStructure.pop_back(); - break; - } - } - - // Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself) - avcodec_close(_codecContext); - - // Returns at the beginning of the stream - const_cast(&_fileProperties->getFormatContext())->seek(0, AVSEEK_FLAG_BYTE); + if(positionOfFirstKeyFrame == -1) + positionOfFirstKeyFrame = count; + else + positionOfLastKeyFrame = count; } + + _gopSize = ++count; + + // If the first 2 key frames are found + if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1) + { + // Set gop size as distance between these 2 key frames + _gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame; + // Update gop structure to keep only one gop + while(_gopStructure.size() > _gopSize) + _gopStructure.pop_back(); + break; + } + } + + // Returns at the beginning of the stream + file.seekAtFrame(0, AVSEEK_FLAG_BYTE); + + // Check GOP size + if(_gopSize <= 0) + { + throw std::runtime_error("Invalid GOP size when decoding the first data."); } } diff --git a/src/AvTranscoder/properties/VideoProperties.hpp b/src/AvTranscoder/properties/VideoProperties.hpp index ed9d59ec..a5a2c9a6 100644 --- a/src/AvTranscoder/properties/VideoProperties.hpp +++ b/src/AvTranscoder/properties/VideoProperties.hpp @@ -42,7 +42,9 @@ class AvExport VideoProperties : public StreamProperties /** * @return The video bitrate in bits/s. - * @warning If there is no such info available in the container, this data is estimated by decoding the first GOP. + * @note 0 if unknown. + * @warning If there is no such info available in the container, this data is estimated using the information of the first GOP. + * @see eAnalyseLevelFirstGop */ size_t getBitRate() const; size_t getMaxBitRate() const; diff --git a/test/pyTest/testProperties.py b/test/pyTest/testProperties.py index 3e29c8b5..91c77584 100644 --- a/test/pyTest/testProperties.py +++ b/test/pyTest/testProperties.py @@ -114,25 +114,26 @@ def testCheckRawVideoProperties(): inputFile = av.InputFile(inputFileName) properties = inputFile.getProperties() + # Check format assert_true(properties.isRawFormat()) assert_equals(properties.getNbStreams(), 1) assert_equals(properties.getNbVideoStreams(), 1) assert_equals(properties.getDuration(), 0) # file duration is unknown assert_equals(properties.getBitRate(), 0) # file bitrate is unknown + assert_equals(properties.getFileSize(), 256293L) - expectedFileSize = 256293L - assert_equals(properties.getFileSize(), expectedFileSize) - - expectedBitRate = 177200L - expectedNbFrames = 200 - expectedDuration = 8 - expectedFps = 25 - + # Check video stream when analyse the header + videoStream = properties.getVideoProperties()[0] + assert_equals(videoStream.getFps(), 25) + assert_equals(videoStream.getNbFrames(), 0) # stream nbFrames is unknown + assert_equals(videoStream.getDuration(), 0) # stream duration is unknown + assert_equals(videoStream.getBitRate(), 0) # stream bitrate is unknown + # Check video stream when analyse the first GOP + inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop) videoStream = properties.getVideoProperties()[0] - assert_equals(videoStream.getNbFrames(), expectedNbFrames) - assert_equals(videoStream.getDuration(), expectedDuration) - assert_equals(videoStream.getBitRate(), expectedBitRate) - assert_equals(videoStream.getFps(), expectedFps) + assert_equals(videoStream.getNbFrames(), 200) + assert_equals(videoStream.getDuration(), 8) + assert_equals(videoStream.getBitRate(), 177200L) def testCheckAudioProperties(): diff --git a/test/pyTest/testTranscoderRewrap.py b/test/pyTest/testTranscoderRewrap.py index cd4ae14a..aee5c50e 100644 --- a/test/pyTest/testTranscoderRewrap.py +++ b/test/pyTest/testTranscoderRewrap.py @@ -164,6 +164,7 @@ def testRewrapRawVideoStream(): # get src file of wrap inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_RAW_FILE'] src_inputFile = av.InputFile(inputFileName) + src_inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop) src_properties = src_inputFile.getProperties() src_videoStream = src_properties.getVideoProperties()[0] @@ -180,6 +181,7 @@ def testRewrapRawVideoStream(): # get dst file of wrap dst_inputFile = av.InputFile(outputFileName) + dst_inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop) dst_properties = dst_inputFile.getProperties() dst_videoStream = dst_properties.getVideoProperties()[0] pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy