VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvavcreader_libav.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvavcreader_libav.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gravc.h"
23 #include "gvavcreader.h"
24 #include "grimagetype.h"
25 #include "gstr.h"
26 #include "gtest.h"
27 #include "ghexdump.h"
28 #include "gassert.h"
29 #include <cstring>
30 #include <vector>
31 #include <algorithm> // std::max
32 #include <iostream>
33 #include <sstream>
34 
35 extern "C" {
36 #define __STDC_CONSTANT_MACROS
37 #include "libavcodec/avcodec.h"
38 #include "libavutil/imgutils.h" // av_image_alloc
39 #include "libavutil/mem.h" // av_free
40 }
41 
42 // the libav api is a moving target, and these transitional version numbers
43 // are not necessarily correct -- autoconf tests are preferable
44 //
45 #ifndef GCONFIG_LIBAV_NEW_FRAME_ALLOC_FN
46 #if LIBAVCODEC_VERSION_MAJOR >= 56
47 #define GCONFIG_LIBAV_NEW_FRAME_ALLOC_FN 1
48 #else
49 #define GCONFIG_LIBAV_NEW_FRAME_ALLOC_FN 0
50 #endif
51 #endif
52 //
53 #ifndef GCONFIG_LIBAV_NEW_DECODE_FN
54 #if LIBAVCODEC_VERSION_MAJOR >= 57
55 #define GCONFIG_LIBAV_NEW_DECODE_FN 1
56 #else
57 #define GCONFIG_LIBAV_NEW_DECODE_FN 0
58 #endif
59 #endif
60 //
61 #ifndef GCONFIG_LIBAV_NEW_DESCRIPTOR
62 #if LIBAVCODEC_VERSION_MAJOR >= 57
63 #define GCONFIG_LIBAV_NEW_DESCRIPTOR 1
64 #else
65 #define GCONFIG_LIBAV_NEW_DESCRIPTOR 0
66 #endif
67 #endif
68 //
69 #ifndef AV_PIX_FMT_FLAG_BE
70 #define AV_PIX_FMT_FLAG_BE PIX_FMT_BE
71 #define AV_PIX_FMT_FLAG_PAL PIX_FMT_PAL
72 #define AV_PIX_FMT_FLAG_BITSTREAM PIX_FMT_BITSTREAM
73 #define AV_PIX_FMT_FLAG_RGB PIX_FMT_RGB
74 #endif
75 
76 static void logger( void * p , int level , const char * s , va_list args )
77 {
78  const char * class_name = p && *(AVClass**)p ? (*(AVClass**)p)->class_name : "libav" ;
79 
80  std::string text ;
81  {
82  static std::vector<char> buffer( 100U ) ;
83  buffer[0] = '\0' ;
84  int rc = vsnprintf( &buffer[0] , buffer.size() , s , args ) ;
85  if( rc <= 0 ) rc = 1 , buffer[0] = '\0' ;
86  size_t lastpos = std::min(static_cast<size_t>(rc),buffer.size()) - 1 ;
87  if( buffer[lastpos] == '\n' ) buffer[lastpos] = '\0' ;
88  text = std::string(class_name) + ": [" + std::string(&buffer[0]) + "]" ;
89  }
90 
91  if( text.find("AVCodecContext: [no frame!]") != std::string::npos )
92  level = AV_LOG_INFO ;
93 
94  if( level <= AV_LOG_ERROR )
95  G_WARNING( "Gv::AvcReaderStream: libav error: " << text ) ;
96  else if( level <= AV_LOG_WARNING )
97  G_WARNING( "Gv::AvcReaderStream: libav warning: " << text ) ;
98  else if( level <= AV_LOG_INFO )
99  G_LOG( "Gv::AvcReaderStream: libav info: " << text ) ;
100  else
101  G_DEBUG( "Gv::AvcReaderStream: libav info: " << text ) ;
102 }
103 
104 /// \class Gv::AvcReaderStreamImp
105 /// A private pimple class for Gv::AvcReaderStream, holding
106 /// AVCode, AVCodecContext and AVFrame libav objects.
107 ///
109 {
110 public:
112  explicit AvcReaderStreamImp( const Gr::Avc::Configuration & avcc ) ;
113  ~AvcReaderStreamImp() ;
114  void init( const Gr::Avc::Configuration * avcc_p ) ;
115 
116 public:
117  int m_dx ;
118  int m_dy ;
119  AVCodec * m_codec ;
120  AVCodecContext * m_cc ;
121  std::string m_sps_pps ;
122  AVFrame * m_frame ;
123 } ;
124 
125 Gv::AvcReaderStreamImp::AvcReaderStreamImp() :
126  m_dx(0) ,
127  m_dy(0) ,
128  m_codec(nullptr) ,
129  m_cc(nullptr) ,
130  m_frame(nullptr)
131 {
132  init( nullptr ) ;
133 }
134 
135 Gv::AvcReaderStreamImp::AvcReaderStreamImp( const Gr::Avc::Configuration & avcc ) :
136  m_dx(0) ,
137  m_dy(0) ,
138  m_codec(nullptr) ,
139  m_cc(nullptr) ,
140  m_frame(nullptr)
141 {
142  init( &avcc ) ;
143 }
144 
145 void Gv::AvcReaderStreamImp::init( const Gr::Avc::Configuration * avcc_p )
146 {
147  if( avcc_p != nullptr )
148  {
149  m_dx = avcc_p->sps(0U).dx() ;
150  m_dy = avcc_p->sps(0U).dy() ;
151  }
152 
153  // logging
154  {
155  av_log_set_callback( logger ) ;
156  if( G::LogOutput::instance() && G::LogOutput::instance()->at(G::Log::s_Debug) )
157  av_log_set_level( AV_LOG_DEBUG ) ;
158  else
159  av_log_set_level( AV_LOG_VERBOSE ) ;
160  }
161 
162  // m_codec
163  {
164  avcodec_register_all() ;
165 
166  m_codec = avcodec_find_decoder( AV_CODEC_ID_H264 ) ;
167  if( m_codec == nullptr )
168  throw std::runtime_error( "avcodec_find_decoder failed" ) ;
169  }
170 
171  // m_cc
172  {
173  m_cc = avcodec_alloc_context3( m_codec ) ;
174  G_ASSERT( m_cc != nullptr ) ;
175  m_cc->debug |= FF_DEBUG_STARTCODE ;
176  m_cc->debug |= FF_DEBUG_PICT_INFO ;
177  }
178 
179  // pass any avcc from a fmtp into to the context
180  if( avcc_p != nullptr )
181  {
182  // create a two-NALU stream from the SPS and PPS structures and feed it into
183  // the libav context via its extradata fields -- see also 14496-10 B.1.2
184  //
185  m_sps_pps = avcc_p->nalus() ; // "00-00-00-01 <sps> 00-00-01 <pps>"
186  m_cc->extradata = reinterpret_cast<uint8_t*>(const_cast<char*>(m_sps_pps.data())) ;
187  m_cc->extradata_size = m_sps_pps.size() ;
188  }
189 
190  // m_codec + m_cc
191  {
192  int e = avcodec_open2( m_cc , m_codec , nullptr ) ;
193  if( e < 0 )
194  throw std::runtime_error( "avcodec_open2 failed: " + G::Str::fromInt(e) ) ;
195 
196  //G_ASSERT( m_cc->codec_type == AVMEDIA_TYPE_VIDEO ) ;
197  //G_ASSERT( m_cc->codec_id == AV_CODEC_ID_H264 ) ;
198  }
199 
200  // m_frame
201  {
202 #if GCONFIG_LIBAV_NEW_FRAME_ALLOC_FN
203  m_frame = av_frame_alloc() ;
204  G_ASSERT( m_frame != nullptr ) ;
205 #else
206  m_frame = avcodec_alloc_frame() ;
207  G_ASSERT( m_frame != nullptr ) ;
208  avcodec_get_frame_defaults( m_frame ) ;
209 #endif
210  }
211 }
212 
213 Gv::AvcReaderStreamImp::~AvcReaderStreamImp()
214 {
215  if( m_cc ) avcodec_close(m_cc) , av_free(m_cc) ;
216 #if GCONFIG_LIBAV_NEW_FRAME_ALLOC_FN
217  if( m_frame ) av_frame_free( &m_frame ) ;
218 #else
219  if( m_frame ) avcodec_free_frame( &m_frame ) ;
220 #endif
221 }
222 
223 // ==
224 
226  m_imp(new AvcReaderStreamImp)
227 {
228 }
229 
231  m_imp(new AvcReaderStreamImp(avcc))
232 {
233 }
234 
236 {
237  delete m_imp ;
238 }
239 
241 {
242  return m_imp->m_dx ;
243 }
244 
246 {
247  return m_imp->m_dy ;
248 }
249 
250 // ==
251 
252 namespace Gv
253 {
254  std::ostream & operator<<( std::ostream & stream , const AvcReader::Component & c )
255  {
256  return stream
257  << "data=" << (const void*)c.data << " "
258  << "offset=" << c.offset << " "
259  << "linesize=" << c.linesize << " "
260  << "step=" << c.step << " "
261  << "depth=" << c.depth << " "
262  << "mask=" << c.mask << " "
263  << "value-shift=" << c.shift << " "
264  << "eightbit=" << c.eightbit << " "
265  << "x-shift=" << c.x_shift << " "
266  << "y-shift=" << c.y_shift ;
267  }
268 }
269 
270 namespace
271 {
272  Gv::AvcReader::Component make_component( const AVFrame & frame , const AVPixFmtDescriptor & pfd ,
273  const AVComponentDescriptor & pfd_comp )
274  {
276  const bool big_endian = !!( pfd.flags & AV_PIX_FMT_FLAG_BE ) ;
277  comp.data = frame.data[pfd_comp.plane] ;
278  comp.linesize = frame.linesize[pfd_comp.plane] ;
279 #if GCONFIG_LIBAV_NEW_DESCRIPTOR
280  comp.offset = pfd_comp.offset ;
281  comp.step = pfd_comp.step ;
282  comp.depth = pfd_comp.depth ;
283 #else
284  comp.offset = pfd_comp.offset_plus1 - 1U ;
285  comp.step = pfd_comp.step_minus1 + 1U ;
286  comp.depth = pfd_comp.depth_minus1 + 1U ;
287 #endif
288  comp.mask = (1U << comp.depth) - 1U ;
289  comp.shift = pfd_comp.shift ;
290  comp.eightbit = (comp.shift + comp.depth) <= 8U ;
291  comp.offset += ( comp.eightbit && big_endian ? 1U : 0U ) ; // as in av_read_image_line()
292  comp.x_shift = 0 ; // set later
293  comp.y_shift = 0 ; // set later
294  return comp ;
295  }
296  bool valid_pfd( const AVPixFmtDescriptor * pfd )
297  {
298  return
299  pfd != nullptr &&
300  pfd->nb_components >= 3U &&
301  !(pfd->flags&AV_PIX_FMT_FLAG_PAL) && // (palette)
302  !(pfd->flags&AV_PIX_FMT_FLAG_BITSTREAM) ;
303  }
304  void interpret_frame( AVFrame * frame , Gv::AvcReader::Data & m )
305  {
306  // set up the Component structures for efficient access by the inline methods
307 
308  // see av_read_image_line() in libavutil/pixdesc.c
309  const AVPixFmtDescriptor * pfd = av_pix_fmt_desc_get( static_cast<AVPixelFormat>(frame->format) ) ;
310  const char * format_name = pfd ? pfd->name : "" ;
311  G_DEBUG( "Gv::AvcReader::init: pixel format " << format_name ) ;
312  if( !valid_pfd(pfd) )
313  throw std::runtime_error( std::string("unexpected pixel format [") + format_name + "]" ) ;
314 
315  // parse the description
316  m.m_rgb = !!( pfd->flags & AV_PIX_FMT_FLAG_RGB ) ;
317  m.m_big_endian = !!( pfd->flags & AV_PIX_FMT_FLAG_BE ) ;
318  m.m_x_shift = pfd->log2_chroma_w ;
319  m.m_y_shift = pfd->log2_chroma_h ;
320 
321  // parse the components
322  unsigned int n = std::min( 4U , static_cast<unsigned int>(pfd->nb_components) ) ;
323  for( unsigned int c = 0U ; c < n ; c++ )
324  {
325  m.m_component[c] = make_component( *frame , *pfd , pfd->comp[c] ) ;
326  }
327 
328  // order the components as r-g-b or y-u-v
329  {
330  // TODO dont rely on the pixel format name
331  const bool do_shift = format_name[0] == 'a' ; // argb/abgr
332  const bool do_swap_1_3 = format_name[0] == 'b' || ( format_name[0] == 'a' && format_name[1] == 'b' ) ; // bgr/abgr
333  const bool do_swap_2_3 = format_name[0] == 'y' && format_name[1] == 'v' ; // yvu
334  if( do_shift )
335  {
336  m.m_component[0] = m.m_component[1] ;
337  m.m_component[1] = m.m_component[2] ;
338  m.m_component[2] = m.m_component[3] ;
339  }
340  if( do_swap_1_3 )
341  std::swap( m.m_component[0] , m.m_component[2] ) ;
342  else if( do_swap_2_3 )
343  std::swap( m.m_component[1] , m.m_component[2] ) ;
344  }
345 
346  // copy the chroma shifts into the component structures
347  m.m_component[1].x_shift = m.m_x_shift ;
348  m.m_component[1].y_shift = m.m_y_shift ;
349  m.m_component[2].x_shift = m.m_x_shift ;
350  m.m_component[2].y_shift = m.m_y_shift ;
351 
352  for( unsigned int c = 0 ; c < 3U ; c++ )
353  {
354  G_DEBUG( "Gv::AvcReader::init: component " << c << ": " << m.m_component[c] ) ;
355  }
356  }
357 }
358 
359 void Gv::AvcReader::init( const unsigned char * p , size_t n )
360 {
361  // sanity check -- do our own parsing of any nalus that we know how to parse
362  if( G::Test::enabled("nalu-check") && n > 5U )
363  {
364  std::string nalu = std::string(reinterpret_cast<const char*>(p+4) , n-4U ) ;
365  unsigned int nalu_type = static_cast<unsigned int>(nalu.at(0U)) & 0x9f ;
366  bool ok = true ;
367  static int sps_chroma_format_idc = -1 ;
368  if( nalu_type == 7U )
369  {
370  G_LOG_S( "Gv::AvcReader::init: checking sps nalu" ) ;
371  Gr::Avc::Sps sps( nalu ) ;
372  ok = sps.valid() ;
373  if( ok ) sps_chroma_format_idc = sps.m_chroma_format_idc ;
374  }
375  else if( nalu_type == 8U && sps_chroma_format_idc != -1 )
376  {
377  G_LOG_S( "Gv::AvcReader::init: checking pps nalu" ) ;
378  Gr::Avc::Pps pps( nalu , sps_chroma_format_idc ) ;
379  ok = pps.valid() ;
380  }
381  if( !ok )
382  throw std::runtime_error( "nalu check failed" ) ;
383  }
384 
385  m_data.m_component[0].data = nullptr ;
386  m_data.m_component[1].data = nullptr ;
387  m_data.m_component[2].data = nullptr ;
388  G_ASSERT( !valid() ) ;
389 
390  m_data.m_dx = m_stream.dx() ;
391  m_data.m_dy = m_stream.dy() ;
392 
393  // wrap the raw payload data in an AVPacket
394  AVPacket av_packet ;
395  av_init_packet( &av_packet ) ;
396  av_packet.size = n ;
397  av_packet.data = const_cast<uint8_t*>(p) ;
398 
399  // pull out the context and frame pointers
400  AVCodecContext * cc = m_stream.m_imp->m_cc ;
401  AVFrame * frame = m_stream.m_imp->m_frame ;
402 
403  // decode the packet into the frame
404 #if GCONFIG_LIBAV_NEW_DECODE_FN
405  int rc = avcodec_send_packet( cc , &av_packet ) ;
406  if( rc < 0 ) G_DEBUG( "Gv::AvcReader::init: avcodec_send_packet failed: " << rc ) ;
407  while( rc >= 0 )
408  {
409  rc = avcodec_receive_frame( cc , frame ) ;
410  if( rc == AVERROR(EAGAIN) )
411  {
412  G_DEBUG( "Gv::AvcReader::init: avcodec_receive_frame eagain" ) ;
413  break ;
414  }
415  else if( rc == AVERROR_EOF )
416  {
417  throw std::runtime_error( "avcodec_receive_frame eof" ) ;
418  }
419  else
420  {
421  // (in the unlikely event of more than one frame per packet just keep the last one)
422  interpret_frame( frame , m_data ) ;
423  m_data.m_dx = cc->width ;
424  m_data.m_dy = cc->height ;
425  }
426  }
427 #else
428  int done = 0 ;
429  ssize_t rc = avcodec_decode_video2( cc , frame , &done , &av_packet ) ;
430  if( rc >= 0 && done )
431  {
432  interpret_frame( frame , m_data ) ;
433  m_data.m_dx = cc->width ;
434  m_data.m_dy = cc->height ;
435  }
436  else if( rc < 0 )
437  {
438  // (some nalu types do not encode images, so not usually an error)
439  G_LOG( "Gv::AvcReader::init: libav error: avcodec_decode_video2 failed: " << rc ) ;
440  }
441 #endif
442 }
443 
445 {
446  return !!m_stream.m_imp->m_frame->key_frame ;
447 }
448 
449 template <typename Tout>
450 void fill_imp( Tout out_p , const Gv::AvcReader & reader , int scale , bool monochrome_out )
451 {
452  const int dx = reader.dx() ;
453  const int dy = reader.dy() ;
454  if( reader.simple() )
455  {
456  if( monochrome_out )
457  {
458  for( int y = 0 ; y < dy ; y += scale )
459  {
460  Gv::AvcReader::SimpleIterator in_p( reader , y , scale ) ;
461  for( int x = 0 ; x < dx ; x += scale , ++in_p )
462  {
463  *out_p++ = in_p.luma() ;
464  }
465  }
466  }
467  else
468  {
469  for( int y = 0 ; y < dy ; y += scale )
470  {
471  Gv::AvcReader::SimpleIterator in_p( reader , y , scale ) ;
472  for( int x = 0 ; x < dx ; x += scale , ++in_p )
473  {
474  *out_p++ = in_p.r() ;
475  *out_p++ = in_p.g() ;
476  *out_p++ = in_p.b() ;
477  }
478  }
479  }
480  }
481  else
482  {
483  if( monochrome_out )
484  {
485  for( int y = 0 ; y < dy ; y += scale )
486  {
487  Gv::AvcReader::Iterator in_p( reader , y , scale ) ;
488  for( int x = 0 ; x < dx ; x += scale , ++in_p )
489  {
490  *out_p++ = in_p.luma() ;
491  }
492  }
493  }
494  else
495  {
496  for( int y = 0 ; y < dy ; y += scale )
497  {
498  Gv::AvcReader::Iterator in_p( reader , y , scale ) ;
499  for( int x = 0 ; x < dx ; x += scale , ++in_p )
500  {
501  *out_p++ = in_p.r() ;
502  *out_p++ = in_p.g() ;
503  *out_p++ = in_p.b() ;
504  }
505  }
506  }
507  }
508 }
509 
510 Gr::ImageType Gv::AvcReader::fill( std::vector<char> & buffer , int scale , bool monochrome )
511 {
512  scale = std::max( 1 , scale ) ;
513  Gr::ImageType image_type = Gr::ImageType::raw( Gr::scaled(m_data.m_dx,scale) , Gr::scaled(m_data.m_dy,scale) , monochrome?1:3 ) ;
514 
515  buffer.resize( image_type.size() ) ;
516  fill_imp( &buffer[0] , *this , scale , monochrome ) ;
517 
518  G_ASSERT( image_type.size() == buffer.size() ) ;
519  return image_type ;
520 }
521 
523 {
524  return true ;
525 }
static bool available()
Returns true if the decoder library is built in.
bool simple() const
Returns true if the data format is simple enough for the optimised iterator, Gv::AvcReader::SimpleIte...
Definition: gvavcreader.h:248
A decoder for an AVC (aka H.264) video packet.
Definition: gvavcreader.h:79
int dx() const
Returns the image width.
Definition: gvavcreader.h:257
Synopsis:
Contains AVC configuration parameters, initialised from an "avcC" file segment or from an SDP "fmtp" ...
Definition: gravc.h:93
static LogOutput * instance()
Returns a pointer to the controlling LogOutput object.
Definition: glogoutput.cpp:109
An encapsulation of image type, including width, height and number of channels, with support for a st...
Definition: grimagetype.h:43
Describes one plane of a Gv::AvcReader image, and points to its data.
Definition: gvavcreader.h:140
int dx() const
Returns the image width as indicated by the first SPS structure, or zero if default constructed...
static std::string fromInt(int i)
Converts int 'i' to a string.
Definition: gstr.cpp:294
unsigned int dx() const
Returns the picture width in pixels.
Definition: gravc.cpp:462
static ImageType raw(int dx, int dy, int channels)
Factory function for a raw image type.
int dy() const
Returns the image height.
Definition: gvavcreader.h:263
A Picture Sequence Parameter Set (PPS) structure.
Definition: gravc.h:262
A Sequence Parameter Set (SPS) structure.
Definition: gravc.h:187
~AvcReaderStream()
Destructor.
size_t size() const
Returns the product of dx, dy and channels.
int dy() const
Returns the image height as indicated by the first SPS structure, or zero if default constructed...
AvcReaderStream()
Default constructor.
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:49
bool keyframe() const
Returns true if a key frame.
unsigned int dy() const
Returns the picture height in pixels.
Definition: gravc.cpp:473
A private pimple class for Gv::AvcReaderStream, holding AVCode, AVCodecContext and AVFrame libav obje...
A row iterator for Gv::AvcReader providing r/g/b or y/u/v across a row.
Definition: gvavcreader.h:171
Gr::ImageType fill(std::vector< char > &, int scale=1, bool monochrome=false)
Fills the supplied buffer with RGB or greyscale image data and returns the raw image type...
const Sps & sps(size_t i) const
Returns a reference to the i-th sps structure.
Definition: gravc.cpp:281
Describes a Gv::AvcReader image, and points to its data via up to four Components.
Definition: gvavcreader.h:161
std::string nalus() const
Returns the NALU byte-stream comprising the four-byte 00-00-00-01 start-code followed by the byte-stu...
Definition: gravc.cpp:291
An optimised row iterator for Gv::AvcReader when simple().
Definition: gvavcreader.h:195