您的位置:首页 > 其它

利用ffmpeg解码h264裸流并存储成YUV420

2017-07-04 10:18 507 查看
此处用的ffmpeg版本为3.2.2。

例子是在linux下所写的,大致流程如下:

初始化ffmpeg库

创建YUV文件,用于存储解码后的YUV数据

初始化H264解码器

给解码器的一些结构变量赋值

打开解码器

打开H264裸流文件

读取一定数据的h264数据(因为不知道一帧到底有多大)

调用ffmpeg函数,循环分析读取到的数据,每循环一次得到一帧数据,然后调用解码器解码,并存储成YUV420文件。直到分析完读入的数据

释放内存,解码结束

编译命令如下(有些库非必须):

gcc -o codec codec.c -lavdevice -lavformat -lavfilter -lpostproc -lavcodec -lswresample -lswscale -lavutil -lpthread -ldl -lxml2 -lz -lx264 -ldl -lm

具体代码:

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <sys/time.h>

#define INBUF_SIZE 40960000
FILE *yuv_fp = NULL;

int main(int argc, int argv)
{
//解码后,存储的文件,测试文件分辨率是640x480
yuv_fp = fopen("test_640x360.yuv","wb");
if(NULL == yuv_fp)
{
fprintf(stderr,"Open test.yuv fail\n");
exit(1);
}

avcodec_register_all();
AVCodec *codec;
AVCodecContext *c = NULL;
AVFrame *frame;
AVCodecParserContext *avParserContext;
FILE *f = NULL;
int frame_count = 0;
//读取264文件后所存储的buf
unsigned char * inbuf = (unsigned char*)malloc(INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
int got_frame;
int read_size;

memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if( NULL == codec )
{
fprintf(stderr,"Codec not found\n");
exit(1);
}

c = avcodec_alloc_context3(codec);
if(NULL == c)
{
fprintf(stderr,"Could not allocate video codec context\n");
exit(1);
}

//初始化解码器所需要的参数,其实不设置也是可以正常解码的,因为264流中有对应的pps,sps相关结构
//里面包含解码所需要的相关信息
c->width = 640;
c->height = 360;
c->bit_rate = 1000;
c->time_base.num = 1;
c->time_base.den = 25;
c->codec_id = AV_CODEC_ID_H264;
c->codec_type = AVMEDIA_TYPE_VIDEO;

avParserContext = av_parser_init(AV_CODEC_ID_H264);
if( NULL == avParserContext)
{
fprintf(stderr,"Could not init avParserContext\n");
exit(1);
}

if(avcodec_open2(c,codec, NULL) < 0)
{
fprintf(stderr,"Could not open codec\n");
exit(1);
}

frame = av_frame_alloc();
if(NULL == frame)
{
fprintf(stderr,"Could not allocate video frame\n");
exit(1);
}

f = fopen("test.264","rb");
if( NULL == f )
{
fprintf(stderr,"Could not allocate video frame");
exit(1);
}

for(;;)
{
//解码还有问题,因为avpkt不是一帧数据,而是有很多帧的数据,
//需要增加一个filter,一帧一帧过滤出来
read_size = fread(inbuf, 1,INBUF_SIZE, f);
printf("read_size_orig=%d\n",read_size);
if(read_size == 0)
{
break;
}

while(read_size)
{
unsigned char *buf = 0;
int buf_len = 0;
int parse_len = av_parser_parse2(avParserContext, c, &buf, &buf_len,
inbuf, read_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE,AV_NOPTS_VALUE);
inbuf += parse_len;
read_size -= parse_len;
//printf("read_size=%d, parse_len=%d, buf_len=%d\n",read_size, parse_len, buf_len);
#if 1
if(buf_len != 0)
{
AVPacket avpkt = {0};
av_init_packet(&avpkt);
avpkt.data = buf;
avpkt.size = buf_len;
time_t start ,end;
start = clock();
int decode_len = avcodec_decode_video2(c,frame, &got_frame, &avpkt);
end = clock();
printf("decoded frame used %d\n",end - start);
if(decode_len < 0)
fprintf(stderr,"Error while decoding frame %d\n",frame_count);
if(got_frame)
{
fprintf(stderr,"decode success\n");
int width = frame->width;
int height = frame->height;
unsigned char* yuv_buf = (unsigned char*)malloc(width * height *1.5);//可以放在while循环外面,这样就不用每次都申请,释放了
int i = 0;

//把解码出来的数据存成YUV数据,方便验证解码是否正确
for(i = 0; i < height; i++)
{
memcpy(yuv_buf + width * i, frame->data[0] + frame->linesize[0]*i, width);
if(i < height >> 1)
{
memcpy(yuv_buf + width * height + width *i / 2, frame->data[1] + frame->linesize[1]*i, width / 2);
memcpy(yuv_buf + width * height * 5 /4 + width * i / 2 ,frame->data[2] + frame->linesize[2]*i, width / 2);
}
}
fwrite(yuv_buf, sizeof(unsigned char),width * height * 1.5 ,yuv_fp);
free(yuv_buf);
frame_count++;

}
else
{
fprintf(stderr,"decode fail\n");
}
av_packet_unref(&avpkt);
}
#endif
}
}

//记得释放变量的空间
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  h264 ffmpeg 解码 yuv420 yuv