学习了FFmpeg的一些转码流程和API后,参考示例代码,实现下面一段代码,主要实现了从一个视频文件抓取若干帧并保存成本地文件,以ppm格式
主要处理的流程:
#include <stdio.h>
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
printf("SaveFrame... %d \n",iFrame);
FILE *file;
char outFilename[20];
sprintf(outFilename,"frame%d.ppm",iFrame);
file=fopen(outFilename,"wb");
if(file == NULL)
printf("open frame%d.ppm faild!\n",iFrame);
// Write header
fprintf(file, "P6\n%d %d\n255\n", width, height);
int y;
for(y=0; y<height; y++) // Write pixel data
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, file);
fclose(file);
}
int img_convert2(AVPicture *dst, enum AVPixelFormat dst_pix_fmt,
AVPicture *src,enum AVPixelFormat src_pix_fmt,
int src_width, int src_height)
{
printf("img_convert2...\n");
int w,h;
w=src_width;
h=src_height;
struct SwsContext *sws_ctx;
sws_ctx = sws_getContext(w,h,src_pix_fmt,w,h,dst_pix_fmt,SWS_BICUBIC,NULL,NULL,NULL);
sws_scale(sws_ctx,src->data,src->linesize,0 ,h,dst->data,dst->linesize);
//free SwsContext mem
sws_freeContext(sws_ctx);
return 0;
}
int main(int argc,char **argv)
{
int ret;
int i;
int videostream = -1;
AVFormatContext *fmt_ctx=NULL;
AVStream *Stream=NULL;
AVCodec *decode=NULL;
AVCodecContext *code_ctx=NULL;
av_register_all();
// Open video file
ret = avformat_open_input(&fmt_ctx,argv[1],NULL,NULL);
if(ret< 0)// Couldn't open file
printf("avformat_open_input failed!\n");
ret = avformat_find_stream_info(fmt_ctx,NULL);
if(ret< 0)// Couldn't find stream information
printf("avformat_find_stream_info failed!\n");
/*
for (i = 0;i < fmt_ctx->nb_streams;i ++)
{
if(fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
videostream = i;
}
*/
videostream =av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,&decode,0);
if(videostream == -1)
printf("NO video stream\n");// Didn't find a video stream
Stream = fmt_ctx->streams[videostream];
// Find the decoder for the video stream
decode = avcodec_find_decoder(Stream->codecpar->codec_id);
if(decode == NULL)
printf("decode is NULL!\n");// Codec not found
code_ctx = avcodec_alloc_context3(decode);
ret = avcodec_parameters_to_context(code_ctx, Stream->codecpar);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
"for stream #%u\n", i);
}
// Open codec
ret = avcodec_open2(code_ctx, decode, NULL);
if(ret < 0)
printf("avcodec_find_decoder failed!\n");
//check the parameter
av_dump_format(fmt_ctx, 0, argv[1], 0);
AVFrame *picture,*pictureRGB;
// Allocate video frame
picture = av_frame_alloc();
pictureRGB = av_frame_alloc();
if(picture == NULL || pictureRGB == NULL)
printf("av_frame_alloc failed!\n");
int numByte;
uint8_t *buf;
// Determine required buffer size and allocate buffer
numByte = avpicture_get_size(AV_PIX_FMT_RGB24,code_ctx->width,code_ctx->height);
buf = (uint8_t *)av_malloc(numByte*sizeof(uint8_t));
avpicture_fill((AVPicture *) pictureRGB,buf,AV_PIX_FMT_RGB24,code_ctx->width,code_ctx->height);
AVPacket packet;
av_init_packet(&packet);
int frameFinished;
while((ret = av_read_frame(fmt_ctx, &packet)) >= 0)
{
if(packet.stream_index == videostream){
// Decode video frame
avcodec_decode_video2(code_ctx,picture,&frameFinished,&packet);
if(frameFinished)//get a complete frame
{
// Convert the image from its native format to RGB
img_convert2((AVPicture *)pictureRGB,AV_PIX_FMT_RGB24,(AVPicture *)picture,code_ctx->pix_fmt,code_ctx->width,code_ctx->height);
for(i = 0;i < 10;i ++)
{
// Save the frame to disk
SaveFrame(pictureRGB,code_ctx->width,code_ctx->height,i);
}
break;
}
av_free_packet(&packet);
}
}
// Free the RGB image
av_free(buf);
av_free(pictureRGB);
// Free the YUV frame
av_free(picture);
// Close the codec
avcodec_close(code_ctx);
avformat_free_context(fmt_ctx);
return 0;
}
运行结果: