解码-ffmpeg如何提取视频的关键帧?

解码-ffmpeg如何提取视频的关键帧?

浮生未歇 发布于 2017-04-07 字数 0 浏览 1235 回复 4

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清晨说ぺ晚安 2017-11-10 4 楼

提供一篇网上教程:live555+ffmpeg如何提取关键帧(I帧,P帧,B帧)
希望对你有用处。

想挽留 2017-07-28 3 楼

转载一篇网上的博客:

开发流媒体播放器的时候,特别是在windows mobile,symbian(S60)平台开发时,很可能遇到需要自己开发播放器的情况。
S60平台提供了CVideoPlayUtility接口可以实现流媒体播放器,但由于非开源,所以相对于自己开发播放器,很多操作受到限制。
live555主要用于网络流接收,ffmpeg则是对接收到的数据进行编码/解码。I帧,P帧,B帧是视频流中三种分类,其中I帧也就是关键帧
是基础帧,P帧一般根据I帧确定,而B帧需要前面两着的信息。
举例说:

 the Input sequence for video encoder
1 2 3 4 5 6 7
I B B P B B I

Let's take 1,2,3.. as PTS for simplification

the out sequence for video encoder ( this equals the decoder sequence)
1 4 2 3 7 5 6
I P B B I B B

播放器LIVE555收到的序列顺序就应该是:

 1 4 2 3 7 5 6

经过解码器解码,顺序又回到1 2 3 4 5 6 7这种正常顺序。

所以我们可以根据avcodec_decode_video来判断帧别。
avcodec_decode_video之后的顺序是一定的。严格按照1 2 3 4。。。这样的顺序来。
判断I帧,P,B帧方法:
(1):假如解码成功,则不是I帧就是P帧(根据AVFrame->keyframe判断是否是I帧)。
假如不是I帧,也不是P帧,则只能是B帧(通过pts判断)。
(2):采用AVFrame->pict_type综合pts的办法:

 if(FF_I_TYPE==picture->pict_type)
{
Printlog("<II>");
}
else if(FF_P_TYPE==picture->pict_type)
{
Printlog("<PP>");
}
else if(FF_B_TYPE==picture->pict_type)
{
Printlog("<BB>");
}
else if(FF_S_TYPE==picture->pict_type)
{
Printlog("<SS>");
}
else
{
Printlog("<OtherType>");
}

正常情况下是不会打印出B帧的,因为解码成功的肯定是I帧或者是P帧.
文章出处:live555+ffmpeg如何提取关键帧(I帧,P帧,B帧)

偏爱自由 2017-07-17 2 楼

用两种方式,一是利用ffmpeg提供的可执行文件进行提取,另外就是用ffmpeg的sdk,进行开发。我下面说一下如何使用ffmpeg sdk进行提取(假设把提取的关键帧保存成bmp,源文件名是sample.mpg):

首先获取文件中的视频流:

av_register_all();

if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
printf("error!n");

if(av_find_stream_info(pFormatCtx)<0)
printf("error!n");

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
printf("error!n");// Didn't find a video stream

// 得到视频流编码上下文的指针
pCodecCtx=pFormatCtx->streams[videoStream]->codec;

然后选择解码器进行解码:
AVCodec *pCodec;

// 寻找视频流的解码器
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
printf("error!n");// 找不到解码器

// 打开解码器
if(avcodec_open(pCodecCtx, pCodec)<0)
printf("error!n"); // 打不开解码器

现在开始,进入解码和提取关键帧的过程:

pFrame=avcodec_alloc_frame();
pFrameRGB = avcodec_alloc_frame();
numBytes=avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width,pCodecCtx->height);
buffer=new uint8_t[numBytes];
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
pSWSCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

i=0;
while(av_read_frame(pFormatCtx,&packet)>=0)
{
if(packet.stream_index==videoStream)
{
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);
if(frameFinished)
{
if(pFrame->key_frame==1) // 这就是关键帧
{
sws_scale(pSWSCtx, pFrame->data, pFrame->linesize,0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
// 保存到磁盘
char pic[200];
sprintf(pic,"pic%d.bmp",i);
i++;
av_create_bmp(pic,pFrameRGB->data[0],pCodecCtx->width,pCodecCtx->height,24);
}
}
}
av_free_packet(&packet);
}

最后,释放资源和句柄

 // 释放 RGB 图象
av_free(pFrameRGB);
// 释放YUV 帧
av_free(pFrame);

sws_freeContext(pSWSCtx);

// 关闭解码器(codec)
avcodec_close(pCodecCtx);

// 关闭视频文件
av_close_input_file(pFormatCtx);

浮生未歇 2017-06-10 1 楼

ffmpeg SDK就支持,以下代码是ffmpeg官方小组提供的

 int main()
{
SwsContext *pSWSCtx;
AVFormatContext *pFormatCtx;
const char *filename="sample.mpg";
int i,videoStream,y_size;
AVCodecContext *pCodecCtx;
AVFrame *pFrame;
AVFrame *pFrameRGB;
int numBytes,frameFinished;
uint8_t *buffer;
static AVPacket packet;

av_register_all();

if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
printf("error!n");

if(av_find_stream_info(pFormatCtx)<0)
printf("error!n");

dump_format(pFormatCtx, 0, filename, false);

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
printf("error!n");// Didn't find a video stream

pCodecCtx=pFormatCtx->streams[videoStream]->codec;
AVCodec *pCodec;

pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
printf("error!n");

if(avcodec_open(pCodecCtx, pCodec)<0)
printf("error!n");
pFrame=avcodec_alloc_frame();

pFrameRGB = avcodec_alloc_frame();
numBytes=avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width,pCodecCtx->height);
buffer=new uint8_t[numBytes];
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
pSWSCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

i=0;
while(av_read_frame(pFormatCtx,&packet)>=0)
{
if(packet.stream_index==videoStream)
{
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);
if(frameFinished)
{
if(pFrame->key_frame==1)//这里取到关键帧数据
{
sws_scale(pSWSCtx, pFrame->data, pFrame->linesize,0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
i++;
}
}
}
av_free_packet(&packet);
}

av_free(pFrameRGB);

av_free(pFrame);

sws_freeContext(pSWSCtx);

avcodec_close(pCodecCtx);

av_close_input_file(pFormatCtx);

return 0;

}