我的程序(使用libav)和ffprobe之间的总帧数略有不同

发布于 2025-02-07 18:31:01 字数 4797 浏览 2 评论 0 原文

出于学习目的,我制作了一个例程,将给定容器内的每个流的每个帧解码。

我注意到对于某些视频,我的代码返回的帧量与工具FFProbe计算的帧不同(随FFMPEG随附)。

我正在使用这样的ffprobe:

ffprobe <media file> -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames

音频等替换“ v:0”

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);
}

用 em> while 在致电 av_seek_frame()之后。这是正在读取实际帧的地方。

顺便说一句,我正在使用av_seek_frame(),因为此程序通过流进行流,将框架分开,因此我需要在发现的每个流中倒带。

无论如何,我已经使用以下文件测试了先前的代码:

#1。 sample-10s.mp4来自 https://samplelib.com/sampamplem.com/sample-mp4.html 。 ..

我的程序: 301 视频帧; 440音频帧

ffprobe: 303 视频帧; 440音频帧

#2。生产ID_3997798.MP4来自 ...

我的程序: 736 视频帧;没有音频帧

ffprobe: 738 视频帧;没有音频帧

我发现了更多具有此差异的视频,但仅在视频流中发生。

我忘记了什么吗? FFProbe显示的内容似乎总是有2帧。

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文