AvTranscoder  0.9.4
C++APIforLibav/FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
VideoProperties.cpp
Go to the documentation of this file.
1 #include "VideoProperties.hpp"
2 
6 
7 extern "C" {
8 #include <libavutil/avutil.h>
9 }
10 
11 #include <stdexcept>
12 #include <iomanip>
13 #include <sstream>
14 #include <limits>
15 #include <cmath>
16 
17 namespace avtranscoder
18 {
19 
20 VideoProperties::VideoProperties(const FormatContext& formatContext, const size_t index, IProgress& progress,
21  const EAnalyseLevel level)
22  : StreamProperties(formatContext, index)
23  , _levelAnalysis(level)
24  , _pixelProperties()
25  , _isInterlaced(false)
26  , _isTopFieldFirst(false)
27  , _gopSize(0)
28  , _gopStructure()
29  , _firstGopTimeCode(-1)
30 {
31  if(_codecContext)
32  {
34  _firstGopTimeCode = _codecContext->timecode_frame_start;
35  }
36 
38  analyseGopStructure(progress);
39 }
40 
42 {
43  if(!_codecContext || !_codec)
44  throw std::runtime_error("unknown codec");
45 
46  if(_codec->capabilities & CODEC_CAP_TRUNCATED)
47  _codecContext->flags |= CODEC_FLAG_TRUNCATED;
48 
49  const char* profile = NULL;
50  if((profile = av_get_profile_name(_codec, getProfile())) == NULL)
51  throw std::runtime_error("unknown codec profile");
52 
53  return std::string(profile);
54 }
55 
57 {
58  if(!_codecContext)
59  throw std::runtime_error("unknown codec context");
60 
61  switch(_codecContext->color_trc)
62  {
63  case AVCOL_TRC_BT709:
64  return "Rec 709 / ITU-R BT1361";
65  case AVCOL_TRC_UNSPECIFIED:
66  return "unspecified";
67  case AVCOL_TRC_GAMMA22:
68  return "Gamma 2.2";
69  case AVCOL_TRC_GAMMA28:
70  return "Gamma 2.8";
71 #if LIBAVCODEC_VERSION_MAJOR > 53
72  case AVCOL_TRC_SMPTE240M:
73  return "Smpte 240M";
74 #endif
75 #if LIBAVCODEC_VERSION_MAJOR > 54
76 #ifdef AVCOL_TRC_SMPTE170M
77  case AVCOL_TRC_SMPTE170M:
78  return "Rec 601 / ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC";
79 #endif
80 #ifdef AVCOL_TRC_LINEAR
81  case AVCOL_TRC_LINEAR:
82  return "Linear transfer characteristics";
83 #endif
84 #ifdef AVCOL_TRC_LOG
85  case AVCOL_TRC_LOG:
86  return "Logarithmic transfer characteristic (100:1 range)";
87 #endif
88 #ifdef AVCOL_TRC_LOG_SQRT
89  case AVCOL_TRC_LOG_SQRT:
90  return "Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range)";
91 #endif
92 #ifdef AVCOL_TRC_IEC61966_2_4
93  case AVCOL_TRC_IEC61966_2_4:
94  return "IEC 61966-2-4";
95 #endif
96 #ifdef AVCOL_TRC_BT1361_ECG
97  case AVCOL_TRC_BT1361_ECG:
98  return "ITU-R BT1361 Extended Colour Gamut";
99 #endif
100 #ifdef AVCOL_TRC_IEC61966_2_1
101  case AVCOL_TRC_IEC61966_2_1:
102  return "IEC 61966-2-1 (sRGB or sYCC)";
103 #endif
104 #ifdef AVCOL_TRC_BT2020_10
105  case AVCOL_TRC_BT2020_10:
106  return "ITU-R BT2020 for 10 bit system";
107 #endif
108 #ifdef AVCOL_TRC_BT2020_12
109  case AVCOL_TRC_BT2020_12:
110  return "ITU-R BT2020 for 12 bit system";
111 #endif
112 #endif
113  case AVCOL_TRC_NB:
114  return "Not ABI";
115  default:
116  return "";
117  }
118 }
119 
121 {
122  if(!_codecContext)
123  throw std::runtime_error("unknown codec context");
124 
125  switch(_codecContext->colorspace)
126  {
127  case AVCOL_SPC_RGB:
128  return "RGB";
129  case AVCOL_SPC_BT709:
130  return "Rec 709";
131  case AVCOL_SPC_UNSPECIFIED:
132  return "unspecified";
133  case AVCOL_SPC_FCC:
134  return "Four CC";
135  case AVCOL_SPC_BT470BG:
136  return "BT470 (PAL - 625)";
137  case AVCOL_SPC_SMPTE170M:
138  return "Smpte 170M (NTSC)";
139  case AVCOL_SPC_SMPTE240M:
140  return "Smpte 240M";
141 #if LIBAVCODEC_VERSION_MAJOR > 53
142  case AVCOL_SPC_YCOCG:
143  return "Y Co Cg";
144 //#else
145 // case AVCOL_SPC_YCGCO:
146 // return "Y Cg Co";
147 #endif
148 #if LIBAVCODEC_VERSION_MAJOR > 54
149 #ifdef AVCOL_TRC_BT2020_12
150  case AVCOL_SPC_BT2020_NCL:
151  return "ITU-R BT2020 non-constant luminance system";
152 #endif
153 #ifdef AVCOL_TRC_BT2020_CL
154  case AVCOL_SPC_BT2020_CL:
155  return "ITU-R BT2020 constant luminance system";
156 #endif
157 #endif
158  case AVCOL_SPC_NB:
159  return "Not ABI";
160  default:
161  return "";
162  }
163 }
164 
166 {
167  if(!_codecContext)
168  throw std::runtime_error("unknown codec context");
169 
170  switch(_codecContext->color_range)
171  {
172  case AVCOL_RANGE_UNSPECIFIED:
173  return "unspecified";
174  case AVCOL_RANGE_MPEG:
175  return "Head";
176  case AVCOL_RANGE_JPEG:
177  return "Full";
178  case AVCOL_RANGE_NB:
179  return "Not ABI";
180  default:
181  return "";
182  }
183 }
184 
186 {
187  if(!_codecContext)
188  throw std::runtime_error("unknown codec context");
189 
190  switch(_codecContext->color_primaries)
191  {
192  case AVCOL_PRI_BT709:
193  return "Rec 709";
194  case AVCOL_PRI_UNSPECIFIED:
195  return "unspecified";
196  case AVCOL_PRI_BT470M:
197  return "BT 470M";
198  case AVCOL_PRI_BT470BG:
199  return "Rec 601 (PAL & SECAM)";
200  case AVCOL_PRI_SMPTE170M:
201  return "Rec 601 (NTSC)";
202  case AVCOL_PRI_SMPTE240M:
203  return "Smpte 240 (NTSC)";
204  case AVCOL_PRI_FILM:
205  return "Film";
206 #if LIBAVCODEC_VERSION_MAJOR > 54
207 #ifdef AVCOL_TRC_BT2020_CL
208  case AVCOL_PRI_BT2020:
209  return "ITU-R BT2020";
210 #endif
211 #endif
212  case AVCOL_PRI_NB:
213  return "Not ABI";
214  default:
215  return "";
216  }
217 }
218 
220 {
221  if(!_codecContext)
222  throw std::runtime_error("unknown codec context");
223 
224  switch(_codecContext->chroma_sample_location)
225  {
226  case AVCHROMA_LOC_UNSPECIFIED:
227  return "unspecified";
228  case AVCHROMA_LOC_LEFT:
229  return "left (mpeg2/4, h264 default)";
230  case AVCHROMA_LOC_CENTER:
231  return "center (mpeg1, jpeg, h263)";
232  case AVCHROMA_LOC_TOPLEFT:
233  return "top left";
234  case AVCHROMA_LOC_TOP:
235  return "top";
236  case AVCHROMA_LOC_BOTTOMLEFT:
237  return "bottom left";
238  case AVCHROMA_LOC_BOTTOM:
239  return "bottom";
240  case AVCHROMA_LOC_NB:
241  return "Not ABI";
242  default:
243  return "";
244  }
245 }
246 
248 {
249  if(!_codecContext)
250  throw std::runtime_error("unknown codec context");
251 
252  switch(_codecContext->field_order)
253  {
254  case AV_FIELD_UNKNOWN:
255  return "unknown";
256  case AV_FIELD_PROGRESSIVE:
257  return "progressive";
258  case AV_FIELD_TT:
259  return "top top";
260  case AV_FIELD_BB:
261  return "bottom bottom";
262  case AV_FIELD_TB:
263  return "top bottom";
264  case AV_FIELD_BT:
265  return "bottom top";
266  default:
267  return "";
268  }
269 }
270 
272 {
273  if(!_codecContext)
274  throw std::runtime_error("unknown codec context");
275  return _firstGopTimeCode;
276 }
277 
279 {
280  int64_t startTimeCode = getStartTimecode();
281  std::ostringstream os;
282  if(startTimeCode == -1)
283  os << "unset";
284  else
285  {
286  os << std::setfill('0');
287  os << std::setw(2) << (startTimeCode >> 19 & 0x1f) << ":"; // 5-bit hours
288  os << std::setw(2) << (startTimeCode >> 13 & 0x3f) << ":"; // 6-bit minutes
289  os << std::setw(2) << (startTimeCode >> 6 & 0x3f); // 6-bit seconds
290  os << (startTimeCode & 1 << 24 ? ';' : ':'); // 1-bit drop flag
291  os << std::setw(2) << (startTimeCode & 0x3f); // 6-bit frames
292  }
293  return os.str();
294 }
295 
297 {
298  if(!_codecContext)
299  throw std::runtime_error("unknown codec context");
300 
301  Rational sar = {
302  _codecContext->sample_aspect_ratio.num, _codecContext->sample_aspect_ratio.den,
303  };
304  return sar;
305 }
306 
308 {
309  if(!_codecContext)
310  throw std::runtime_error("unknown codec context");
311 
312  int darNum, darDen;
313  av_reduce(&darNum, &darDen, _codecContext->width * getSar().num, _codecContext->height * getSar().den, 1024 * 1024);
314 
315  Rational dar = {
316  darNum, darDen,
317  };
318  return dar;
319 }
320 
322 {
323  if(!_codecContext)
324  throw std::runtime_error("unknown codec context");
325  // return bit rate of stream if present or VBR mode
326  if(_codecContext->bit_rate || _codecContext->rc_max_rate)
327  return _codecContext->bit_rate;
328 
329  // else compute bit rate from the first GOP
330  if(!_formatContext || !_codec)
331  throw std::runtime_error("cannot compute bit rate: unknown format or codec context");
332 
333  if(!_codecContext->width || !_codecContext->height)
334  throw std::runtime_error("cannot compute bit rate: invalid frame size");
335 
336  // Needed to get the gop size
338  throw std::runtime_error("cannot compute bit rate: need to get info from the first gop (see eAnalyseLevelFirstGop)");
339 
340  // discard no frame type when decode
341  _codecContext->skip_frame = AVDISCARD_NONE;
342 
343  Frame frame;
344  AVPacket pkt;
345  av_init_packet(&pkt);
346  avcodec_open2(_codecContext, _codec, NULL);
347 
348  int gotFrame = 0;
349  size_t count = 0;
350  int gopFramesSize = 0;
351 
352  while(!av_read_frame(const_cast<AVFormatContext*>(_formatContext), &pkt))
353  {
354  if(pkt.stream_index == (int)_streamIndex)
355  {
356  avcodec_decode_video2(_codecContext, &frame.getAVFrame(), &gotFrame, &pkt);
357  if(gotFrame)
358  {
359 #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 7, 100)
360  gopFramesSize += frame.getEncodedSize();
361 #else
362  gopFramesSize += pkt.size;
363 #endif
364  ++count;
365  }
366  }
367  av_free_packet(&pkt);
368  if(getGopSize() == count)
369  break;
370  }
371 
372  int bitsPerByte = 8;
373  return (gopFramesSize / getGopSize()) * bitsPerByte * getFps();
374 }
375 
377 {
378  if(!_codecContext)
379  throw std::runtime_error("unknown codec context");
380  return _codecContext->rc_max_rate;
381 }
382 
384 {
385  if(!_codecContext)
386  throw std::runtime_error("unknown codec context");
387  return _codecContext->rc_min_rate;
388 }
389 
391 {
392  if(!_formatContext)
393  throw std::runtime_error("unknown format context");
394  size_t nbFrames = _formatContext->streams[_streamIndex]->nb_frames;
395  if(nbFrames == 0)
396  nbFrames = getFps() * getDuration();
397  return nbFrames;
398 }
399 
401 {
402  if(!_codecContext)
403  throw std::runtime_error("unknown codec context");
404  return _codecContext->ticks_per_frame;
405 }
406 
408 {
409  if(!_codecContext)
410  throw std::runtime_error("unknown codec context");
411  return _codecContext->width;
412 }
413 
415 {
416  if(!_codecContext)
417  throw std::runtime_error("unknown codec context");
418  return _codecContext->height;
419 }
420 
422 {
423  if(!_codecContext)
424  throw std::runtime_error("unknown codec context");
425  return _codecContext->dtg_active_format;
426 }
427 
429 {
430  if(!_codecContext)
431  throw std::runtime_error("unknown codec context");
432  return _codecContext->refs;
433 }
434 
436 {
437  if(!_codecContext)
438  throw std::runtime_error("unknown codec context");
439  return _codecContext->profile;
440 }
441 
443 {
444  if(!_codecContext)
445  throw std::runtime_error("unknown codec context");
446  return _codecContext->level;
447 }
448 
450 {
451  return av_q2d(_formatContext->streams[_streamIndex]->avg_frame_rate);
452 }
453 
455 {
456  if(!_codecContext)
457  throw std::runtime_error("unknown codec context");
458  return (bool)_codecContext->has_b_frames;
459 }
460 
461 // CODEC_FLAG_CLOSED_GOP is superior of INT_MAX, and _codecContext->flags is an int
462 // => Need a patch from FFmpeg
463 // bool VideoProperties::isClosedGop() const
464 //{
465 // if( ! _codecContext )
466 // throw std::runtime_error( "unknown codec context" );
467 // return ( _codecContext->flags & CODEC_FLAG_CLOSED_GOP ) == CODEC_FLAG_CLOSED_GOP;
468 //}
469 
471 {
473  {
474  if(_codecContext->width && _codecContext->height)
475  {
476  // Discard no frame type when decode
477  _codecContext->skip_frame = AVDISCARD_NONE;
478 
479  AVPacket pkt;
480  av_init_packet(&pkt);
481 
482  // Initialize the AVCodecContext to use the given AVCodec
483  avcodec_open2(_codecContext, _codec, NULL);
484 
485  Frame frame;
486  size_t count = 0;
487  int gotFrame = 0;
488  int positionOfFirstKeyFrame = -1;
489  int positionOfLastKeyFrame = -1;
490 
491  while(!av_read_frame(const_cast<AVFormatContext*>(_formatContext), &pkt))
492  {
493  if(pkt.stream_index == (int)_streamIndex)
494  {
495  avcodec_decode_video2(_codecContext, &frame.getAVFrame(), &gotFrame, &pkt);
496  if(gotFrame)
497  {
498  AVFrame& avFrame = frame.getAVFrame();
499 
500  _gopStructure.push_back(
501  std::make_pair(av_get_picture_type_char(avFrame.pict_type), frame.getEncodedSize()));
502  _isInterlaced = avFrame.interlaced_frame;
503  _isTopFieldFirst = avFrame.top_field_first;
504  if(avFrame.pict_type == AV_PICTURE_TYPE_I)
505  {
506  if(positionOfFirstKeyFrame == -1)
507  positionOfFirstKeyFrame = count;
508  else
509  positionOfLastKeyFrame = count;
510  }
511 
512  ++count;
513  }
514  }
515  av_free_packet(&pkt);
516 
517  // If the first 2 key frames are found
518  if(positionOfFirstKeyFrame != -1 && positionOfLastKeyFrame != -1)
519  {
520  // Set gop size as distance between these 2 key frames
521  _gopSize = positionOfLastKeyFrame - positionOfFirstKeyFrame;
522  // Update gop structure to keep only one gop
523  while(_gopStructure.size() > _gopSize)
524  _gopStructure.pop_back();
525  break;
526  }
527  }
528 
529  // Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself)
530  avcodec_close(_codecContext);
531  }
532  }
533 }
534 
536 {
537  // Add properties of base class
538  PropertyVector basedProperty;
539  StreamProperties::fillVector(basedProperty);
540  data.insert(data.begin(), basedProperty.begin(), basedProperty.end());
541 
542  addProperty(data, "profile", &VideoProperties::getProfile);
543  addProperty(data, "profileName", &VideoProperties::getProfileName);
544  addProperty(data, "level", &VideoProperties::getLevel);
545  addProperty(data, "startTimecode", &VideoProperties::getStartTimecodeString);
546  addProperty(data, "width", &VideoProperties::getWidth);
547  addProperty(data, "height", &VideoProperties::getHeight);
548  addProperty(data, "pixelAspectRatio", &VideoProperties::getSar);
549  addProperty(data, "displayAspectRatio", &VideoProperties::getDar);
550  addProperty(data, "dtgActiveFormat", &VideoProperties::getDtgActiveFormat);
551  addProperty(data, "colorTransfert", &VideoProperties::getColorTransfert);
552  addProperty(data, "colorspace", &VideoProperties::getColorspace);
553  addProperty(data, "colorRange", &VideoProperties::getColorRange);
554  addProperty(data, "colorPrimaries", &VideoProperties::getColorPrimaries);
555  addProperty(data, "chromaSampleLocation", &VideoProperties::getChromaSampleLocation);
556  addProperty(data, "fieldOrder", &VideoProperties::getFieldOrder);
557  addProperty(data, "fps", &VideoProperties::getFps);
558  addProperty(data, "nbFrame", &VideoProperties::getNbFrames);
559  addProperty(data, "ticksPerFrame", &VideoProperties::getTicksPerFrame);
560  addProperty(data, "bitRate", &VideoProperties::getBitRate);
561  addProperty(data, "maxBitRate", &VideoProperties::getMaxBitRate);
562  addProperty(data, "minBitRate", &VideoProperties::getMinBitRate);
563  addProperty(data, "hasBFrames", &VideoProperties::hasBFrames);
564  addProperty(data, "referencesFrames", &VideoProperties::getReferencesFrames);
565 
566  // Add properties available when decode first gop
568  {
569  detail::add(data, "gopSize", detail::propertyValueIfError);
571  detail::add(data, "interlaced", detail::propertyValueIfError);
572  detail::add(data, "topFieldFirst", detail::propertyValueIfError);
573  }
574  else
575  {
576  addProperty(data, "gopSize", &VideoProperties::getGopSize);
577 
578  std::stringstream gop;
579  for(size_t frameIndex = 0; frameIndex < _gopStructure.size(); ++frameIndex)
580  {
581  gop << _gopStructure.at(frameIndex).first;
582  gop << "(";
583  gop << _gopStructure.at(frameIndex).second;;
584  gop << ")";
585  gop << " ";
586  }
587  detail::add(data, "gop", gop.str());
588  // detail::add( data, "isClosedGop", isClosedGop() );
589 
590  addProperty(data, "interlaced ", &VideoProperties::isInterlaced);
591  addProperty(data, "topFieldFirst", &VideoProperties::isTopFieldFirst);
592  }
593 
594  // Add properties of the pixel
595  PropertyVector pixelProperties;
596  _pixelProperties.fillVector(pixelProperties);
597  data.insert(data.end(), pixelProperties.begin(), pixelProperties.end());
598 
599  return data;
600 }
601 
602 std::ostream& operator<<(std::ostream& flux, const VideoProperties& videoProperties)
603 {
604  flux << std::left;
605  flux << detail::separator << " Video stream " << detail::separator << std::endl;
606 
607  PropertyVector properties = videoProperties.asVector();
608  for(PropertyVector::iterator it = properties.begin(); it != properties.end(); ++it)
609  {
610  flux << std::setw(detail::keyWidth) << it->first << ": " << it->second << std::endl;
611  }
612 
613  return flux;
614 }
615 }
Base class of Progress. Inherit this class to have your own way to manage a progress bar...
Definition: IProgress.hpp:23
std::vector< std::pair< std::string, std::string > > PropertyVector
PropertyVector is a vector of pair, because the order of properties matters to us.
Definition: util.hpp:23
const AVFormatContext * _formatContext
Has link (no ownership)
EAnalyseLevel
Level of file analysis.
Definition: util.hpp:10
std::string getChromaSampleLocation() const
std::vector< std::pair< char, int > > _gopStructure
picture type, encoded frame size in bytes
const size_t keyWidth
Definition: util.hpp:32
const std::string separator
Definition: util.hpp:33
std::string getStartTimecodeString() const
void add(PropertyVector &propertyVector, const std::string &key, const size_t &value)
Definition: util.cpp:16
AVCodec * _codec
Has link (no ownership)
void analyseGopStructure(IProgress &progress)
frame type / is key frame
PropertyVector & fillVector(PropertyVector &data) const
To avoid copy of the vector.
float getDuration() const
in seconds
size_t getBitRate() const
in bits/s
void addProperty(PropertyVector &dataVector, const std::string &key, T(VideoProperties::*getter)(void) const) const
int getEncodedSize() const
Definition: Frame.cpp:43
std::string getProfileName() const
Wrapper of an AVFormatContext.
EAnalyseLevel _levelAnalysis
Level of analysis asked.
std::string getColorPrimaries() const
std::string getColorRange() const
float getFps() const
Corresponds to the 'fps' returned by ffprobe. fps = the average framerate that has come from the AVSt...
virtual PropertyVector & fillVector(PropertyVector &data) const
To avoid copy of the vector.
std::string getFieldOrder() const
int64_t _firstGopTimeCode
GOP timecode of the first frame.
AVCodecContext * _codecContext
Has link (no ownership)
This class describes decoded (raw) audio or video data.
Definition: Frame.hpp:16
PropertyVector & fillVector(PropertyVector &data) const
To avoid copy of the vector.
std::string getColorTransfert() const
Virtual based class of properties for all types of stream.
std::string getColorspace() const
AVRational Rational
Definition: common.hpp:55
VideoProperties(const FormatContext &formatContext, const size_t index, IProgress &progress, const EAnalyseLevel level=eAnalyseLevelFirstGop)
std::ostream & operator<<(std::ostream &flux, const InputFile &input)
Definition: InputFile.cpp:171
AVFrame & getAVFrame()
Definition: Frame.hpp:88
const std::string propertyValueIfError
Definition: util.hpp:38
PropertyVector asVector() const
Same data with a specific order.
PixelProperties _pixelProperties
All the pixel properties contained in this stream.