第一步 定义一些接下来会用到的变量
代码中有详细解释 就不一一叙述了
#define INBUF_SIZE 4096
//输入文件路径和输出文件路径
const char* filename, * outfilename;
//编解码器结构体
const AVCodec* codec;
// 解析器上下文 这个东西是为了把你文件中的数据打包到我下面的AVPacket中去 必须要通过这个分成一包一包的 然后才能解压缩
AVCodecParserContext* parser;
//编解码器上下文 保存编解码器的一些相关信息
AVCodecContext* c = NULL;
//文件指针
FILE* f;
//解码后的数据放这里
AVFrame* frame;
//缓冲区 AV_INPUT_BUFFER_PADDING_SIZE为内存对齐需要多加的东西
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
//临时的变量 主要是为了等会文件操作更加方便
uint8_t* data;
size_t data_size;
//验错处理
int ret;
//未解码的数据放这里
AVPacket* pkt;
第二步 给各个变量分配内存空间 然后找到编解码器 分析器 打开编解码器和分析器
//输入文件路径和输出文件路径
const char* filename, * outfilename;
//编解码器结构体
const AVCodec* codec;
// 解析器上下文 这个东西是为了把你文件中的数据打包到我下面的AVPacket中去 必须要通过这个分成一包一包的 然后才能解压缩
AVCodecParserContext* parser;
//编解码器上下文 保存编解码器的一些相关信息
AVCodecContext* c = NULL;
//文件指针
FILE* f;
//解码后的数据放这里
AVFrame* frame;
//缓冲区 AV_INPUT_BUFFER_PADDING_SIZE为内存对齐需要多加的东西
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
//临时的变量 主要是为了等会文件操作更加方便
uint8_t* data;
size_t data_size;
//验错处理
int ret;
//未解码的数据放这里
AVPacket* pkt;
filename = argv[1];
outfilename = argv[2];
//给pkt申请内存空间 用来存放未解码的数据
pkt = av_packet_alloc();
//把缓冲区后内存对齐的部分设置为0 以防以后出问题
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
//通过ID拿到对应的编解码器 这里是拿到h.264的编解码器
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
//通过编解码器的ID来拿到对应的分析器 分析器是为了从文件流中读取packet
parser = av_parser_init(codec->id);
//给编解码器上下文分配内存空间
c = avcodec_alloc_context3(codec);
//打开编解码器 打开后一直存在 后续不需要指定
avcodec_open2(c, codec, NULL);
//打开文件
f = fopen(filename, "rb");
//给解码后的帧分配内存空间
frame = av_frame_alloc();
//读取文件
while (!feof(f)) {
data_size = fread(inbuf, 1, INBUF_SIZE, f);
if (!data_size)
break;
//临时指针data指向inbuf
data = inbuf;
while (data_size > 0) {
//把这个缓冲区内的数据打包成Packet放入pkt中
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
data += ret;
data_size -= ret;
//读到一个包 解析一个包
if (pkt->size)
decode(c, frame, pkt, outfilename);
}
}
//最后再解码一次 因为最后一帧还没有解析
decode(c, frame, NULL, outfilename);
//释放内存空间
fclose(f);
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
第三步 decode函数 真正的调用解码器去解码
static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt,
const char* filename)
{
//缓冲区
char buf[1024];
//验错
int ret;
//把packet发到编解码器解析 之前已经打开了 所以现在直接发就是了
ret = avcodec_send_packet(dec_ctx, pkt);
while (ret >= 0) {
//从解码器哪里拿到解码后的数据 并放入frame中
ret = avcodec_receive_frame(dec_ctx, frame);
//如果读到结尾了 那就停止
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
//下面两步就是把对应的数据写入文件 跟编解码没关系
snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);
pgm_save(frame->data[0], frame->linesize[0],
frame->width, frame->height, buf);
}
}
关于pgm_save函数
static void pgm_save(unsigned char* buf, int wrap, int xsize, int ysize,
char* filename)
{
FILE* f;
int i;
f = fopen(filename, "w");
fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
for (i = 0; i < ysize; i++)
fwrite(buf + i * wrap, 1, xsize, f);
fclose(f);
}