ffmpeg 解码H264裸数据流

相信很多人在刚接触ffmpeg的时候,想要ffmpeg的api都觉得很比较繁琐,因为本身代码量比较大,模块比较多,api也比较多,而且在ffmpeg中的例程都是以文件的行驶传入到编解码器重的,所以想实现简单的纯数据流解码就感觉无从下手了;本文就是描述将一帧完整的H264数据解码为yuyv格式的数据。

ffmpeg版本:ffmpeg-3.1.2

用到的ffmpeg库有:libavformat、libavcodec、libavutil

  1. 对外提供的接口有:
        //初始化解码器,codec_id为选择的解码器
    
        int InitDecoder(int codec_id);
    
        //入参:framedata、framelen为H264数据源和数据长度
    
        //出参:outputframe为解码后的yuyv数据存放区,width/height/pixfmt返回视频宽/高/解码后的数据格式,
    
        int DecodeFrame(unsigned char * framedata , int framelen ,//input
                               unsigned char *outputframe , int *width , int *height , int *pixfmt);//output
    
        //参数意义同上一接口,作用是将解码器中的缓存数据刷出
    
        int DecodeFrameFlush(unsigned char *outputframe , int *width , int *height , int *pixfmt);//output
    
        //清理解码器申请的资源
    
        void UninitDecoder(void);
    

  2. 头文件和变量:
    #include 
    #include 
    #include 
    #include 
    #include "socket_lib.h"
    
    static AVCodec *codec = NULL;
    static AVCodecContext *codec_ctx= NULL;
    static AVFrame *frame = NULL;
    static AVPacket avpkt;
    static bool codecInited = false;

  3. 解码器初始化:
    int InitDecoder(int codec_id)
    {
    	if(codecInited != false)
    	{
    		printf("decoder inited fail\n");
    		return -1;
            }
            avcodec_register_all();
    	codec = avcodec_find_decoder(codec_id);
            if(!codec)
    	{
    		printf("avcodec_find_decoder fail\n");
    		return -1;
            }
    	codec_ctx = avcodec_alloc_context3(codec);
            if(!codec_ctx)
    	{
    		printf("avcodec_alloc_context3 fail\n");
    		return -1;
            }/*
    	else
    	{
    		codec_ctx->time_base.num = 1;
    		codec_ctx->frame_number = 1; //每包一个视频帧	
    		codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
    		codec_ctx->bit_rate = 0;
    		codec_ctx->time_base.den = den;//帧率
    		codec_ctx->width = width;//视频宽
    		codec_ctx->height = height;//视频高
    	}*/
    	if(avcodec_open2(codec_ctx, codec, NULL) < 0)
    	{
    		printf("avcodec_open2 fail\n");
    		return -1;
    	}
    	frame = av_frame_alloc();
    	if(!frame)
    	{
    		printf("av_frame_alloc fail\n");
    		return -1;
    	}	
    	av_init_packet(&avpkt);
    	codecInited = true;
    	return 0;
    }

  4. 解码一帧H264视频:
    1. 在成功解码后这个接口会返回这个视频的宽、高、解码后的数据格式(yuv420或者yuv422等等)
      int DecodeFrame(unsigned char * framedata , int framelen ,//input
      		unsigned char *outputframe , int *width , int *height , int *pixfmt)//output
      {
      	avpkt.size = framelen;
      	avpkt.data = framedata;
      	while(avpkt.size > 0)
      	{
      		if(decode_frame(codec_ctx, frame, &avpkt, 0, outputframe) < 0)
      		{
      			printf("%s decode fail\n",__func__);
      			return -1;
      		}
      	}
      	*width = codec_ctx->width;
      	*height = codec_ctx->height;
      	*pixfmt = codec_ctx->pix_fmt;
      	return 0;
      }

    2. 解码一帧详细操作(这里只是做了YUV422P的处理):
      static int decode_frame(AVCodecContext *avctx,AVFrame *frame,//input
      				AVPacket *pkt, int last,//input
      				unsigned char *outputframe)//output
      {
              int len = 0;
      	int got_frame = 0;
      	if(!codecInited)
      	{
      		printf("%s decoder uninted\n",__func__);
      		return -1;
              }
      	len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);
              if(len < 0)
      	{
      		printf("Error while decoding frames\n");
      		return len;
              }	
              if(got_frame)
      	{
      		switch(avctx->pix_fmt)
      		{
      			case AV_PIX_FMT_YUV422P:
      			{
      				int index = 0;
      				int y_i  = 0 , u_i = 0 , v_i = 0;
      				for(index = 0 ; index < frame->width*frame->height*2 ;)
      				{
      					outputframe[index++] = frame->data[0][y_i++];
      					outputframe[index++] = frame->data[1][u_i++];
      					outputframe[index++] = frame->data[0][y_i++];
      					outputframe[index++] = frame->data[2][v_i++];
      				}
      			}break;
      			default:
      			{
      				return -1;
      			}
      		}
              }
              if(pkt->data)
      	{
                  pkt->size -= len;
                  pkt->data += len;
              }
              return 0;
      }
    3. 因为avcodec_decode_video2这个接口被ffmpeg所摒弃的,可以用新的接口://20180329 add
      int DecodeFrame(unsigned char * framedata, int framelen,//input
      	unsigned char *outputframe, int *width, int *height, int *pixfmt)//output
      {
      	avpkt.size = framelen;
      	avpkt.data = framedata;
      
      	if (avcodec_send_packet(codec_ctx, &avpkt))
      	{
      	        printf("%s %d avcodec_send_packet fail\n",__func__,__LINE__);
      		return -1;
      	}
      	if(avcodec_receive_frame(codec_ctx, frame))
      	{
      	        printf("%s %d avcodec_receive_frame fail\n", __func__, __LINE__);
      		return -1;
      	}
              switch (codec_ctx->pix_fmt)
              {
                  case AV_PIX_FMT_YUV422P:
                  {
                      int index = 0;
                      int y_i = 0, u_i = 0, v_i = 0;
                      for (index = 0; index < frame->width*frame->height * 2;)
                      {
                          outputframe[index++] = frame->data[0][y_i++];
                          outputframe[index++] = frame->data[1][u_i++];
                          outputframe[index++] = frame->data[0][y_i++];
                          outputframe[index++] = frame->data[2][v_i++];
                      }
                  }break;
                  default:
                  {
                      printf("default format:%d\n", codec_ctx->pix_fmt);
                      return -1;
                  }
              }
          *width = codec_ctx->width;
          *height = codec_ctx->height;
          *pixfmt = codec_ctx->pix_fmt;
          return 0;
      }
  5. 解码完成后,刷出解码器中的数据:
    int DecodeFrameFlush(unsigned char *outputframe , int *width , int *height , int *pixfmt)//output
    {
        if(!codecInited)
        {
            printf("%s decoder uninted\n",__func__);
            return -1;
        }
        avpkt.data = NULL;
        avpkt.size = 0;
        if(decode_frame(codec_ctx, frame, &avpkt, 1, outputframe) < 0)
            return -1;
        *width = codec_ctx->width;
        *height = codec_ctx->height;
        *pixfmt = codec_ctx->pix_fmt;
        return 0;
    }

  6. 释放解码器的资源:
    void UninitDecoder(void)
    {
    	if(codecInited)
    	{
    	    avcodec_close(codec_ctx);
    	    av_free(codec_ctx);
    	    av_frame_free(&frame);
    	    codecInited = false;
    	}
    }

     这里有相关资源的下载:

    http://download.csdn.net/download/xushan239/10199075

    http://download.csdn.net/download/xushan239/10190449

你可能感兴趣的:(ffmpeg 解码H264裸数据流)