03 ffmpeg 解码SDK调用 H264转YUV420
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了03 ffmpeg 解码SDK调用 H264转YUV420相关的知识,希望对你有一定的参考价值。
制作一个H264文件
[[email protected] ~]# cd /home/ [[email protected] home]# wget http://sh.yinyuetai.com/uploads/videos/common/0E3E014EBF3448D901AF3519C4A1D4E0.mp4 [[email protected] home]# /ffmpeg -t 20 -i 0E3E014EBF3448D901AF3519C4A1D4E0.mp4 -c copy 1920_1080.h264
同样的Makefile文件:
[[email protected] 03]# cat makefile FLAGS = -Wall -g INCLUDEPATH = -I /home/ffmpeg_dev/include/ LIBPATH = -L /home/ffmpeg_dev/lib/ LIBS= -l avcodec -l pthread -l avutil -l m -l dl -l swresample exe=yuv2h264 $(exe): gcc main.c ${FLAGS} ${INCLUDEPATH} ${LIBPATH} ${LIBS} -o [email protected] clean: rm -rf ${exe} [[email protected] 03]#
同样的自动编译运行脚本:
[[email protected] 03]# cat auto.sh make clean make ./yuv2h264 in.h264 out.yuv [[email protected] 03]#
这是一个可以正常运行的H264转YUV的程序:
[[email protected] 03_01]# cat main.c #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 #define INBUF_SIZE 409600000 int main(int argc, char** argv) { FILE *pFileH264 = NULL; FILE *pFileYUV = NULL; pFileH264 = fopen(argv[1],"rb"); if( NULL == pFileH264 ) { fprintf(stderr,"Could not open file \n"); exit(1); } pFileYUV = fopen(argv[2],"wb"); if(NULL == pFileYUV) { fprintf(stderr,"Open test.yuv fail\n"); exit(1); } avcodec_register_all(); AVCodec *codec; AVCodecContext *c = NULL; AVFrame *frame; AVCodecParserContext *avParserContext; int frame_count = 0; //unsigned char buffer[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; unsigned char * inbuf = (unsigned char*)malloc(INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); //unsigned char *inbuf = buffer; 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); } while(1) { //解码还有问题,因为avpkt不是一帧数据,而是有很多帧的数据, //需要增加一个filter,一帧一帧过滤出来 read_size = fread(inbuf, 1,INBUF_SIZE, pFileH264); 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); printf("av_parser_parse2 len=[%d]\n", parse_len); 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(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 ,pFileYUV); free(yuv_buf); frame_count++; } else { fprintf(stderr,"decode fail\n"); } av_packet_unref(&avpkt); } } } //记得释放变量的空间 return 0; }
运行后产生的文件:
[[email protected] 03_01]# ll total 1.4G drwxr-xr-x. 2 root root 97 Oct 23 08:47 . drwxr-xr-x. 7 root root 4.0K Oct 23 08:42 .. -rwxr-xr-x. 1 root root 44 Oct 23 08:00 auto.sh -rw-r--r--. 1 root root 3.9M Oct 23 07:59 in.h264 -rw-r--r--. 1 root root 5.1K Oct 23 08:38 main.c -rwxr-xr-x. 1 root root 338 Oct 23 07:59 makefile -rw-r--r--. 1 root root 1.4G Oct 23 08:46 out.yuv -rwxr-xr-x. 1 root root 53K Oct 23 08:46 yuv2h264 [[email protected] 03_01]#
这是一个还没有调试好的H264转YUV的程序
[[email protected] 03]# cat main.c #include <stdio.h> #include <stdlib.h> #include "libavcodec/avcodec.h" #include "libavutil/opt.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/mathematics.h" #include "libavutil/samplefmt.h" #define INBUF_SIZE 4096000 //缓存区太小,太大都不行 FILE *pFin = NULL; FILE *pFout = NULL; AVCodec *pCodec = NULL; AVCodecContext *pCodecContext = NULL; AVCodecParserContext *pCodecParserContext = NULL; AVFrame *frame = NULL; AVPacket pkt; static void finish() { fclose(pFin); fclose(pFout); avcodec_close(pCodecContext); av_free(pCodecContext); av_frame_free(&frame); } static int open_codec() { avcodec_register_all(); av_init_packet(&pkt); pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);// 查找编解码器 if(NULL == pCodec) { printf("Not find H264 Codec\n"); return -1; } pCodecContext = avcodec_alloc_context3(pCodec);//分配AV context 空间 if(NULL == pCodecContext) { printf("avcodec_alloc_context3 err\n"); return -1; } if(pCodec->capabilities & AV_CODEC_CAP_TRUNCATED)//判断读取码流的状态 ,可能以截断的方式来读取 { pCodecContext->flags |= AV_CODEC_CAP_TRUNCATED; printf("AV_CODEC_CAP_TRUNCATED \n"); } pCodecParserContext = av_parser_init(AV_CODEC_ID_H264);//根据码流格式,初始化解析器 if(NULL == pCodecParserContext) { printf("av_parser_init err\n"); return -1; } if(avcodec_open2(pCodecContext, pCodec, NULL) < 0) { printf("avcodec_open2 ERR \n"); return -1; } frame = av_frame_alloc(); if(NULL == frame) { printf("av_frame err\n"); return -1; } return 0; } static int init(int argc, char **argv) { const char *inputFileName = argv[1]; const char *oututFileName = argv[2]; pFin = fopen(inputFileName, "rb+"); if(NULL == pFin) { printf("open [%s],fail \n", inputFileName); return -1; } printf("open [%s],OK \n", inputFileName); pFout = fopen(oututFileName, "wb+"); if(NULL == pFout) { printf("open [%s],fail \n", oututFileName); return -1; } printf("open [%s],OK\n", oututFileName); return 0; } static void write_out_yuv_frame(AVFrame *frame) { int *pStride = frame->linesize; int i; for( i= 0; i < 3; i++ ) { int nWidth = i == 0 ? frame->width : frame->width/2; int nHeight = i == 0 ? frame->height : frame->height/2; int y; for(y = 0; y < nHeight; y++) { fwrite(frame->data[i], 1, nWidth, pFout); frame->data[i] += pStride[i]; } fflush(pFout); } } int main(int argc, char **argv) { uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; unsigned int uDataSize = 0; int got_frame = 0; uint8_t *pDataPtr = NULL; uint8_t len = 0; if(init(argc, argv) != 0) { printf("init err \n"); return -1; } printf("init OK\n"); if(open_codec() != 0) { printf("open_codec err \n"); return -1; } printf("open_codec OK\n"); while(1) { memset(inbuf, 0, AV_INPUT_BUFFER_PADDING_SIZE + INBUF_SIZE); uDataSize = fread(inbuf, 1, INBUF_SIZE, pFin); printf("read size=[%d] ----------------------------------- \n", uDataSize); if(uDataSize == 0) { printf("break while\n"); break; } pDataPtr = inbuf; while(uDataSize > 0) { printf("into while uDataSize=[%d]\n", uDataSize); //int av_parser_parse2(AVCodecParserContext *s, // AVCodecContext *avctx, // uint8_t **poutbuf, int *poutbuf_size, // const uint8_t *buf, int buf_size, // int64_t pts, int64_t dts, // int64_t pos); len = av_parser_parse2(pCodecParserContext, pCodecContext, &pkt.data, &pkt.size, pDataPtr, uDataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE); printf("av_parser_parse2 len=[%d]\n", len); pDataPtr += len; uDataSize -= len; if(pkt.size != 0) { //成功的解析出一个包的数据 printf("Parser Packet! uDataSize=[%d]\n", uDataSize); //int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, // int *got_picture_ptr, // const AVPacket *avpkt); int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt); printf("avcodec_decode_video2 return[%d]***************************\n", ret); if(ret < 0) { printf("decode Err\n"); return -1; } if(got_frame) { printf("width*Height:[%d]*[%d]\n", frame->width, frame->height); write_out_yuv_frame(frame); } } else { printf("."); continue; } } } pkt.data = NULL; pkt.size = 0; while(1) { int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt); if(ret < 0) { printf("decode fial\n"); return -1; } if(got_frame) { printf("fflush width*Height:[%d]*[%d]", frame->width, frame->height); write_out_yuv_frame(frame); } else { break; } } finish(); printf("everything will be OK! \n"); return 0; } [[email protected] 03]#
本文出自 “李春利” 博客,转载请与作者联系!
以上是关于03 ffmpeg 解码SDK调用 H264转YUV420的主要内容,如果未能解决你的问题,请参考以下文章
FFMpeg SDK使用6调用FFmpeg SDK实现视频文件的转封装
FFMpeg SDK使用3调用FFmpeg SDK实现视频编码
FFMpeg Android Stagefright SIGSEGV 错误(h264 解码)