zoukankan      html  css  js  c++  java
  • 解密硬件解码关键技术

    通过上一篇文章,我们用ffmpeg分离出一个多媒体容器中的音视频数据,但是很可能这些数据是不能被正确解码的。为什么呢?因为在解码这些数据之前,需要对解码器做一些配置,典型的就是目前流行的高清编码“黄金搭档”组合H264 + AAC的搭配。本文将讲述H264和AAC的关键解码配置参数的解析,如果没有这些配置信息,数据帧往往不完整,导致了解码器不能解码。

    • H264的配置信息解析

      前面我们知道,ffmpeg的avformat_find_stream_info函数可以取得音视频媒体多种,比如播放持续时间、音视频压缩格式、音轨信息、字幕信息、帧率、采样率等。在信息结果中有一项扩展数据描述(avcodec.h文件中):

              AVCodecContext定义如下:

        如果视频流是H264,这个extradate里面就包含了H264的配置信息,这个扩展数据有如下定义:

        详细解释可以参考“ISO-14496-15 AVC file format”文档。里面最重要的就是NAL长度和SPS,PPS数据和对应的长度信息。对该数据的解析在ffmpeg里面有现成的函数:ff_h264_decode_extradata,在我的项目里面是自己写的扩展数据解析。

    • AAC的配置信息解析及设置

      如果音频数据是AAC流,在解码时需要ADTS(Audio Data Transport Stream)头部,不管是容器封装还是流媒体,没有这个,一般都是不能播放的。很多朋友在做AAC流播放时遇到播不出声音,很可能就是这个原因导致。

      ADTS所需的数据仍然是放在上面的扩展数据extradata中,我们需要先解码这个扩展数据,然后再从解码后的数据信息里面重新封装成ADTS头信息,加到每一帧AAC数据之前再送解码器,这样就可以正常解码了。

      extradate数据定义如下:

       

          详细信息及说明请参考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。里面最重要的部分有采样频率、通道配置和音频对象类型,这几个一般都是AAC解码器需要的配置参数。

          这个数据在ffmpeg中也有相应的解码函数:avpriv_aac_parse_header。在我的项目中,我没有使用这个函数,而是自己实现的:

    • 1
      2
      3
      4
      5
      6
      7
      typedef struct
      {
            int write_adts;
            int objecttype;
            int sample_rate_index;
            int channel_conf;
      }ADTSContext;

        

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      int aac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize)
      {
            int aot, aotext, samfreindex;
            int i, channelconfig;
            unsigned char *p = pbuf;
          
            if (!adts || !pbuf || bufsize<2)
            {
                  return -1;
            }
            aot = (p[0]>>3)&0x1f;
            if (aot == 31)
            {
                  aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;
                  aot = 32 + aotext;
                  samfreindex = (p[1]>>1) & 0x0f;
                    
                  if (samfreindex == 0x0f)
                  {
                        channelconfig = ((p[4]<<3) | (p[5]>>5)) & 0x0f;
                  }
                  else
                  {
                        channelconfig = ((p[1]<<3)|(p[2]>>5)) & 0x0f;
                  }
            }
            else
            {
                  samfreindex = ((p[0]<<1)|p[1]>>7) & 0x0f;
                  if (samfreindex == 0x0f)
                  {
                        channelconfig = (p[4]>>3) & 0x0f;
                  }
                  else
                  {
                        channelconfig = (p[1]>>3) & 0x0f;
                  }
            }
          
      #ifdef AOT_PROFILE_CTRL
            if (aot < 2) aot = 2;
      #endif
            adts->objecttype = aot-1;
            adts->sample_rate_index = samfreindex;
            adts->channel_conf = channelconfig;
            adts->write_adts = 1;
          
            return 0;
      }

        

            上面的pbuf就是extradata。

          接下来,再用ADTSContext数据编码为ADTS头信息插入每一个AAC帧前面:

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)
      {      
            unsigned char byte;
          
            if (size < ADTS_HEADER_SIZE)
            {
                  return -1;
            }
              
            buf[0] = 0xff;
            buf[1] = 0xf1;
            byte = 0;
            byte |= (acfg->objecttype & 0x03) << 6;
            byte |= (acfg->sample_rate_index & 0x0f) << 2;
            byte |= (acfg->channel_conf & 0x07) >> 2;
            buf[2] = byte;
            byte = 0;
            byte |= (acfg->channel_conf & 0x07) << 6;
            byte |= (ADTS_HEADER_SIZE + size) >> 11;
            buf[3] = byte;
            byte = 0;
            byte |= (ADTS_HEADER_SIZE + size) >> 3;
            buf[4] = byte;
            byte = 0;
            byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;
            byte |= (0x7ff >> 6) & 0x1f;
            buf[5] = byte;
            byte = 0;
            byte |= (0x7ff & 0x3f) << 2;
            buf[6] = byte;
          
            return 0;
      }

        

      这个头部是固定的7字节长度,所以可提前空出这7个字节供ADTS占用。

        通过以上对H264和AAC的扩展数据处理,播放各种“黄金搭档”的多媒体文件、流媒体、视频点播等都应该没有问题了。

        想第一时间获得更多原创文章,请关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或者搜索微信号coder_online即可关注,里面有大量Android,Chromium,Linux等相关文章等着您,我们还可以在线交流。

                                                                               

      摘自:http://my.oschina.net/u/2336532/blog/400790

  • 相关阅读:
    什么是 bean 的自动装配?
    什么是 Spring 的内部 bean?
    什么是 Spring 的 MVC 框架?
    Spring AOP and AspectJ AOP 有什么区别?
    解释 JDBC 抽象和 DAO 模块?
    volatile 类型变量提供什么保证?
    一个 Spring Bean 定义 包含什么?
    什么是 Spring MVC 框架的控制器?
    使用 Spring 访问 Hibernate 的方法有哪些?
    什么是 Callable 和 Future?
  • 原文地址:https://www.cnblogs.com/lichwei/p/4426373.html
Copyright © 2011-2022 走看看