使用ffmpeg的lib库解码H264/H265

ffmpeg的开源代码自行在ffmpeg的官方下载(http://ffmpeg.org/)

编译

编译之前需要先安装gcc编译器,本文使用的是arm-linux的交叉编译器,这里简称arm-linux-gcc

本文使用的版本是 ffmpeg-3.3.3,下载得到ffmpeg-3.3.3.tar.bz2

mkdir dist
tar xvf ffmpeg-3.3.3.tar.bz2
cd ffmpeg-3.3.3

./configure --cc="arm-linux-gcc" --cxx="arm-linux-g++" --ar="arm-linux-ar" --prefix=$(pwd)/../dist --enable-cross-compile --target-os=none --arch=x86_32 --cpu=generic \
 --enable-gpl --enable-version3 --disable-avdevice --disable-avformat --disable-swresample --disable-postproc --disable-avfilter \
 --disable-programs --disable-logging --disable-everything --enable-decoder=hevc --enable-decoder=h264 \
 --disable-ffplay --disable-ffprobe --disable-ffserver --disable-asm --disable-doc --disable-devices --disable-network \
 --disable-hwaccels --disable-parsers --disable-bsfs --disable-debug --disable-protocols --disable-indevs --disable-outdevs 

make
make install

configure命令的使用可以输入./configure –help查看,根据具体需求启用/禁用相关选项。为了裁剪代码,本文使用的编译选项只开启h264和h265的解码,其他功能禁用掉。编译生成静态lib库*.a,不生成可执行程序,可以方便在demo程序中直接调用。

以上命令都执行完之后,在dist目录中会生成libavcodec.a和libavutil.a

api使用

需要用到的api

void avcodec_register_all(void);
AVCodec *avcodec_find_decoder(enum AVCodecID id);
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
void av_free(void *ptr);
AVFrame *av_frame_alloc(void);
int avcodec_close(AVCodecContext *avctx);
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                         int *got_picture_ptr,
                         const AVPacket *avpkt);

在demo程序调用api

这里只写基本框架

AVCodec * m_codec = NULL;
AVCodecContext * m_context = NULL;
AVFrame *m_pic = NULL;

//初始化解码器
int ffmpeg_init(AVCodec *m_codec, AVCodecContext *m_context, AVFrame *m_pic,int AV_CODEC_ID)
{
avcodec_register_all();
// AV_CODEC_ID H264是AV_CODEC_ID_H264, H265是AV_CODEC_ID_HEVC, 具体看ffmpeg源码中的宏定义
m_codec = avcodec_find_decoder(AV_CODEC_ID);
m_context = libffmpeg.avcodec_alloc_context3(m_codec);
int avcodecopenRes = avcodec_open2(m_context, m_codec, 0);
if (avcodecopenRes < 0){
  if (m_context){
   av_free(m_context);
   m_context = NULL;
   return -1;
  }
}
m_pic = av_frame_alloc();
if (!m_pic){
   if (m_context){
     avcodec_close(m_context);
     av_free(m_context);
     m_context = NULL;
   }
    return -1;
}
return 0;
}

//解码, 把原始的H264/H265视频帧数据传入,得到解码后的YUV数据
int ffmpeg_decode(AVCodecContext *m_context, AVFrame *m_pic,unsigned char *avpkt_data,int avpkt_size)
{
AVPacket avpkt;
memset(&avpkt,0,sizeof(AVPacket));
avpkt.data = avpkt_data;
avpkt.size = avpkt_size;
int got_picture_ptr=0;
int ret_value=0;
ret_value=avcodec_decode_video2(m_context, m_pic, &got_picture_ptr,&avpkt);
if(ret_value>0 && got_picture_ptr>0){
  return 0;
 }
 else{
  return -1;
 }
}

/*
解码后的yuv数据在AVFrame *m_pic之中
m_pic->width 表示视频画面的像素宽度
m_pic->height 表示视频画面的像素高度
m_pic->format 表示yuv的格式,如比YUV420、YUV422、YUV444等,具体看ffmpeg源码中的枚举enum AVPixelFormat已经相关宏定义
m_pic->data[3] 表示解码后的yuv数据中元素data[0]、data[1]、data[2]分别表示Y、U、V
m_pic->linesize[3] 表示解码后的Y、U、V数据每行的size,linesize[0]、linesize[1]、linesize[2]分别表示Y、U、V
*/

//释放解码器
void ffmpeg_free(AVCodec *m_codec, AVCodecContext *m_context, AVFrame *m_pic)
{
    if (m_codec && m_context){
      m_codec->close(m_context);
      m_codec = NULL;
    }
    if (this.m_context)
    {
      avcodec_close(m_context);
      av_free(m_context);
    }
    if (m_pic)
    {
      av_free(m_pic);
    }
    m_pic = NULL;
    m_codec = NULL;
    m_context = NULL;
}