Skip to content

Commit 3d0dc4a

Browse files
authored
Merge pull request #284 from cchampet/refactor_VideoPropertiesAnalyseFirstGOP
VideoProperties: improve how to analyse the first GOP
2 parents bcc9741 + 2ccf4b6 commit 3d0dc4a

File tree

8 files changed

+94
-153
lines changed

8 files changed

+94
-153
lines changed

src/AvTranscoder/file/InputFile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ InputFile::InputFile(const std::string& filename)
3030
_formatContext.findStreamInfo();
3131

3232
// Get the stream information as properties
33-
_properties = new FileProperties(_formatContext);
33+
_properties = new FileProperties(*this);
3434

3535
// Create streams
3636
for(size_t streamIndex = 0; streamIndex < _formatContext.getNbStreams(); ++streamIndex)

src/AvTranscoder/file/InputFile.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class AvExport InputFile
7979

8080
std::string getFilename() const { return _filename; }
8181

82-
FormatContext& getFormatContext() { return _formatContext; }
82+
const FormatContext& getFormatContext() const { return _formatContext; }
8383

8484
/**
8585
* @brief Set the format of the input file

src/AvTranscoder/properties/FileProperties.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
namespace avtranscoder
1212
{
1313

14-
FileProperties::FileProperties(const FormatContext& formatContext)
15-
: _formatContext(&formatContext)
16-
, _avFormatContext(&formatContext.getAVFormatContext())
14+
FileProperties::FileProperties(const InputFile& file)
15+
: _file(file)
16+
, _formatContext(&file.getFormatContext())
17+
, _avFormatContext(&file.getFormatContext().getAVFormatContext())
1718
, _videoStreams()
1819
, _audioStreams()
1920
, _dataStreams()
@@ -31,8 +32,8 @@ FileProperties::FileProperties(const FormatContext& formatContext)
3132
void FileProperties::extractStreamProperties(IProgress& progress, const EAnalyseLevel level)
3233
{
3334
// Returns at the beginning of the stream before any deep analysis
34-
if(level > eAnalyseLevelHeader && !isRawFormat())
35-
const_cast<FormatContext*>(_formatContext)->seek(0, AVSEEK_FLAG_BACKWARD);
35+
if(level > eAnalyseLevelHeader && ! isRawFormat())
36+
const_cast<InputFile&>(_file).seekAtFrame(0, AVSEEK_FLAG_BACKWARD);
3637

3738
// clear properties
3839
clearStreamProperties();
@@ -123,8 +124,8 @@ void FileProperties::extractStreamProperties(IProgress& progress, const EAnalyse
123124
}
124125

125126
// Returns at the beginning of the stream after any deep analysis
126-
if(level > eAnalyseLevelHeader && !isRawFormat())
127-
const_cast<FormatContext*>(_formatContext)->seek(0, AVSEEK_FLAG_BACKWARD);
127+
if(level > eAnalyseLevelHeader && ! isRawFormat())
128+
const_cast<InputFile&>(_file).seekAtFrame(0, AVSEEK_FLAG_BACKWARD);
128129
}
129130

130131
std::string FileProperties::getFilename() const

src/AvTranscoder/properties/FileProperties.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include <AvTranscoder/common.hpp>
55
#include <AvTranscoder/properties/util.hpp>
6-
#include <AvTranscoder/file/FormatContext.hpp>
6+
#include <AvTranscoder/file/InputFile.hpp>
77
#include <AvTranscoder/progress/IProgress.hpp>
88

99
#include <AvTranscoder/properties/StreamProperties.hpp>
@@ -29,7 +29,7 @@ class AvExport FileProperties
2929
* @note The default streams analyse level is eAnalyseLevelHeader
3030
* @see FormatContext
3131
*/
32-
FileProperties(const FormatContext& formatContext);
32+
FileProperties(const InputFile& file);
3333

