VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gravc.cpp
Go to the documentation of this file.
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 // gravc.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gravc.h"
23 #include "gbase64.h"
24 #include "gstr.h"
25 #include "ghexdump.h"
26 #include "gexpgolomb.h"
27 #include "gdebug.h"
28 #include <algorithm> // std::min()
29 #include <sstream>
30 #include <set>
31 
32 namespace
33 {
34  unsigned int fromHex( const std::string & s )
35  {
36  unsigned int result = 0U ;
37  for( size_t i = 0U ; i < s.length() ; i++ )
38  {
39  result <<= 4 ;
40  unsigned int c = static_cast<unsigned char>(s.at(i)) ;
41  if( c >= 48U && c <= 57U ) result += c - 48U ;
42  else if( c >= 65U && c <= 70U ) result += c - 65U + 10U ;
43  else if( c >= 97U && c <= 102U ) result += c - 97U + 10U ;
44  else throw std::runtime_error( "invalid hex string [" + s + "]" ) ;
45  }
46  return result ;
47  }
48 }
49 
51 {
52  return Configuration( /*is_fmtp=*/ true , fmtp ) ;
53 }
54 
56 {
57  return Configuration( /*is_fmtp=*/ false , avcc ) ;
58 }
59 
60 Gr::Avc::Configuration::Configuration( bool is_fmtp , const std::string & s )
61 {
62  is_fmtp ? initFmtp(s) : initAvcc(s) ;
63 }
64 
66 {
67  report( "default constructed" ) ;
68 }
69 
70 void Gr::Avc::Configuration::initFmtp( const std::string & fmtp )
71 {
72  G::StringArray parts ; G::Str::splitIntoTokens( fmtp , parts , "; " ) ;
73  for( G::StringArray::iterator p = parts.begin() ; p != parts.end() ; ++p )
74  {
75  typedef std::string::size_type pos_t ;
76  pos_t pos = (*p).find("=") ;
77  std::string key = G::Str::head( *p , pos , std::string() ) ;
78  std::string value = G::Str::tail( *p , pos , std::string() ) ;
79  if( key == "profile-level-id" )
80  {
81  // the profile and level are in the sps, but presumbly here as a separate
82  // fmtp attribute for convenience -- where is the format specified?
83  m_fmtp_info.profile = value.length() == 6U ? fromHex(value.substr(0U,2U)) : 999U ;
84  m_fmtp_info.level = value.length() == 6U ? fromHex(value.substr(2U)) : 999U ;
85  //if( value.length() != 6U ) report( "invalid profile_level in fmtp" ) ;
86  }
87  else if( key == "packetization-mode" )
88  {
89  m_fmtp_info.packetisation_mode = G::Str::toUInt( value ) ;
90  }
91  else if( key == "sprop-parameter-sets" )
92  {
93  G::StringArray s ; G::Str::splitIntoFields( value , s , "," ) ;
94  for( G::StringArray::iterator sp = s.begin() ; sp != s.end() ; ++sp )
95  {
96  G_DEBUG( "Gr::Avc: fmtp parameter set [" << *sp << "]" ) ;
97  std::string pset = G::Base64::decode( *sp ) ;
98  G_DEBUG( "Gr::Avc: fmtp parameter set [" << G::hexdump<999>(pset) << "]" ) ;
99  unsigned int nalu_type = static_cast<unsigned char>(pset.at(0U)) & 0x1f ;
100  if( nalu_type == 7 ) // sps
101  m_sps_list.push_back( pset ) ;
102  else if( nalu_type == 8 ) // pps
103  m_pps_list.push_back( pset ) ;
104  else
105  G_WARNING( "Gr::Avc: unexpected nalu type in fmtp: " << nalu_type ) ;
106  }
107  }
108  }
109 
110  checkFmtp() ;
111  spsListParse() ;
112  ppsListParse() ;
113  checkIds() ;
114  commit() ;
115 }
116 
117 void Gr::Avc::Configuration::initAvcc( const std::string & avcc_in )
118 {
119  // the outer avcC structure is byte-oriented, but use a bit stream
120  // here for consistency with the bit-oriented sps/pps parsing
121  G_DEBUG( "Gr::Avc: avcc=[" << G::hexdump<999>(avcc_in.begin(),avcc_in.end()) << "]" ) ;
122  bit_stream_t stream( bit_iterator_t(avcc_in.data()) , bit_iterator_t(avcc_in.data()+avcc_in.size()) ) ;
123 
124  m_avcc_info.configuration_version = stream.get_byte() ;
125  m_avcc_info.profile = stream.get_byte() ;
126  m_avcc_info.profile_compat = stream.get_byte() ;
127  m_avcc_info.level = stream.get_byte() ;
128 
129  m_avcc_info.length_size_minus_one = stream.get_byte() ;
130  if( ( m_avcc_info.length_size_minus_one & 0xfc ) != 0xfc ) report( "invalid reserved bits" ) ;
131  m_avcc_info.length_size_minus_one &= 0x03 ;
132 
133  unsigned int sps_count = stream.get_byte() ;
134  if( ( sps_count & 0xe0 ) != 0xe0 ) report( "invalid reserved bits" ) ;
135  sps_count &= 0x1f ;
136 
137  unsigned int sps_size = stream.get_word() ;
138  G_DEBUG( "Gr::Avc: avcc: sps_count=" << sps_count << " sps_size=" << sps_size ) ;
139  for( unsigned int i = 0 ; i < sps_count ; i++ )
140  {
141  std::string s ;
142  for( unsigned int j = 0 ; j < sps_size ; j++ )
143  s.append( 1U , static_cast<char>(stream.get_byte()) ) ;
144  m_sps_list.push_back( s ) ;
145  G_DEBUG( "Gr::Avc: avcc: sps=[" << G::hexdump<999>(m_sps_list.back().begin(),m_sps_list.back().end()) << "]" ) ;
146  }
147 
148  unsigned int pps_count = stream.get_byte() ;
149  unsigned int pps_size = stream.get_word() ;
150  G_DEBUG( "Gr::Avc: avcc: pps_count=" << pps_count << " pps_size=" << pps_size ) ;
151  for( unsigned int i = 0 ; i < pps_count ; i++ )
152  {
153  std::string s ;
154  for( unsigned int j = 0 ; j < pps_size ; j++ )
155  s.append( 1U , static_cast<char>(stream.get_byte()) ) ;
156  m_pps_list.push_back( s ) ;
157  G_DEBUG( "Gr::Avc: avcc: pps=[" << G::hexdump<999>(m_pps_list.back().begin(),m_pps_list.back().end()) << "]" ) ;
158  }
159 
160  checkStream( stream ) ;
161  spsListParse() ;
162  ppsListParse() ;
163  checkIds() ;
164  commit() ;
165 }
166 
167 void Gr::Avc::Configuration::checkFmtp()
168 {
169  if( m_fmtp_info.packetisation_mode != 1U )
170  report( "unsuported packetisation mode in fmtp string" ) ;
171 }
172 
173 void Gr::Avc::Configuration::checkStream( bit_stream_t & stream )
174 {
175  if( !stream.good() )
176  report( "avcc premature end" ) ;
177  else if( !stream.at_end() )
178  report( "avcc has trailing junk" ) ;
179 }
180 
181 void Gr::Avc::Configuration::spsListParse()
182 {
183  for( unsigned int i = 0 ; i < m_sps_list.size() ; i++ )
184  {
185  m_sps.push_back( Sps(m_sps_list.at(i)) ) ;
186  if( !m_sps.back().valid() )
187  report( m_sps.back().reason() ) ;
188  }
189 }
190 
191 void Gr::Avc::Configuration::ppsListParse()
192 {
193  if( m_sps.empty() )
194  {
195  report( "cannot parse pps without sps" ) ;
196  return ;
197  }
198 
199  // arbitrarily take the chroma-format from the first sps -- should probably
200  // take account of the sps/pps interleaving order, but in practice there
201  // is only one sps
202  //
203  int sps_chroma_format_idc = m_sps.front().m_chroma_format_idc ;
204 
205  for( unsigned int i = 0 ; m_reason.empty() && i < m_pps_list.size() ; i++ )
206  {
207  m_pps.push_back( Pps(m_pps_list.at(i),sps_chroma_format_idc) ) ;
208  if( !m_pps.back().valid() )
209  report( m_pps.back().reason() ) ;
210  }
211 }
212 
213 void Gr::Avc::Configuration::checkIds()
214 {
215  std::set<unsigned int> sps_ids ;
216  for( size_t i = 0U ; i < m_sps.size() ; i++ )
217  {
218  unsigned int sps_id = m_sps.at(i).m_sequence_parameter_set_id ;
219  if( sps_ids.find(sps_id) != sps_ids.end() ) report( "sps id not unique" ) ;
220  sps_ids.insert( sps_id ) ;
221  }
222  std::set<unsigned int> pps_ids ;
223  for( size_t i = 0U ; i < m_pps.size() ; i++ )
224  {
225  unsigned int pps_id = m_pps.at(i).m_pic_parameter_set_id ;
226  unsigned int sps_id = m_pps.at(i).m_seq_parameter_set_id ;
227  if( pps_ids.find(pps_id) != pps_ids.end() ) report( "pps id not unique" ) ;
228  if( sps_ids.find(sps_id) == sps_ids.end() ) report( "invalid sps id in pps" ) ;
229  pps_ids.insert( pps_id ) ;
230  }
231 }
232 
233 void Gr::Avc::Configuration::commit()
234 {
235  if( !m_reason.empty() )
236  {
237  ; // m_sps.clear() ; m_pps.clear() ; // moot
238  }
239 }
240 
241 void Gr::Avc::Configuration::report( const std::string & s )
242 {
243  m_reason.append( m_reason.empty() ? s : (": "+s) ) ;
244 }
245 
247 {
248  return m_reason.empty() ;
249 }
250 
252 {
253  return m_reason ;
254 }
255 
257 {
258  return m_avcc_info.length_size_minus_one + 1U ;
259 }
260 
261 const std::vector<std::string> & Gr::Avc::Configuration::spsList() const
262 {
263  return m_sps_list ;
264 }
265 
266 const std::vector<std::string> & Gr::Avc::Configuration::ppsList() const
267 {
268  return m_pps_list ;
269 }
270 
272 {
273  return m_sps.size() ;
274 }
275 
277 {
278  return m_pps.size() ;
279 }
280 
281 const Gr::Avc::Sps & Gr::Avc::Configuration::sps( size_t i ) const
282 {
283  return m_sps.at(i) ;
284 }
285 
286 const Gr::Avc::Pps & Gr::Avc::Configuration::pps( size_t i ) const
287 {
288  return m_pps.at(i) ;
289 }
290 
291 std::string Gr::Avc::Configuration::nalus() const
292 {
293  // TODO preserve the SPS/PPS interleaving order
294  std::string result ;
295  std::string sep = Gr::Avc::Rbsp::_0001() ;
296  for( size_t i = 0U ; i < m_sps_list.size() ; i++ , sep = Gr::Avc::Rbsp::_001() )
297  {
298  result.append( sep ) ;
299  result.append( m_sps_list.at(i) ) ;
300  }
301  for( size_t i = 0U ; i < m_pps_list.size() ; i++ , sep = Gr::Avc::Rbsp::_001() )
302  {
303  result.append( sep ) ;
304  result.append( m_pps_list.at(i) ) ;
305  }
306  return result ;
307 }
308 
309 // ==
310 
311 Gr::Avc::Sps::Sps( const std::string & sps_in )
312 {
313  // see 14496-10 7.3.2.1.1
314 
315  std::string sps = Rbsp::removeByteStuffing(sps_in) ;
316  size_t rbsp_stop_bit_pos = Rbsp::findStopBit( sps ) ;
317  bit_stream_t stream( bit_iterator_t(sps.data()) , bit_iterator_t(sps.data()+sps.size()) ) ;
318 
319  m_nalu_type = stream.get_byte() & 0x1f ;
320  m_profile_idc = stream.get_byte() ;
321  m_constraint_set0_flag = stream.get_bool() ;
322  m_constraint_set1_flag = stream.get_bool() ;
323  m_constraint_set2_flag = stream.get_bool() ;
324  m_constraint_set3_flag = stream.get_bool() ;
325  m_constraint_set4_flag = stream.get_bool() ;
326  m_constraint_set5_flag = stream.get_bool() ;
327  m_zero = stream.get( 2U ) ;
328  m_level_idc = stream.get_byte() ;
329  m_sequence_parameter_set_id = stream.get_unsigned_golomb() ;
330 
331  bool extended_profile =
332  m_profile_idc == 100 || m_profile_idc == 110 || m_profile_idc == 122 ||
333  m_profile_idc == 244 || m_profile_idc == 44 || m_profile_idc == 83 ||
334  m_profile_idc == 86 || m_profile_idc == 118 || m_profile_idc == 128 ;
335  m_chroma_format_idc = 0 ;
336  m_separate_colour_plane_flag = false ;
337  m_bit_depth_luma_minus8 = 0U ;
338  m_bit_depth_chroma_minus8 = 0U ;
339  m_qpprime_y_zero_transform_bypass_flag = false ;
340  m_seq_scaling_matrix_present_flag = false ;
341  if( extended_profile )
342  {
343  m_chroma_format_idc = stream.get_unsigned_golomb() ;
344  m_separate_colour_plane_flag = m_chroma_format_idc == 3 ? stream.get_bool() : false ;
345  m_bit_depth_luma_minus8 = stream.get_unsigned_golomb() ;
346  m_bit_depth_chroma_minus8 = stream.get_unsigned_golomb() ;
347  m_qpprime_y_zero_transform_bypass_flag = stream.get_bool() ;
348  m_seq_scaling_matrix_present_flag = stream.get_bool() ;
349  if( m_seq_scaling_matrix_present_flag )
350  {
351  report( "sps seq_scaling_matrix_present_flag not yet implemented" ) ;
352  int n = (m_chroma_format_idc != 3) ? 8 : 12 ;
353  for( int i = 0 ; i < n ; i++ )
354  ; // TODO scaling_list()
355  }
356  }
357 
358  m_log2_max_frame_num_minus4 = stream.get_unsigned_golomb() ;
359  m_pic_order_cnt_type = stream.get_unsigned_golomb() ;
360 
361  m_log2_max_pic_order_cnt_lsb_minus4 = 0U ;
362  m_delta_pic_order_always_zero_flag = false ;
363  m_offset_for_non_ref_pic = 0 ;
364  m_offset_for_top_to_bottom_field = 0 ;
365  m_num_ref_frames_in_pic_order_cnt_cycle = 0U ;
366  if( m_pic_order_cnt_type == 0U )
367  {
368  m_log2_max_pic_order_cnt_lsb_minus4 = stream.get_unsigned_golomb() ;
369  }
370  else if( m_pic_order_cnt_type == 1U )
371  {
372  m_delta_pic_order_always_zero_flag = stream.get_bool() ;
373  m_offset_for_non_ref_pic = stream.get_signed_golomb() ;
374  m_offset_for_top_to_bottom_field = stream.get_signed_golomb() ;
375  m_num_ref_frames_in_pic_order_cnt_cycle = stream.get_unsigned_golomb() ;
376  for( unsigned int i = 0 ; i < m_num_ref_frames_in_pic_order_cnt_cycle ; i++ )
377  (void) stream.get_signed_golomb() ;
378  }
379 
380  m_max_num_ref_frames = stream.get_unsigned_golomb() ;
381  m_gaps_in_frame_num_value_allowed_flag = stream.get_bool() ;
382  m_pic_width_in_mbs_minus1 = stream.get_unsigned_golomb() ;
383  m_pic_height_in_map_units_minus1 = stream.get_unsigned_golomb() ;
384 
385  m_frame_mbs_only_flag = stream.get_bool() ;
386  m_mb_adaptive_frame_field_flag = m_frame_mbs_only_flag ? false : stream.get_bool() ; // sic
387 
388  m_direct_8x8_inference_flag = stream.get_bool() ;
389 
390  m_frame_cropping_flag = stream.get_bool() ;
391  m_frame_crop_left_offset = m_frame_cropping_flag ? stream.get_unsigned_golomb() : 0U ;
392  m_frame_crop_right_offset = m_frame_cropping_flag ? stream.get_unsigned_golomb() : 0U ;
393  m_frame_crop_top_offset = m_frame_cropping_flag ? stream.get_unsigned_golomb() : 0U ;
394  m_frame_crop_bottom_offset = m_frame_cropping_flag ? stream.get_unsigned_golomb() : 0U ;
395 
396  m_vui_parameters_present_flag = stream.get_bool() ;
397  m_nal_hrd_parameters_present_flag = false ;
398  m_vcl_hrd_parameters_present_flag = false ;
399  if( m_vui_parameters_present_flag )
400  {
401  // see E.1.1
402  G_DEBUG( "Gr::Avc: vui parameters preset at bit offset " << stream.tellg() ) ;
403  bool aspect_ratio_info_present_flag = stream.get_bool() ;
404  if( aspect_ratio_info_present_flag )
405  {
406  unsigned char aspect_ratio_idc = stream.get_byte() ;
407  if( aspect_ratio_idc == 255U ) // Extended_SAR
408  {
409  /*unsigned int sar_width=*/ stream.get_word() ;
410  /*unsigned int sar_height=*/ stream.get_word() ;
411  }
412  }
413  bool overscan_info_present_flag = stream.get_bool() ;
414  if( overscan_info_present_flag )
415  /*unsigned char overscan_appropriate_flag=*/ stream.get_bool() ;
416  bool video_signal_type_present_flag = stream.get_bool() ;
417  if( video_signal_type_present_flag )
418  {
419  /*unsigned char video_format=*/ stream.get( 3 ) ;
420  /*bool video_full_range_flag=*/ stream.get_bool() ;
421  bool colour_description_present_flag = stream.get_bool() ;
422  if( colour_description_present_flag )
423  stream.get_byte() , stream.get_byte() , stream.get_byte() ;
424  }
425  bool chroma_loc_info_present_flag = stream.get_bool() ;
426  if( chroma_loc_info_present_flag )
427  stream.get_unsigned_golomb() , stream.get_unsigned_golomb() ;
428  bool timing_info_present_flag = stream.get_bool() ;
429  if( timing_info_present_flag )
430  stream.get_dword() , stream.get_dword() , stream.get_bool() ;
431  m_nal_hrd_parameters_present_flag = stream.get_bool() ;
432  if( m_nal_hrd_parameters_present_flag )
433  skip_hrd_parameters( stream ) ;
434  m_vcl_hrd_parameters_present_flag = stream.get_bool() ;
435  if( m_vcl_hrd_parameters_present_flag )
436  skip_hrd_parameters( stream ) ;
437  if( m_nal_hrd_parameters_present_flag || m_vcl_hrd_parameters_present_flag )
438  stream.get_bool() ;
439  /*bool pic_struct_present_flag=*/ stream.get_bool() ;
440  bool stream_restriction_flag = stream.get_bool() ;
441  if( stream_restriction_flag )
442  {
443  stream.get_bool() ;
444  for( int i = 0 ; i < 6 ; i++ )
445  stream.get_unsigned_golomb() ;
446  }
447  }
448  G_DEBUG( "Gr::Avc: sps: tellg=" << stream.tellg() << " stopbit=" << rbsp_stop_bit_pos ) ;
449 
450  // report errors
451  if( Rbsp::hasMarkers(sps_in) ) report( "byte stuffing error" ) ;
452  if( !stream.good() ) report( "premature end of data" ) ;
453  if( stream.tellg() != rbsp_stop_bit_pos ) report( "parsing did not finish at the stop bit" ) ;
454  if( m_nalu_type != 7U ) report( "invalid nalu type (" , m_nalu_type , ")" ) ;
455  if( m_zero != 0U ) report( "invalid zero field (" , m_zero , ")" ) ;
456  if( m_pic_order_cnt_type > 1U ) report( "invalid pic_order_cnt_type (" , m_pic_order_cnt_type , ")" ) ;
457  if( m_seq_scaling_matrix_present_flag ) report( "seq_scaling_matrix_present_flag not yet implemented" ) ;
458  if( m_nal_hrd_parameters_present_flag ) report( "nal_hrd_parameters_present_flag not yet implemented" ) ;
459  if( m_vcl_hrd_parameters_present_flag ) report( "vcl_hrd_parameters_present_flag not yet implemented" ) ;
460 }
461 
462 unsigned int Gr::Avc::Sps::dx() const
463 {
464  // see libav's h264.c -- willfully ignores crop_left
465  unsigned int mb_width = m_pic_width_in_mbs_minus1 + 1 ;
466  const bool chroma_444 = m_chroma_format_idc == 3 ;
467  if( chroma_444 )
468  return 16U * mb_width - std::min( m_frame_crop_right_offset , 15U ) ;
469  else
470  return 16U * mb_width - 2U * std::min( m_frame_crop_right_offset , 7U ) ;
471 }
472 
473 unsigned int Gr::Avc::Sps::dy() const
474 {
475  unsigned int mb_height = m_pic_height_in_map_units_minus1 + 1U ;
476  const unsigned int chroma_y_shift = ( m_chroma_format_idc <= 1U ) ? 1U : 0U ;
477  if( m_frame_mbs_only_flag )
478  return 16U * mb_height - (1<<chroma_y_shift) * std::min( m_frame_crop_bottom_offset , (0x10>>chroma_y_shift)-1U ) ;
479  else
480  return 16U * mb_height - (2<<chroma_y_shift) * std::min( m_frame_crop_bottom_offset , (0x10>>chroma_y_shift)-1U ) ;
481 }
482 
483 void Gr::Avc::Sps::report( const std::string & a , unsigned int b , const std::string & c )
484 {
485  m_reason.append( m_reason.empty() ? "sps error: " : ": " ) ;
486  m_reason += a ;
487  if( !c.empty() )
488  {
489  m_reason += G::Str::fromUInt(b) ;
490  m_reason += c ;
491  }
492 }
493 
495 {
496  return m_reason.empty() ;
497 }
498 
499 std::string Gr::Avc::Sps::reason() const
500 {
501  return m_reason ;
502 }
503 
504 void Gr::Avc::Sps::skip_hrd_parameters( bit_stream_t & )
505 {
506  ; // TODO not implemented
507 }
508 
509 void Gr::Avc::Sps::streamOut( std::ostream & stream ) const
510 {
511  stream <<
512  "profile_idc=" << m_profile_idc << " "
513  "constraint_set0_flag=" << m_constraint_set0_flag << " "
514  "constraint_set1_flag=" << m_constraint_set1_flag << " "
515  "constraint_set2_flag=" << m_constraint_set2_flag << " "
516  "constraint_set3_flag=" << m_constraint_set3_flag << " "
517  "constraint_set4_flag=" << m_constraint_set4_flag << " "
518  "constraint_set5_flag=" << m_constraint_set5_flag << " "
519  "zero=" << m_zero << " "
520  "level_idc=" << m_level_idc << " "
521  "sequence_parameter_set_id=" << m_sequence_parameter_set_id << " "
522  "log2_max_frame_num_minus4=" << m_log2_max_frame_num_minus4 << " "
523  "pic_order_cnt_type=" << m_pic_order_cnt_type << " "
524  "log2_max_pic_order_cnt_lsb_minus4=" << m_log2_max_pic_order_cnt_lsb_minus4 << " "
525  "delta_pic_order_always_zero_flag=" << m_delta_pic_order_always_zero_flag << " "
526  "offset_for_non_ref_pic=" << m_offset_for_non_ref_pic << " "
527  "offset_for_top_to_bottom_field=" << m_offset_for_top_to_bottom_field << " "
528  "num_ref_frames_in_pic_order_cnt_cycle=" << m_num_ref_frames_in_pic_order_cnt_cycle << " "
529  "max_num_ref_frames=" << m_max_num_ref_frames << " "
530  "gaps_in_frame_num_value_allowed_flag=" << m_gaps_in_frame_num_value_allowed_flag << " "
531  "pic_width_in_mbs_minus1=" << m_pic_width_in_mbs_minus1 << " "
532  "pic_height_in_map_units_minus1=" << m_pic_height_in_map_units_minus1 << " "
533  "frame_mbs_only_flag=" << m_frame_mbs_only_flag << " "
534  "mb_adaptive_frame_field_flag=" << m_mb_adaptive_frame_field_flag << " "
535  "direct_8x8_inference_flag=" << m_direct_8x8_inference_flag << " "
536  "frame_cropping_flag=" << m_frame_cropping_flag << " "
537  "frame_crop_left_offset=" << m_frame_crop_left_offset << " "
538  "frame_crop_right_offset=" << m_frame_crop_right_offset << " "
539  "frame_crop_top_offset=" << m_frame_crop_top_offset << " "
540  "frame_crop_bottom_offset=" << m_frame_crop_bottom_offset << " "
541  "vui_parameters_present_flag=" << m_vui_parameters_present_flag << " "
542  "dx=" << dx() << " "
543  "dy=" << dy() ;
544 }
545 
546 // ==
547 
548 Gr::Avc::Pps::Pps( const std::string & pps_in , int sps_chroma_format_idc )
549 {
550  // see 14496-10 7.3.2.2, and ff_h264_decode_picture_parameter_set() in h264_ps.c
551 
552  std::string pps = Rbsp::removeByteStuffing(pps_in) ;
553  size_t rbsp_stop_bit_pos = Rbsp::findStopBit( pps ) ;
554  bit_stream_t stream( bit_iterator_t(pps.data()) , bit_iterator_t(pps.data()+pps.size()) ) ;
555 
556  m_nalu_type = stream.get_byte() & 0x1f ;
557  m_pic_parameter_set_id = stream.get_unsigned_golomb() ;
558  m_seq_parameter_set_id = stream.get_unsigned_golomb() ;
559  m_entropy_coding_mode_flag = stream.get_bool() ; // cabac vs. cavlc
560  m_bottom_field_pic_order_in_frame_present_flag = stream.get_bool() ;
561  m_num_slice_groups_minus1 = stream.get_unsigned_golomb() ;
562  if( m_num_slice_groups_minus1 > 0U )
563  {
564  ; // TODO slice group parsing
565  report( "slice group parsing not yet implemented" ) ;
566  }
567  m_num_ref_idx_10_default_active_minus1 = stream.get_unsigned_golomb() ; // ref_count[0]-1
568  m_num_ref_idx_11_default_active_minus1 = stream.get_unsigned_golomb() ; // ref_count[1]-1
569  m_weighted_pred_flag = stream.get_bool() ;
570  m_weighted_bipred_idc = stream.get(2) ;
571  m_pic_init_qp_minus26 = stream.get_signed_golomb() ; // init_qp
572  m_pic_init_qs_minus26 = stream.get_signed_golomb() ; // init_qs
573  m_chroma_qp_index_offset = stream.get_signed_golomb() ;
574  m_deblocking_filter_control_present_flag = stream.get_bool() ;
575  m_constrained_intra_pred_flag = stream.get_bool() ;
576  m_redundant_pic_cnt_present_flag = stream.get_bool() ;
577 
578  m_transform_8x8_mode_flag = false ;
579  m_pic_scaling_matrix_present_flag = false ;
580  m_second_chroma_qp_index_offset = 0 ;
581  if( stream.tellg() < rbsp_stop_bit_pos ) // more_rbsp_data() ie. before rbsp_trailing_bits()
582  {
583  G_DEBUG( "Gr::Avc: more_rbsp_data at " << stream.tellg() ) ;
584  m_transform_8x8_mode_flag = stream.get_bool() ;
585  m_pic_scaling_matrix_present_flag = stream.get_bool() ;
586  if( m_pic_scaling_matrix_present_flag )
587  {
588  if( sps_chroma_format_idc < 0 ) report( "invalid chroma format idc when parsing pps" ) ;
589  int n = 6+((sps_chroma_format_idc!=3)?2:6)*(m_transform_8x8_mode_flag?1:0) ;
590  G_DEBUG( "Gr::Avc: pic_scaling_matrix_present: chroma_format_idc=" << sps_chroma_format_idc << " n=" << n ) ;
591  for( int i = 0 ; i < n ; i++ )
592  {
593  if( stream.get_bool() ) // pic_scaling_list_present_flag
594  {
595  ; // TODO scaling_list()
596  G_DEBUG( "Gr::Avc: pic_scaling_list_present but parsing not implemented" ) ;
597  report( "pic_scaling_list_present_flag not yet implemented" ) ;
598  }
599  }
600  }
601  m_second_chroma_qp_index_offset = stream.get_signed_golomb() ;
602  }
603  G_DEBUG( "Gr::Avc: pps: tellg=" << stream.tellg() << " stopbit=" << rbsp_stop_bit_pos ) ;
604 
605  // report errors
606  if( Rbsp::hasMarkers(pps_in) ) report( "byte stuffing error" ) ;
607  if( !stream.good() ) report( "premature end of pps data" ) ;
608  if( stream.tellg() != rbsp_stop_bit_pos ) report( "parsing did not finish at the stop bit" ) ;
609  if( m_nalu_type != 8U ) report( "invalid nalu type" ) ;
610 }
611 
612 void Gr::Avc::Pps::report( const std::string & s )
613 {
614  m_reason.append( m_reason.empty() ? "pps error: " : ": " ) ;
615  m_reason.append( s ) ;
616 }
617 
619 {
620  return m_reason.empty() ;
621 }
622 
623 std::string Gr::Avc::Pps::reason() const
624 {
625  return m_reason ;
626 }
627 
628 void Gr::Avc::Pps::streamOut( std::ostream & stream ) const
629 {
630  stream <<
631  "pic_parameter_set_id=" << m_pic_parameter_set_id << " "
632  "seq_parameter_set_id=" << m_seq_parameter_set_id << " "
633  "entropy_coding_mode_flag=" << m_entropy_coding_mode_flag << " "
634  "bottom_field_pic_order_in_frame_present_flag=" << m_bottom_field_pic_order_in_frame_present_flag << " "
635  "num_slice_groups_minus1=" << m_num_slice_groups_minus1 << " "
636  "num_ref_idx_10_default_active_minus1=" << m_num_ref_idx_10_default_active_minus1 << " "
637  "num_ref_idx_11_default_active_minus1=" << m_num_ref_idx_11_default_active_minus1 << " "
638  "weighted_pred_flag=" << m_weighted_pred_flag << " "
639  "weighted_bipred_idc=" << m_weighted_bipred_idc << " "
640  "pic_init_qp_minus26=" << m_pic_init_qp_minus26 << " "
641  "pic_init_qs_minus26=" << m_pic_init_qs_minus26 << " "
642  "chroma_qp_index_offset=" << m_chroma_qp_index_offset << " "
643  "deblocking_filter_control_present_flag=" << m_deblocking_filter_control_present_flag << " "
644  "constrained_intra_pred_flag=" << m_constrained_intra_pred_flag << " "
645  "redundant_pic_cnt_present_flag=" << m_redundant_pic_cnt_present_flag << " "
646  "transform_8x8_mode_flag=" << m_transform_8x8_mode_flag << " "
647  "pic_scaling_matrix_present_flag=" << m_pic_scaling_matrix_present_flag << " "
648  "second_chroma_qp_index_offset=" << m_second_chroma_qp_index_offset ;
649 } ;
650 
651 // ==
652 
653 std::ostream & Gr::Avc::operator<<( std::ostream & stream , const Pps & pps )
654 {
655  pps.streamOut( stream ) ;
656  return stream ;
657 }
658 
659 std::ostream & Gr::Avc::operator<<( std::ostream & stream , const Avc::Sps & sps )
660 {
661  sps.streamOut( stream ) ;
662  return stream ;
663 }
664 
665 // ==
666 
667 size_t Gr::Avc::Rbsp::findStopBit( const std::string & s , size_t fail )
668 {
669  size_t pos = s.find_last_not_of( std::string(1U,'\0') ) ; // allow for cabac_zero_word
670  if( pos == std::string::npos )
671  return fail ; // fail if empty or all zeros
672 
673  unsigned int c = static_cast<unsigned char>(s.at(pos)) ;
674  size_t result = (pos+1U) * 8U - 1U ;
675  for( ; !(c&1U) ; result-- ) c >>= 1 ;
676  return result ;
677 }
678 
679 const std::string & Gr::Avc::Rbsp::_000()
680 {
681  static const std::string s( "\x00\x00\x00" , 3U ) ;
682  return s ;
683 }
684 
685 const std::string & Gr::Avc::Rbsp::_001()
686 {
687  static const std::string s( "\x00\x00\x01" , 3U ) ;
688  return s ;
689 }
690 
691 const std::string & Gr::Avc::Rbsp::_002()
692 {
693  static const std::string s( "\x00\x00\x02" , 3U ) ;
694  return s ;
695 }
696 
697 const std::string & Gr::Avc::Rbsp::_0001()
698 {
699  static const std::string s( "\x00\x00\x00\x01" , 4U ) ;
700  return s ;
701 }
702 
703 std::string Gr::Avc::Rbsp::removeByteStuffing( const std::string & s )
704 {
705  std::string result( s ) ;
706  G::Str::replaceAll( result , std::string("\x00\x00\x03",3U) , std::string("\x00\x00",2U) ) ;
707  return result ;
708 }
709 
710 void Gr::Avc::Rbsp::addByteStuffing( std::string & s )
711 {
712  static const std::string _00( "\0\0" , 2U ) ;
713  size_t pos = 0U ;
714  for(;;)
715  {
716  pos = s.find( _00 , pos ) ;
717  if( pos == std::string::npos || (pos+2U) >= s.length() )
718  break ;
719  char c = s.at(pos+2U) ;
720  if( c == '\x00' || c == '\x01' || c == '\x02' || c == '\x03' )
721  {
722  s.insert( pos+2U , 1U , '\x03' ) ;
723  pos += 4U ;
724  }
725  }
726 }
727 
728 std::string Gr::Avc::Rbsp::byteStuffed( const std::string & s )
729 {
730  std::string result( s ) ;
731  addByteStuffing( result ) ;
732  return result ;
733 }
734 
735 bool Gr::Avc::Rbsp::hasMarkers( const std::string & s )
736 {
737  return
738  s.find(_000()) != std::string::npos ||
739  s.find(_001()) != std::string::npos ||
740  s.find(_002()) != std::string::npos ;
741 }
742 
743 /// \file gravc.cpp
const std::vector< std::string > & spsList() const
Returns a list of binary strings for the sps structures.
Definition: gravc.cpp:261
Sps(const std::string &binary_sps_buffer)
Constructor taking a binary byte-stuffed sps buffer.
Definition: gravc.cpp:311
static Configuration fromAvcc(const std::string &binary_string)
Factory function taking an "avcC" binary string, including RBSP byte-stuffing (but no start code) – s...
Definition: gravc.cpp:55
g_uint16_t get_word()
Gets a word. Sets the fail state on underflow.
Definition: gbitstream.h:189
void streamOut(std::ostream &) const
Streams out the sps info.
Definition: gravc.cpp:509
size_t spsCount() const
Returns sps().size().
Definition: gravc.cpp:271
bool good() const
Returns !fail().
Definition: gbitstream.h:220
Synopsis:
static const std::string & _002()
Returns the 00-00-02 string.
Definition: gravc.cpp:691
Contains AVC configuration parameters, initialised from an "avcC" file segment or from an SDP "fmtp" ...
Definition: gravc.h:93
size_t ppsCount() const
Returns pps().size().
Definition: gravc.cpp:276
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
Pps(const std::string &binary_pps_buffer, int sps_chroma_format_idc)
Constructor taking a binary byte-stuffed pps buffer.
Definition: gravc.cpp:548
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:868
void streamOut(std::ostream &) const
Streams out the pps info.
Definition: gravc.cpp:628
unsigned int get_unsigned_golomb()
Gets an exp-golomb encoded unsigned value. Sets the fail state on underflow.
Definition: gbitstream.h:208
bool valid() const
Returns true if a usable object.
Definition: gravc.cpp:618
unsigned int dx() const
Returns the picture width in pixels.
Definition: gravc.cpp:462
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1051
A Picture Sequence Parameter Set (PPS) structure.
Definition: gravc.h:262
A Sequence Parameter Set (SPS) structure.
Definition: gravc.h:187
unsigned char get_byte()
Gets a byte. Sets the fail state on underflow.
Definition: gbitstream.h:183
unsigned char get(int bits)
Gets a number of bits, returned in a byte.
Definition: gbitstream.h:177
int get_signed_golomb()
Gets an exp-golomb encoded signed value. Sets the fail state on underflow.
Definition: gbitstream.h:201
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.cpp:315
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
Definition: gstr.cpp:157
static std::string head(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1037
static const std::string & _001()
Returns the 00-00-01 string.
Definition: gravc.cpp:685
A class for pulling integer values of various widths out of a bit stream.
Definition: gbitstream.h:55
static Configuration fromFmtp(const std::string &fmtp)
Factory function taking a SDP (Session Description Protocol) "fmtp" attribute string, something like "profile-level-id=...; ...; sprop-parameters-sets=Z00AKZpmA8==,aO48gA==".
Definition: gravc.cpp:50
std::string reason() const
Returns the in-valid() reason.
Definition: gravc.cpp:251
A bit-by-bit input iterator that extracts bits in msb-to-lsb order from a sequence of bytes...
Definition: gbititerator.h:42
static std::string removeByteStuffing(const std::string &s)
Removes byte stuffing from a byte-stuffed RBSP buffer.
Definition: gravc.cpp:703
size_t tellg() const
Returns the current bit offset.
Definition: gbitstream.h:239
unsigned int dy() const
Returns the picture height in pixels.
Definition: gravc.cpp:473
bool valid() const
Returns true if a usable object.
Definition: gravc.cpp:246
bool valid() const
Returns true if a usable object.
Definition: gravc.cpp:494
static std::string byteStuffed(const std::string &s)
Returns a byte-stuffed version of the given unstuffed RBSP buffer.
Definition: gravc.cpp:728
const Pps & pps(size_t i) const
Returns a reference to the i-th pps structure.
Definition: gravc.cpp:286
bool get_bool()
Extracts a boolean. Sets the fail state on underflow.
Definition: gbitstream.h:165
static bool hasMarkers(const std::string &s)
Returns true if the string contains one of the inter-RBSP markers (000, 001, 002).
Definition: gravc.cpp:735
g_uint32_t get_dword()
Gets a dword. Sets the fail state on underflow.
Definition: gbitstream.h:195
static std::string decode(const std::string &)
Decodes the given string.
Definition: gbase64.cpp:131
std::string reason() const
Returns the in-valid() reason.
Definition: gravc.cpp:623
static void addByteStuffing(std::string &s)
Adds byte-stuffing to the given unstuffed RBSP buffer.
Definition: gravc.cpp:710
const Sps & sps(size_t i) const
Returns a reference to the i-th sps structure.
Definition: gravc.cpp:281
const std::vector< std::string > & ppsList() const
Returns a list of binary strings for the pps structures.
Definition: gravc.cpp:266
static size_t findStopBit(const std::string &s, size_t fail=0U)
Returns the bit offset of the RBSP stop bit.
Definition: gravc.cpp:667
std::string reason() const
Returns the in-valid() reason.
Definition: gravc.cpp:499
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
static const std::string & _000()
Returns the 00-00-00 string.
Definition: gravc.cpp:679
unsigned int nalu_length_size() const
Returns the size of the nalu length values, typically 2.
Definition: gravc.cpp:256
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &seperators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:921
static const std::string & _0001()
Returns the 00-00-00-01 string.
Definition: gravc.cpp:697
Configuration()
A default constructor for an in-valid() object.
Definition: gravc.cpp:65