只要流程图如下:
注意事项:
1. 里面的gy_log_info和gy_log_error为我自己编写的日志系统,需要自己替换成printf。
2. 文件路径请自行修改,内部使用的是追加,所以一开始要用fopen来清除文件的内容。
源码如下:
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/log.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define INBUF_SIZE 4096
uint64_t fileSize = 0;
static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
const char *filename)
{
char buf[1024];
int ret;
FILE* fp;
int i;
// 将包含h264数据的packet传入AVCodecContext里面,该过程包含解码过程,接完的数据放置于AVCodecContext 里面。
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
gy_log_error(__FILE__, __LINE__, "Error sending a packet for decoding.\n");
exit(1);
}
fp = fopen(filename, "a+");
if(fp == NULL)
{
gy_log_error(__FILE__, __LINE__, "Open %s error.\n", filename);
exit(-1);
}
while (ret >= 0)
{
// 从AVCodecContext 里面获取数据
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
fclose(fp);
return;
}
else if (ret < 0)
{
gy_log_error(__FILE__, __LINE__, "Error during decoding.\n");
exit(1);
}
gy_log_info(__FILE__, __LINE__, "Saving frame %d.\n", dec_ctx->frame_number);
fflush(stdout);
/* the picture is allocated by the decoder. no need to
free it */
gy_log_info(__FILE__, __LINE__, "P5:\n\t(%d %d) %d %d %d\n", frame->width, frame->height, frame->linesize[0], frame->linesize[1], frame->linesize[2]);
// 写入文件,由于我这里是yuv420,所以是这么编写的,其他格式请自行参考。
fwrite(frame->data[0], frame->height, frame->width, fp);
fwrite(frame->data[1], frame->height/2, frame->width/2, fp);
fwrite(frame->data[2], frame->height/2, frame->width/2, fp);
}
fclose(fp);
return;
}
void decode_avc(const char* inputFilePath, const char* outputFilePath)
{
FILE *inFp;
const AVCodec *codec;
AVCodecParserContext *parser;
AVCodecContext *c = NULL;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t size = 0;
size_t data_size;
int ret;
AVPacket *pkt;
pkt = av_packet_alloc();
if(!pkt)
{
gy_log_error(__FILE__, __LINE__, "Packet alloc error.\n");
exit(1);
}
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if(!codec)
{
gy_log_error(__FILE__, __LINE__, "Codec not found.\n");
exit(-1);
}
parser = av_parser_init(codec->id);
if(!parser)
{
gy_log_error(__FILE__, __LINE__, "parser not found.\n");
exit(-1);
}
c = avcodec_alloc_context3(codec);
if(!c)
{
gy_log_error(__FILE__, __LINE__, "Could not allocate video codec context.\n");
exit(-1);
}
if(avcodec_open2(c, codec, NULL) < 0)
{
gy_log_error(__FILE__, __LINE__, "Could not open codec.\n");
exit(-1);
}
inFp = fopen(inputFilePath, "rb");
if(!inFp)
{
gy_log_error(__FILE__, __LINE__, "Could not open %s.\n", inputFilePath);
exit(-1);
}
frame = av_frame_alloc();
if(!frame)
{
gy_log_error(__FILE__, __LINE__, "Could not allocate video frame.\n");
exit(-1);
}
while(!feof(inFp))
{
data_size = fread(inbuf, 1, INBUF_SIZE, inFp);
if(!data_size)
{
gy_log_info(__FILE__, __LINE__, "File is end. The sum of size is %d\n", size);
break;
}
size += data_size;
data = inbuf;
while(data_size > 0)
{
// 获取data数据,并将其转化成packet。
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, data,
data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if(ret < 0)
{
gy_log_error(__FILE__, __LINE__, "Error while parsing.\n");
exit(-1);
}
data += ret;
data_size -= ret;
if(pkt->size)
decode(c, frame, pkt, outputFilePath);
}
}
/* flush the decoder */
decode(c, frame, NULL, outputFilePath);
fclose(inFp);
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
}
int main()
{
FILE* fp = fopen("/home/user/TestFfmpeg/out10.yuv", "w");
fclose(fp);
decode_avc("/home/user/TestFfmpeg/out.h264", "/home/user/TestFfmpeg/out10.yuv");
return 0;
}