3434
/**
3535
* @brief Relaunch streams analysis with a specific level.
@@ -62,7 +62,7 @@ class AvExport FileProperties
6262
size_t getNbAttachementStreams() const { return _attachementStreams.size(); }
6363
size_t getNbUnknownStreams() const { return _unknownStreams.size(); }
6464

65-
const FormatContext& getFormatContext() const { return *_formatContext; }
65+
const InputFile& getInputFile() const { return _file; }
6666

6767
//@{
6868
// @brief Get the properties at the indicated stream index
@@ -110,6 +110,7 @@ class AvExport FileProperties
110110
void clearStreamProperties(); ///< Clear all array of stream properties
111111

112112
private:
113+
const InputFile& _file; ///< Has link (no ownership)
113114
const FormatContext* _formatContext; ///< Has link (no ownership)
114115
const AVFormatContext* _avFormatContext; ///< Has link (no ownership)
115116

src/AvTranscoder/properties/VideoProperties.cpp

Lines changed: 62 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#include "VideoProperties.hpp"
22

3+
#include <AvTranscoder/util.hpp>
4+
#include <AvTranscoder/decoder/VideoDecoder.hpp>
5+
#include <AvTranscoder/data/decoded/VideoFrame.hpp>
36
#include <AvTranscoder/properties/util.hpp>
47
#include <AvTranscoder/properties/FileProperties.hpp>
58
#include <AvTranscoder/progress/NoDisplayProgress.hpp>
6-
#include <AvTranscoder/data/decoded/VideoFrame.hpp>
79

810
extern "C" {
911
#include <libavutil/avutil.h>
@@ -328,78 +330,20 @@ size_t VideoProperties::getBitRate() const
328330
if(_codecContext->bit_rate || _codecContext->rc_max_rate)
329331
return _codecContext->bit_rate;
330332

331-
LOG_WARN("The bitrate of the stream '" << _streamIndex << "' of file '" << _formatContext->filename << "' is unknown.")
332-
LOG_INFO("Compute the video bitrate by decoding the first GOP.")
333-
334-
if(!_codecContext->width || !_codecContext->height)
335-
throw std::runtime_error("cannot compute bit rate: invalid frame size");
336-
337-
if(!_formatContext || !_codec)
338-
throw std::runtime_error("cannot compute bit rate: unknown format or codec");
339-
if(!_codecContext->width || !_codecContext->height)
340-
throw std::runtime_error("cannot compute bit rate: invalid frame size");
341-
342-
// discard no frame type when decode
343-
_codecContext->skip_frame = AVDISCARD_NONE;
344-
345-
AVPacket pkt;
346-
av_init_packet(&pkt);
347-
348-
avcodec_open2(_codecContext, _codec, NULL);
349-
350-
VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false);
351-
AVFrame& avFrame = frame.getAVFrame();
352-
353-
int gotFrame = 0;
354-
size_t nbDecodedFrames = 0;
355-
int gopFramesSize = 0;
356-
int positionOfFirstKeyFrame = -1;
357-
int positionOfLastKeyFrame = -1;
358-
359-
while(!av_read_frame(const_cast<AVFormatContext*>(_formatContext), &pkt))
333+
if(_levelAnalysis == eAnalyseLevelHeader)
360334
{
361-
if(pkt.stream_index == (int)_streamIndex)
362-
{
363-
avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt);
364-
if(gotFrame)
365-
{
366-
// check distance between key frames
367-
if(avFrame.pict_type == AV_PICTURE_TYPE_I)
368-
{
369-
if(positionOfFirstKeyFrame == -1)
370-
positionOfFirstKeyFrame = nbDecodedFrames;
371-
else
372-
positionOfLastKeyFrame = nbDecodedFrames;
373-
}
374-
++nbDecodedFrames;
375-
376-
// added size of all frames of the same gop
377-
if(positionOfLastKeyFrame == -1)
378-
{
379-
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 7, 100)
380-
gopFramesSize += av_frame_get_pkt_size(&avFrame);
381-
#else
382-
gopFramesSize += pkt.size;
383-
#endif
384-
}
385-
}
386-
}
387-
av_free_packet(&pkt);
388-
if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1)
389-
break;
335+
LOG_WARN("The bitrate of the stream '" << _streamIndex << "' of file '" << _formatContext->filename << "' is unknown. "
336+
"Need a deeper analysis: see eAnalyseLevelFirstGop.")
337+
return 0;
390338
}
391-
// Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself)
392-
avcodec_close(_codecContext);
393-
// Returns at the beginning of the stream
394-
const_cast<FormatContext*>(&_fileProperties->getFormatContext())->seek(0, AVSEEK_FLAG_BYTE);
395339

396-
const size_t gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame;
397-
if(gopSize > 0)
340+
LOG_INFO("Estimate the video bitrate from the first GOP.")
341+
size_t gopFramesSize = 0;
342+
for(size_t picture = 0; picture < _gopStructure.size(); ++picture)
398343
{
399-
const float fps = av_q2d(_formatContext->streams[_streamIndex]->avg_frame_rate);
400-
return (gopFramesSize / gopSize) * 8 * fps;
344+
gopFramesSize += _gopStructure.at(picture).second;
401345
}
402-
return 0;
346+
return (gopFramesSize / getGopSize()) * 8 * getFps();
403347
}
404348

405349
size_t VideoProperties::getMaxBitRate() const
@@ -556,69 +500,59 @@ std::vector<std::pair<char, int> > VideoProperties::getGopStructure() const
556500

557501
void VideoProperties::analyseGopStructure(IProgress& progress)
558502
{
559-
if(_formatContext && _codecContext && _codec)
503+
if(! _formatContext || ! _codecContext || ! _codec)
504+
return;
505+
if(! _codecContext->width || ! _codecContext->height)
506+
return;
507+
508+
InputFile& file = const_cast<InputFile&>(_fileProperties->getInputFile());
509+
// Get the stream
510+
IInputStream& stream = file.getStream(_streamIndex);
511+
stream.activate();
512+
// Create a decoder
513+
VideoDecoder decoder(static_cast<InputStream&>(stream));
514+
515+
size_t count = 0;
516+
int positionOfFirstKeyFrame = -1;
517+
int positionOfLastKeyFrame = -1;
518+
VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelFormatName(getPixelProperties().getAVPixelFormat())), false);
519+
while(decoder.decodeNextFrame(frame))
560520
{
561-
if(_codecContext->width && _codecContext->height)
521+
AVFrame& avFrame = frame.getAVFrame();
522+
523+
_gopStructure.push_back(
524+
std::make_pair(av_get_picture_type_char(avFrame.pict_type), av_frame_get_pkt_size(&avFrame)));
525+
_isInterlaced = avFrame.interlaced_frame;
526+
_isTopFieldFirst = avFrame.top_field_first;
527+
if(avFrame.pict_type == AV_PICTURE_TYPE_I)
562528
{
563-
// Discard no frame type when decode
564-
_codecContext->skip_frame = AVDISCARD_NONE;
565-
566-
AVPacket pkt;
567-
av_init_packet(&pkt);
568-
569-
// Initialize the AVCodecContext to use the given AVCodec
570-
avcodec_open2(_codecContext, _codec, NULL);
571-
572-
VideoFrame frame(VideoFrameDesc(getWidth(), getHeight(), getPixelProperties().getPixelFormatName()), false);
573-
AVFrame& avFrame = frame.getAVFrame();
574-
575-
size_t count = 0;
576-
int gotFrame = 0;
577-
int positionOfFirstKeyFrame = -1;
578-
int positionOfLastKeyFrame = -1;
579-
580-
while(!av_read_frame(const_cast<AVFormatContext*>(_formatContext), &pkt))
581-
{
582-
if(pkt.stream_index == (int)_streamIndex)
583-
{
584-
avcodec_decode_video2(_codecContext, &avFrame, &gotFrame, &pkt);
585-
if(gotFrame)
586-
{
587-
_gopStructure.push_back(
588-
std::make_pair(av_get_picture_type_char(avFrame.pict_type), av_frame_get_pkt_size(&avFrame)));
589-
_isInterlaced = avFrame.interlaced_frame;
590-
_isTopFieldFirst = avFrame.top_field_first;
591-
if(avFrame.pict_type == AV_PICTURE_TYPE_I)
592-
{
593-
if(positionOfFirstKeyFrame == -1)
594-
positionOfFirstKeyFrame = count;
595-
else
596-
positionOfLastKeyFrame = count;
597-
}
598-
599-
_gopSize = ++count;
600-
}
601-
}
602-
av_free_packet(&pkt);
603-
604-
// If the first 2 key frames are found
605-
if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1)
606-
{
607-
// Set gop size as distance between these 2 key frames
608-
_gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame;
609-
// Update gop structure to keep only one gop
610-
while(_gopStructure.size() > _gopSize)
611-
_gopStructure.pop_back();
612-
break;
613-
}
614-
}
615-
616-
// Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself)
617-
avcodec_close(_codecContext);
618-
619-
// Returns at the beginning of the stream
620-
const_cast<FormatContext*>(&_fileProperties->getFormatContext())->seek(0, AVSEEK_FLAG_BYTE);
529+
if(positionOfFirstKeyFrame == -1)
530+
positionOfFirstKeyFrame = count;
531+
else
532+
positionOfLastKeyFrame = count;
621533
}
534+
535+
_gopSize = ++count;
536+
537+
// If the first 2 key frames are found
538+
if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1)
539+
{
540+
// Set gop size as distance between these 2 key frames
541+
_gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame;
542+
// Update gop structure to keep only one gop
543+
while(_gopStructure.size() > _gopSize)
544+
_gopStructure.pop_back();
545+
break;
546+
}
547+
}
548+
549+
// Returns at the beginning of the stream
550+
file.seekAtFrame(0, AVSEEK_FLAG_BYTE);
551+
552+
// Check GOP size
553+
if(_gopSize <= 0)
554+
{
555+
throw std::runtime_error("Invalid GOP size when decoding the first data.");
622556
}
623557
}
624558

src/AvTranscoder/properties/VideoProperties.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ class AvExport VideoProperties : public StreamProperties
4242

4343
/**
4444
* @return The video bitrate in bits/s.
45-
* @warning If there is no such info available in the container, this data is estimated by decoding the first GOP.
45+
* @note 0 if unknown.
46+
* @warning If there is no such info available in the container, this data is estimated using the information of the first GOP.
47+
* @see eAnalyseLevelFirstGop
4648
*/
4749
size_t getBitRate() const;
4850
size_t getMaxBitRate() const;

