Reputation: 207
For learning purposes, I made a routine that decodes each frame for each stream inside a given container.
I noticed that for some videos, the amount of frames returned by my code differs the one calculated by the tool ffprobe (which comes with ffmpeg).
I'm using ffprobe like this:
ffprobe <media file> -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames
Replacing "v:0" with "a:0" for audio, etc.
And this is my source code:
void showFrames(char *szFilename) {
int iError;
char szError[AV_ERROR_MAX_STRING_SIZE];
AVFormatContext *fcFormatCtx;
AVCodec *cdCodec;
AVCodecParameters *cdpCodecParams;
AVCodecContext *ccCodecCtx;
AVPacket *pkPacket;
AVFrame *frFrame;
fcFormatCtx=avformat_alloc_context();
iError=avformat_open_input(&fcFormatCtx,szFilename,NULL,NULL);
if(0>iError) {
av_strerror(iError,szError,sizeof(szError));
fprintf(stderr,"avformat_open_input() failed: %s\n",szError);
return;
}
iError=avformat_find_stream_info(fcFormatCtx,NULL);
if(0>iError) {
av_strerror(iError,szError,sizeof(szError));
fprintf(stderr,"avformat_find_stream_info() failed: %s\n",szError);
avformat_close_input(&fcFormatCtx);
return;
}
for(uint uiSt=0;uiSt<fcFormatCtx->nb_streams;uiSt++) {
cdpCodecParams=fcFormatCtx->streams[uiSt]->codecpar;
cdCodec=avcodec_find_decoder(cdpCodecParams->codec_id);
if(NULL==cdCodec) {
fprintf(stderr,"no codec found for stream %u\n",uiSt);
continue;
}
fprintf(stderr,"stream %u\n",uiSt);
if(AVMEDIA_TYPE_VIDEO==cdpCodecParams->codec_type)
fprintf(stderr,"video codec id=%d name='%s'\n",
cdCodec->id,cdCodec->long_name);
else if(AVMEDIA_TYPE_AUDIO==cdpCodecParams->codec_type)
fprintf(stderr,"audio codec id=%d name='%s'\n",
cdCodec->id,cdCodec->long_name);
else {
fprintf(stderr,"unsupported codec id=%d name='%s'\n",
cdCodec->id,cdCodec->long_name);
continue;
}
ccCodecCtx=avcodec_alloc_context3(cdCodec);
avcodec_parameters_to_context(ccCodecCtx,cdpCodecParams);
iError=avcodec_open2(ccCodecCtx,cdCodec,NULL);
if(0>iError) {
av_strerror(iError,szError,sizeof(szError));
fprintf(stderr,"avcodec_open2() failed: %s\n",szError);
avcodec_free_context(&ccCodecCtx);
continue;
}
pkPacket=av_packet_alloc();
frFrame=av_frame_alloc();
av_seek_frame(fcFormatCtx,uiSt,0,AVSEEK_FLAG_FRAME);
while(0==av_read_frame(fcFormatCtx,pkPacket)) {
if(uiSt==pkPacket->stream_index) {
iError=avcodec_send_packet(ccCodecCtx,pkPacket);
if(0>iError) {
av_strerror(iError,szError,sizeof(szError));
fprintf(stderr,"avcodec_send_packet() failed: %s\n",szError);
break;
}
while(true) {
iError=avcodec_receive_frame(ccCodecCtx,frFrame);
if(0>iError)
break;
fprintf(stderr,"stream %u, frame %d\n",
uiSt,ccCodecCtx->frame_number);
av_frame_unref(frFrame);
}
if(AVERROR(EAGAIN)!=iError&&AVERROR_EOF!=iError) {
av_strerror(iError,szError,sizeof(szError));
fprintf(stderr,"avcodec_receive_frame() failed: %s\n",szError);
break;
}
}
av_packet_unref(pkPacket);
}
av_packet_free(&pkPacket);
av_frame_free(&frFrame);
avcodec_free_context(&ccCodecCtx);
}
avformat_close_input(&fcFormatCtx);
}
It's pretty much self contained but you may ignore all the initializations and go directly to the while after the call to av_seek_frame(). This is where the actual frames are being read.
BTW, I'm using av_seek_frame() because this program goes stream by stream, separating the frames, so I need to rewind with every stream found.
Anyway, I've tested the previous code with the following files:
#1. sample-10s.mp4 from https://samplelib.com/sample-mp4.html ...
My program: 301 video frames; 440 audio frames
ffprobe: 303 video frames; 440 audio frames
#2. production ID_3997798.mp4 from https://www.pexels.com/video/hands-hand-table-colorful-3997798/ ...
My program: 736 video frames; no audio frames
ffprobe: 738 video frames; no audio frames
I found more videos with this difference, but it ONLY happens in the video streams.
Is there something I am forgetting? There seem to be always 2 frames behind what ffprobe shows.
Thank you.
Upvotes: 3
Views: 258