AVCodecContext *c //編碼器環境句柄
AVFrame* f //需要編碼的壹幀視頻
/*在avcodec.h文件中有這樣的定義
#define FF_I_TYPE 1 ///< Intra
#define FF_P_TYPE 2 ///< Predicted
#define FF_B_TYPE 3 ///< Bi-dir predicted
#define FF_S_TYPE 4 ///< S(GMC)-VOP MPEG4
#define FF_SI_TYPE 5 ///< Switching Intra
#define FF_SP_TYPE 6 ///< Switching Predicted
#define FF_BI_TYPE 7
*/
在編碼前設置
f->pict_type=FF_I_TYPE;
f->key_frame=1;
註:該幀為I幀時,f->pict_type==FF_I_TYPE && f->key_frame==1
然後編碼
*outsize = avcodec_encode_video(c, temp, outbuf_size, f);
則編碼之後通過如下參數判斷是否為關鍵幀:
key_frame=c->coded_frame->key_frame;
pict_type=c->coded_frame->pict_type;
key_frame==FF_I_TYPE && pict_type==1
ffmpeg如何提取視頻的關鍵幀
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)
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;
}
live555+ffmpeg如何提取關鍵幀(I幀,P幀,B幀)
live555+ffmpeg如何提取關鍵幀(I幀,P幀,B幀)開發流媒體播放器的時候,特別是在windows mobile,symbian(S60)平臺開發時,很可能遇到需要自己開發播放器的情況。S60平臺提供了CVideoPlayUtility接口可以實現流媒體播放器,但由於非開源,所以相對於自己開發播放器,很多操作受到限制。live555主要用於網絡流接收,ffmpeg則是對接收到的數據進行編碼/解碼。I幀,P幀,B幀是視頻流中三種分類,其中I幀也就是關鍵幀是基礎幀,P幀壹般根據I幀確定,而B幀需要前面兩著的信息。舉例說:the Input sequence for video encoder1 2 3 4 5 6 7I 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 6I 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幀.