test/pyTest/testProperties.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,25 +114,26 @@ def testCheckRawVideoProperties():
114114
inputFile = av.InputFile(inputFileName)
115115
properties = inputFile.getProperties()
116116

117+
# Check format
117118
assert_true(properties.isRawFormat())
118119
assert_equals(properties.getNbStreams(), 1)
119120
assert_equals(properties.getNbVideoStreams(), 1)
120121
assert_equals(properties.getDuration(), 0) # file duration is unknown
121122
assert_equals(properties.getBitRate(), 0) # file bitrate is unknown
123+
assert_equals(properties.getFileSize(), 256293L)
122124

123-
expectedFileSize = 256293L
124-
assert_equals(properties.getFileSize(), expectedFileSize)
125-
126-
expectedBitRate = 177200L
127-
expectedNbFrames = 200
128-
expectedDuration = 8
129-
expectedFps = 25
130-
125+
# Check video stream when analyse the header
126+
videoStream = properties.getVideoProperties()[0]
127+
assert_equals(videoStream.getFps(), 25)
128+
assert_equals(videoStream.getNbFrames(), 0) # stream nbFrames is unknown
129+
assert_equals(videoStream.getDuration(), 0) # stream duration is unknown
130+
assert_equals(videoStream.getBitRate(), 0) # stream bitrate is unknown
131+
# Check video stream when analyse the first GOP
132+
inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop)
131133
videoStream = properties.getVideoProperties()[0]
132-
assert_equals(videoStream.getNbFrames(), expectedNbFrames)
133-
assert_equals(videoStream.getDuration(), expectedDuration)
134-
assert_equals(videoStream.getBitRate(), expectedBitRate)
135-
assert_equals(videoStream.getFps(), expectedFps)
134+
assert_equals(videoStream.getNbFrames(), 200)
135+
assert_equals(videoStream.getDuration(), 8)
136+
assert_equals(videoStream.getBitRate(), 177200L)
136137

137138

138139
def testCheckAudioProperties():

test/pyTest/testTranscoderRewrap.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def testRewrapRawVideoStream():
164164
# get src file of wrap
165165
inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_RAW_FILE']
166166
src_inputFile = av.InputFile(inputFileName)
167+
src_inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop)
167168
src_properties = src_inputFile.getProperties()
168169
src_videoStream = src_properties.getVideoProperties()[0]
169170

@@ -180,6 +181,7 @@ def testRewrapRawVideoStream():
180181

181182
# get dst file of wrap
182183
dst_inputFile = av.InputFile(outputFileName)
184+
dst_inputFile.analyse(av.NoDisplayProgress(), av.eAnalyseLevelFirstGop)
183185
dst_properties = dst_inputFile.getProperties()
184186
dst_videoStream = dst_properties.getVideoProperties()[0]
185187

0 commit comments

Comments
 (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