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 <libavutil/imgutils.h>
    #include <libavutil/samplefmt.h>
    #include <libavutil/timestamp.h>
    #include <libavformat/avformat.h>
    #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
  • 5
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
要使用FFmpeg库将H.264流文件解码为RGBA格式,可以使用以下C代码示例: ```c #include <libavutil/imgutils.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> // 解码H.264流文件为RGBA格式 int decode_h264_to_rgba(const char* file_name, uint8_t** rgba_data, int* width, int* height) { AVFormatContext* format_ctx = NULL; AVCodecContext* codec_ctx = NULL; AVCodec* codec = NULL; AVFrame* frame = NULL; AVPacket pkt; struct SwsContext* sws_ctx = NULL; int ret, got_frame; // 注册FFmpeg库 av_register_all(); // 打开输入文件 if (avformat_open_input(&format_ctx, file_name, NULL, NULL) != 0) { return -1; // 打开文件失败 } // 查找流信息 if (avformat_find_stream_info(format_ctx, NULL) < 0) { return -1; // 查找流信息失败 } // 获取视频流索引 int video_stream_index = -1; for (int i = 0; i < format_ctx->nb_streams; i++) { if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if (video_stream_index == -1) { return -1; // 未找到视频流 } // 获取视频解码器参数 AVCodecParameters* codec_params = format_ctx->streams[video_stream_index]->codecpar; // 查找视频解码器 codec = avcodec_find_decoder(codec_params->codec_id); if (!codec) { return -1; // 未找到解码器 } // 创建解码器上下文 codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { return -1; // 创建解码器上下文失败 } // 初始化解码器上下文参数 if (avcodec_parameters_to_context(codec_ctx, codec_params) < 0) { return -1; // 初始化解码器上下文参数失败 } // 打开解码器 if (avcodec_open2(codec_ctx, codec, NULL) < 0) { return -1; // 打开解码器失败 } // 创建帧对象 frame = av_frame_alloc(); if (!frame) { return -1; // 创建帧对象失败 } // 初始化数据包 av_init_packet(&pkt); // 读取并解码视频帧 while (av_read_frame(format_ctx, &pkt) >= 0) { if (pkt.stream_index == video_stream_index) { ret = avcodec_send_packet(codec_ctx, &pkt); if (ret < 0) { break; // 解码失败 } while (ret >= 0) { ret = avcodec_receive_frame(codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { break; // 解码失败 } // 分配RGBA内存 int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, frame->width, frame->height, 1); *rgba_data = (uint8_t*)av_malloc(num_bytes * sizeof(uint8_t)); // 设置RGBA内存指针 av_image_fill_arrays(frame->data, frame->linesize, *rgba_data, AV_PIX_FMT_RGBA, frame->width, frame->height, 1); // 转换帧数据到RGBA格式 sws_ctx = sws_getContext(frame->width, frame->height
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值