您的位置:首页 > 其它

03 ffmpeg 解码SDK调用 H264转YUV420

2017-10-23 00:51 543 查看
制作一个H264文件
[root@localhost ~]# cd /home/
[root@localhost home]# wget http://sh.yinyuetai.com/uploads/videos/common/0E3E014EBF3448D901AF3519C4A1D4E0.mp4 [root@localhost home]# /ffmpeg -t 20 -i 0E3E014EBF3448D901AF3519C4A1D4E0.mp4 -c copy 1920_1080.h264


同样的Makefile文件:
[root@localhost 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 $@

clean:
rm -rf ${exe}

[root@localhost 03]#


同样的自动编译运行脚本:
[root@localhost 03]# cat auto.sh
make clean
make
./yuv2h264 in.h264  out.yuv
[root@localhost 03]#


这是一个可以正常运行的H264转YUV的程序:
[root@localhost 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;
}


运行后产生的文件:
[root@localhost 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
[root@localhost 03_01]#





这是一个还没有调试好的H264转YUV的程序
[root@localhost 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;
}

[root@localhost 03]#
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SDK 解码 ffmpeg