ffmpeg关于AVStream中nb_frames变量数目不正确的问题分析

本人最近在做一个可视化工具,可以输入视频文件,对视频文件进行转码转封装,其中需要显示处理进度,本人以视频帧总数为基准进行进度显示,比如文件中视频帧总数为1000时,现在处理到第100帧,此时进度条显示10%。

本人使用AVStream中nb_frames作为视频帧总数,数值为1200,但是本人在一帧一帧读取,解码处理时,最终得到的视频帧数是1199;比1200少1。

为此,本人使用ffprobe探帧命令行测试,得到的结果也是1199,命令行如下:

ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 2022-01-08T22-32-58.mp4

本人然后又用ffprobe打印每一帧的具体情况,最终也是1199帧。

问题出在哪里,为此本人调试了ffprobe,关于ffprobe的调试工程搭建,大家可以参看我的博客vs2017调试ffprobe源码

很快,源码调试就是快,本人很快找到了原因,在ffprobe.c里面,有一个函数,如下:

static av_always_inline int process_frame(WriterContext *w,
                                          InputFile *ifile,
                                          AVFrame *frame, AVPacket *pkt,
                                          int *packet_new)
{
    
    
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
    AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
    AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
    AVSubtitle sub;
    int ret = 0, got_frame = 0;

    clear_log(1);
    if (dec_ctx && dec_ctx->codec) {
    
    
        switch (par->codec_type) {
    
    
        case AVMEDIA_TYPE_VIDEO:
        case AVMEDIA_TYPE_AUDIO:
            if (*packet_new) {
    
    
                ret = avcodec_send_packet(dec_ctx, pkt);
                if (ret == AVERROR(EAGAIN)) {
    
    
                    ret = 0;
                } else if (ret >= 0 || ret == AVERROR_EOF) {
    
    
                    ret = 0;
                    *packet_new = 0;
                }
            }
            if (ret >= 0) {
    
    
                ret = avcodec_receive_frame(dec_ctx, frame);
                if (ret >= 0) {
    
    
                    got_frame = 1;
                } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
    
    
                    ret = 0;
                }
            }
            break;

        case AVMEDIA_TYPE_SUBTITLE:
            if (*packet_new)
                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
            *packet_new = 0;
            break;
        default:
            *packet_new = 0;
        }
    } else {
    
    
        *packet_new = 0;
    }

    if (ret < 0)
        return ret;
    if (got_frame) {
    
    
        int is_sub = (par->codec_type == AVMEDIA_TYPE_SUBTITLE);
        nb_streams_frames[pkt->stream_index]++;
        if (do_show_frames)
            if (is_sub)
                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
            else
                show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
        if (is_sub)
            avsubtitle_free(&sub);
    }
    return got_frame || *packet_new;
}

大家可以看到nb_streams_frames[pkt->stream_index]++;
这个nb_streams_frames里面记录的是ffprobe打印出来的帧数,而这个++执行的条件是got_frame为真。

现在我在将里面的关键点摘抄出来,如下所示:

if (ret >= 0) {
    
    
    ret = avcodec_receive_frame(dec_ctx, frame);
    if (ret >= 0) {
    
    
        got_frame = 1;
    } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
    
    
        ret = 0;
    }
}

这个是个解码的过程,只有成功解码出一帧(ret >= 0),才被计算在总数内。

2023年2月11号:本人发现,在编码的时候,如果AVPacket里面的duration写为0,则将导致结尾某处的一帧不可解码,所以后面本人在编码时,duration不设置为0就行了。

猜你喜欢

转载自blog.csdn.net/tusong86/article/details/128666261