读取视频帧保存为ppm格式图片
ffmpeg 4.0.1
Qt 5.9
ppm图像格式解析:
https://blog.csdn.net/gengshenghong/article/details/8648577
https://blog.csdn.net/kinghzkingkkk/article/details/70226214
////////////////////////////////////////////////////////////////////////////////////////
/// 将视频中的5帧保存为ppm格式图像
////////////////////////////////////////////////////////////////////////////////////////
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
#include <stdio.h>
///
/// \brief SaveFrame 保存rgb为ppm图像
/// \param pFrame
/// \param width
/// \param height
/// \param iFrame
///
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;
// 打开文件
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// 写入图片的头部
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// 写入图片的rgb数据
for(y=0; y<height; y++)
{
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
}
// 关闭文件
fclose(pFile);
}
int main(int argc, char *argv[])
{
AVFormatContext *pFormatCtx = NULL;
int i, videoStream;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVFrame *pFrameRGB = NULL;
AVPacket packet;
int frameFinished;
int numBytes;
uint8_t *buffer = NULL;
AVDictionary *optionsDict = NULL;
struct SwsContext *sws_ctx = NULL;
char *filename = "C:/Users/Administrator/Desktop/test.wmv";
/*
if(argc < 2)
{
printf("Please provide a movie file\n");
return -1;
}
*/
// 注册解码器
av_register_all();
// 打开视频文件
if(avformat_open_input(&pFormatCtx, filename, NULL, NULL)!=0)
{
// 打开失败
return -1;
}
// 检索流信息
if(avformat_find_stream_info(pFormatCtx, NULL)<0)
{
// 无法找到刘信息
return -1;
}
// 输出流信息
av_dump_format(pFormatCtx, 0, filename, 0);
// 寻找视频流
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStream=i;
break;
}
}
if(videoStream==-1)
{
// 找不到视频流
return -1;
}
// 获取视频流的编解码器上下文
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
// 获取视频解码器
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
fprintf(stderr, "Unsupported codec!\n");
// 找不到解码器
return -1;
}
// 打开解码器
if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0)
{
// 无法打开解码器
return -1;
}
// 分配帧空间
pFrame=av_frame_alloc();
// 分配帧空间
pFrameRGB=av_frame_alloc();
if(pFrameRGB==NULL)
return -1;
// 分配一张图像的空间
numBytes=avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
sws_ctx =
sws_getContext
(
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_RGB24,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
// 将适当的缓冲区部分分配给pFrameRGB中的图像
// 请注意,pFrameRGB是一个AVFrame,但AVFrame是AVPicture的超集
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
// 读取并保存视频中的5张图像
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0)
{
// Is this a packet from the video stream?
if(packet.stream_index==videoStream)
{
// 解码视频帧
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet);
// 是否获取到一帧图像
if(frameFinished)
{
// 将图像从其原始格式转换为RGB
sws_scale
(
sws_ctx,
(uint8_t const * const *)pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
pFrameRGB->data,
pFrameRGB->linesize
);
// 保存一帧图像
if(++i<=5)
{
SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,i);
}
}
}
// 释放packet分配的内存空间
av_free_packet(&packet);
}
// 释放内存空间
av_free(buffer);
av_free(pFrameRGB);
// 释放YUV frame内存空间
av_free(pFrame);
// 关闭解码器
avcodec_close(pCodecCtx);
// 关闭打开的文件
avformat_close_input(&pFormatCtx);
return 0;
}