1、引入头文件
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
// 如果需要将YUV420p转换成RGB
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
2、 初始化
1、定义
const AVCodec *codec; // 解码器
AVCodecContext *c = NULL; // 上下文
AVPacket pkt; // pkaket
AVFrame *frame; // AVFrame结构体
AVFrame *frameRGBA;
AVFormatContext *avformatcontext;
2、解码器配置
av_register_all();
avformatcontext = avformat_alloc_context();
// 从filename中读取数据到avformatcontext中,读取成功后input>0
int input = avformat_open_input(&avformatcontext, filename, NULL, NULL);
if (input < 0)
{
fprintf(stderr, " open input error ,\n input ------->>%d", input);
exit(1);
}
avformatcontext->probesize = 2456000;
avformatcontext->max_analyze_duration = 1500;
// 读取媒体文件的数据包以获取流信息
int streamInfo = avformat_find_stream_info(avformatcontext, NULL);
if (streamInfo < 0)
{
fprintf(stderr, "find_stream error, \n streamInfo ------>>%d", streamInfo);
exit(1);
}
// 找到第一帧
unsigned int i;
int videostream = -1;
for (i = 0; i < avformatcontext->nb_streams; i++)
{
if (avformatcontext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videostream < 0)
{
videostream = i;
break;
}
}
if (videostream == -1)
{
fprintf(stderr, "can not find a video stream");
exit(1);
}
// 获取codec
c = avformatcontext->streams[videostream]->codec;
// 设置线程数
//c->thread_count = 4;
// 找到解码器
codec = avcodec_find_decoder(c->codec_id);
if (!codec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
// 打开解码器
if (avcodec_open2(c, codec, NULL) < 0)
{
fprintf(stderr, "Could not open codec\n");
exit(1);
}
// 初始化frame
frame = avcodec_alloc_frame();
if (!frame)
{
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
// 初始化frameRGBA,用于接收RGB数据渲染
frameRGBA = avcodec_alloc_frame();
if (!frameRGBA)
{
fprintf(stderr, "Could not allocate video frameRGBA\n");
exit(1);
}
// 设置经过swsscale后输出图像的大小(若不需要缩放,可以直接用c->width和c->height)
int width_out, height_out;
// 给frameRGBA分配空间
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width_out, height_out, 1);
uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(frameRGBA->data, frameRGBA->linesize, buffer,
AV_PIX_FMT_RGBA, width_out, height_out, 1);
// 设置swsscale
// swscale选择的算法可以参考 https://blog.csdn.net/leixiaohua1020/article/details/12029505
sws_ctx = sws_getContext(c->width, c->height,
c->pix_fmt, width_out, height_out, AV_PIX_FMT_RGBA,
SWS_POINT, NULL, NULL, NULL);
3、解码
while (av_read_frame(avformatcontext, &pkt) >= 0)
{
if (pkt.stream_index == videostream)
{
int len;
// 送入packet,当解码器完成一个frame的解码时,framefinished返回一个非0数
// 送入第一个packet时可能不会立即有数据,到最后会出现少帧的现象
// 可以参考 https://blog.csdn.net/subfate/article/details/50273955
len = avcodec_decode_video2(c, frame, &framefinished, &pkt);
if (len < 0)
{
fprintf(stderr, "Error sending a packet for decoding\n");
exit(1);
}
if (framefinished)
{
// 将解码出来的数据转换为RGB
sws_scale(sws_ctx, (uint8_t const *const *)frame->data,
frame->linesize, 0, c->height,
frameRGBA->data, frameRGBA->linesize);
// 保存
FILE *fd = fopen("RGB.rgb", "w+");
if(fd)
{
fwrite(frameRGBA->data[0], 1, (frameRGBA->width * frameRGBA->height * 4), fd);
fclose(fd);
}
}
av_free_packet(&pkt);
}
else
{
printf("no stream");
}
}
4、释放资源
avcodec_close(c);
avformat_close_input(&avformatcontext);
avcodec_free_frame(&frame);
avcodec_free_frame(&frameRGBA);
av_free_packet(&pkt);
sws_freeContext(sws_ctx);
free(buffer);