关于ffmpeg分离mp4视频帧硬解码遇到的问题

    前段时间在WINCE6搞好一阵时间利用ffmpeg分离mp4出来的视频帧送到硬件解码去,但发现不少问题,我用的是三星的S5PC100处理器,想利用三星提供的MFC的API来进行硬解码,但是根本无法解码,根据调试输出的信息,少了信息头数据,上网查找了好久后,发现mp4/mkv/mov/flv封装的h.264,因为为了减少存储,减少了一些头信息,不是标准的ES流,而从av_read_frame出来的码流一般都是完整的一帧数据的

 

      经过研究的ffmpeg的原代码发现,一般视频帧的信息头数据都会保存在AVCodecContext结构中的extradata ,extradata_size的变量中,  如果是MPEG4编码的话,直接把extradata加在经过av_read_frame分离的视频数据帧前面就可以硬解码了,若是H264的话,我是运用如下函数得到标准ES流的.

 #define EINVAL          22
#define ENOMEM          12
#define AV_RB16(x)  ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1])
#define AV_RB32(x)  ((((const uint8_t*)(x))[0] << 24) | /
 (((const uint8_t*)(x))[1] << 16) | /
 (((const uint8_t*)(x))[2] <<  8) | /
 ((const uint8_t*)(x))[3])

#define AV_RB32(x)  ((((const uint8_t*)(x))[0] << 24) | /
 (((const uint8_t*)(x))[1] << 16) | /
 (((const uint8_t*)(x))[2] <<  8) | /
 ((const uint8_t*)(x))[3])

#define AV_WB32(p, d) do { /
 ((uint8_t*)(p))[3] = (d); /
 ((uint8_t*)(p))[2] = (d)>>8; /
 ((uint8_t*)(p))[1] = (d)>>16; /
 ((uint8_t*)(p))[0] = (d)>>24; } while(0)

 

typedef struct H264BSFContext {
 uint8_t  length_size;
 uint8_t  first_idr;
 uint8_t *sps_pps_data;
 uint32_t size;
} H264BSFContext;

 

static void alloc_and_copy(uint8_t **poutbuf,          int *poutbuf_size,
         const uint8_t *sps_pps, uint32_t sps_pps_size,
         const uint8_t *in,      uint32_t in_size)
{
 uint32_t offset = *poutbuf_size;
 uint8_t nal_header_size =  4;

 *poutbuf_size += sps_pps_size+in_size+nal_header_size;
 *poutbuf = (unsigned char *)av_realloc(*poutbuf, *poutbuf_size);
    if (*poutbuf==NULL)
        return ;
   
 if (sps_pps)
  memcpy(*poutbuf+offset, sps_pps, sps_pps_size);
 memcpy(*poutbuf+sps_pps_size+nal_header_size+offset, in, in_size);
 //if (!offset)
  AV_WB32(*poutbuf+offset+sps_pps_size, 1);
 //else
 //{
 // (*poutbuf+offset+sps_pps_size)[0] = (*poutbuf+offset+sps_pps_size)[1] = 0;
 // (*poutbuf+offset+sps_pps_size)[2] = 1;
 //}
}

static int h264_mp4toannexb_filter(H264BSFContext *ctx,  AVCodecContext *avctx, const char *args,
           uint8_t  **poutbuf, int *poutbuf_size,
           const uint8_t *buf, int      buf_size,
           int keyframe)
{

 uint8_t unit_type;
 uint32_t nal_size, cumul_size = 0;

 /* nothing to filter */
 if (!avctx->extradata || avctx->extradata_size < 6)
 {
  *poutbuf = (uint8_t*) buf;
  *poutbuf_size = buf_size;
  return 0;
 }

 /* retrieve sps and pps NAL units from extradata */
 if (!ctx->sps_pps_data)
 {
  uint16_t unit_size;
  uint32_t total_size = 0;
  uint8_t *out = NULL, unit_nb, sps_done = 0;
  const uint8_t *extradata = avctx->extradata+4;
  static const uint8_t nalu_header[4] = {0, 0, 0, 1};

  /* retrieve length coded size */
  ctx->length_size = (*extradata++ & 0x3) + 1;
  if (ctx->length_size == 3)
   return AVERROR(EINVAL);

  /* retrieve sps and pps unit(s) */
  unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
  if (!unit_nb)
  {
   unit_nb = *extradata++; /* number of pps unit(s) */
   sps_done++;
  }
  while (unit_nb--)
  {
   unit_size = AV_RB16(extradata);
   total_size += unit_size+4;
   if (extradata+2+unit_size > avctx->extradata+avctx->extradata_size)
   {
    av_free(out);
    return AVERROR(EINVAL);
   }
   out = (unsigned char *)av_realloc(out, total_size);
   if (!out)
    return AVERROR(ENOMEM);
   memcpy(out+total_size-unit_size-4, nalu_header, 4);
   memcpy(out+total_size-unit_size,   extradata+2, unit_size);
   extradata += 2+unit_size;

   if (!unit_nb && !sps_done++)
    unit_nb = *extradata++; /* number of pps unit(s) */
  }

  ctx->sps_pps_data = out;
  ctx->size = total_size;
  ctx->first_idr = 1;
  //char str[512]={0};
  //                           memcpy(str, out, total_size);
 }
 *poutbuf_size = 0;
 *poutbuf = NULL;
 do
 {
  if (ctx->length_size == 1)
   nal_size = buf[0];
  else if (ctx->length_size == 2)
   nal_size = AV_RB16(buf);
  else
  {
   nal_size = AV_RB32(buf);

   
  }

  buf += ctx->length_size;
  unit_type = *buf & 0x1f;

  /* prepend only to the first type 5 NAL unit of an IDR picture */
  if ( unit_type != 6 )
  {
   alloc_and_copy(poutbuf, poutbuf_size, ctx->sps_pps_data, ctx->size, buf, nal_size);
   ctx->first_idr = 0;
  }
  //else
  //{
  // alloc_and_copy(poutbuf, poutbuf_size, NULL, 0,  buf, nal_size);
  // if (!ctx->first_idr && unit_type == 1)
  //  ctx->first_idr = 1;
  //}

  buf += nal_size;
  cumul_size += nal_size + ctx->length_size;
 } while (cumul_size < buf_size);

 return 1;
}

 

通过调用 h264_mp4toannexb_filter函数后就可以得到标准的ES流了,先定义一个H264BSFContext变量,然后传给h264_mp4toannexb_filter函数,

如: 

 H264BSFContext *ctx=(H264BSFContext *) malloc(sizeof(H264BSFContext));
  memset(ctx, 0, sizeof(H264BSFContext));

  while(av_read_frame(pFormatCtx, &packet) >= 0)
  {    

       if(packet.stream_index==videoStream)
      { 
 
          unsigned char *lp=NULL;   //输入的指针
           int len;
           h264_mp4toannexb_filter(ctx, pCodecCtx, NULL, &lp, &len, packet.data, packet.size, 1);
           memcpy((LPBYTE)virInBuf, pCodecCtx->extradata, pCodecCtx->extradata_size);

       }

  }

硬件就可能解码了,这个我是在S5PC100测试成功的,如果是S3C6410应该是类似的,如果是avi文件很容易了,通过av_read_frame直接把它送硬件解码器进行解码就行了

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值