- 初始化 FFmpeg: 使用 av_register_all() 初始化 FFmpeg 库。
- 打开视频文件: 使用 avformat_open_input() 打开视频文件,并获取 AVFormatContext。
- 查找视频流: 使用 avformat_find_stream_info() 获取流信息,然后遍历 pFormatCtx->streams 找到视频流,并记录其索引。
- 获取编解码器上下文: 使用 avcodec_alloc_context3() 分配编解码器上下文,并使用 avcodec_parameters_to_context() 将流参数复制到编解码器上下文。
- 查找并打开解码器: 使用 avcodec_find_decoder() 查找适合的视频解码器,并使用 avcodec_open2() 打开解码器。
- 分配原始帧和 RGB 帧: 使用 av_frame_alloc() 分配两个 AVFrame 对象,一个用于存储解码后的原始帧,另一个用于存储转换后的 RGB 帧。
- 分配 RGB 帧缓冲区: 使用 av_image_get_buffer_size() 计算 RGB 帧所需的缓冲区大小,并使用 av_malloc() 分配缓冲区内存。然后使用 av_image_fill_arrays() 将缓冲区分配给 RGB 帧的图像平面。
- 初始化 SWS 上下文: 使用 sws_getContext() 初始化图像转换上下文。
- 读取并处理视频帧: 使用 av_read_frame() 从视频文件中读取数据包,检查是否属于视频流,并使用 avcodec_decode_video2() 解码数据包。如果帧解码成功,就使用 sws_scale() 将解码后的原始帧转换为 RGB 格式。
- 保存帧为 BMP 文件: 使用标准文件 I/O 函数(如 fopen() 和 fwrite())将 RGB 帧保存为 BMP 图像文件。需注意 BMP 文件的文件头和信息头的格式。
- 释放资源: 处理完成后,释放分配的缓冲区和 AVFrame,关闭编解码器上下文,并关闭视频文件。
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
const char *filename = "your_video_file.mp4";
const char *output_filename = "frame.bmp";
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
SwsContext *sws_ctx = NULL;
int videoStreamIndex;
av_register_all();
if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) {
fprintf(stderr, "Could not open file\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
return -1;
}
videoStreamIndex = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
fprintf(stderr, "Could not find video stream\n");
return -1;
}
pCodecCtx = avcodec_alloc_context3(NULL);
if (!pCodecCtx) {
fprintf(stderr, "Could not allocate video codec context\n");
return -1;
}
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
fprintf(stderr, "Unsupported codec\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return -1;
}
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameRGB = av_frame_alloc();
if (pFrame == NULL || pFrameRGB == NULL) {
fprintf(stderr, "Could not allocate video frame\n");
return -1;
}
int target_width = 1280;
int target_height = 720;
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, target_width, target_height, 1);
uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, target_width, target_height, 1);
sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
target_width, target_height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, NULL, NULL, NULL);
int frameFinished;
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStreamIndex) {
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (frameFinished) {
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
FILE *pFile = fopen(output_filename, "wb");
if (pFile == NULL) {
fprintf(stderr, "Could not open output file\n");
return -1;
}
int width = target_width;
int height = target_height;
int bytesPerPixel = 3;
int fileHeaderSize = 14;
int infoHeaderSize = 40;
int paddingSize = (4 - (width * bytesPerPixel) % 4) % 4;
int fileSize = fileHeaderSize + infoHeaderSize + (bytesPerPixel * width + paddingSize) * height;
unsigned char fileHeader[14] = {
'B', 'M',
fileSize, 0, 0, 0,
0, 0, 0, 0,
fileHeaderSize + infoHeaderSize, 0, 0, 0
};
unsigned char infoHeader[40] = {
infoHeaderSize, 0, 0, 0,
width, 0, 0, 0,
height, 0, 0, 0,
1, 0,
24, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
fwrite(fileHeader, 1, fileHeaderSize, pFile);
fwrite(infoHeader, 1, infoHeaderSize, pFile);
for (int y = height - 1; y >= 0; y--) {
fwrite(pFrameRGB->data[0] + y * pFrameRGB->linesize[0], bytesPerPixel, width, pFile);
fwrite("\0\0\0", 1, paddingSize, pFile);
}
fclose(pFile);
break;
}
}
av_packet_unref(&packet);
}
av_free(buffer);
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}