上篇文章用libavcodec API解码音频文件,本篇文章解码视频。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
static uint8_t *video_dst_data[4] = {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static int width;
static int height;
static const char *fmt_name = NULL;
#define VIDEO_INBUF_SIZE 20480
#define VIDEO_REFILL_THRESH 4096
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
FILE *outfile)
{
int ret;
/* send the packet with the compressed data to the decoder */
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error submitting the packet to the decoder\n");
exit(1);
}
/* read all the output frames (in general there may be any number of them */
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
if (!video_dst_data[0]) {
width = frame->width;
height = frame->height;
fmt_name = av_get_pix_fmt_name(frame->format);
ret = av_image_alloc(video_dst_data, video_dst_linesize, width, height, frame->format, 1);
if (ret < 0) {
printf("Could not allocate raw video buffer\n");
exit(1);
}
video_dst_bufsize = ret;
}
//复制解码后的视频到之前创建的缓存区
av_image_copy(video_dst_data, video_dst_linesize, (const uint8_t **)frame->data, frame->linesize, frame->format, frame->width, frame->height);
//写入视频
fwrite(video_dst_data[0], 1, video_dst_bufsize, outfile);
}
}
static void decode_video(void)
{
const char *infilename = "/Users/zhw/Desktop/sintel.h264";
const char *outfilename = "/Users/zhw/Desktop/sintel.yuv";
const AVCodec *codec;
AVCodecContext *c= NULL;
AVCodecParserContext *parser = NULL;
size_t len, ret;
FILE *f, *outfile;
uint8_t inbuf[VIDEO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
AVPacket *pkt;
AVFrame *decoded_frame = NULL;
pkt = av_packet_alloc();
/* 此处应根据输入文件的实际编码格式选择正确的编码ID */
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
parser = av_parser_init(codec->id);
if (!parser) {
fprintf(stderr, "Parser not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(infilename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", infilename);
exit(1);
}
outfile = fopen(outfilename, "wb");
if (!outfile) {
exit(1);
}
/* decode until eof */
data = inbuf;
data_size = fread(inbuf, 1, VIDEO_INBUF_SIZE, f);
while (data_size > 0) {
if (!decoded_frame) {
if (!(decoded_frame = av_frame_alloc())) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
}
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, (int)data_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;
if (pkt->size){
decode(c, pkt, decoded_frame, outfile);
av_packet_unref(pkt);
}
if (data_size < VIDEO_REFILL_THRESH) {
memmove(inbuf, data, data_size);
data = inbuf;
len = fread(data + data_size, 1,
VIDEO_INBUF_SIZE - data_size, f);
if (len > 0)
data_size += len;
}
}
/* flush the decoder */
pkt->data = NULL;
pkt->size = 0;
decode(c, pkt, decoded_frame, outfile);
av_packet_unref(pkt);
fclose(outfile);
fclose(f);
avcodec_free_context(&c);
av_parser_close(parser);
av_frame_free(&decoded_frame);
av_packet_free(&pkt);
printf("finish decode video\n");
printf("Play the output video file with the command:\n"
"ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s\n",
fmt_name, width, height,
outfilename);
}