ffmpeg4.x版本命令行解码后出现drop或者dup也就是丢帧和多帧的现象

前言

最近ffmpeg3.x版本命令行解码后出现drop或者dup也就是丢帧和多帧的现象,追踪了一下发现是输入视频pts和dts有问题。

有个专业属于是:插帧算法
因为实际视频中,有变帧率视频

通过追踪代码发现是ffmpeg.c中的do_video_out()函数中

static void do_video_out(OutputFile *of,
                         OutputStream *ost,
                         AVFrame *next_picture)
{
    
    
    int ret, format_video_sync;
    AVPacket *pkt = ost->pkt;
    AVCodecContext *enc = ost->enc_ctx;
    AVRational frame_rate;
    int nb_frames, nb0_frames, i;
    double delta, delta0;
    double duration = 0;
    double sync_ipts = AV_NOPTS_VALUE;
    int frame_size = 0;
    InputStream *ist = NULL;
    AVFilterContext *filter = ost->filter->filter;

    init_output_stream_wrapper(ost, next_picture, 1);
    //如果输入视频pts和dts有问题,就会导致这里的sync_ipts 出现问题,正常情况下这里是正值,如果pts出现问题,就会导致这里出现负值
    sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);

    if (ost->source_index >= 0)
        ist = input_streams[ost->source_index];

    frame_rate = av_buffersink_get_frame_rate(filter);
    if (frame_rate.num > 0 && frame_rate.den > 0)
        duration = 1/(av_q2d(frame_rate) * av_q2d(enc->time_base));

    if(ist && ist->st->start_time != AV_NOPTS_VALUE && ist->st->first_dts != AV_NOPTS_VALUE && ost->frame_rate.num)
        duration = FFMIN(duration, 1/(av_q2d(ost->frame_rate) * av_q2d(enc->time_base)));

    if (!ost->filters_script &&
        !ost->filters &&
        (nb_filtergraphs == 0 || !filtergraphs[0]->graph_desc) &&
        next_picture &&
        ist &&
        lrintf(next_picture->pkt_duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)) > 0) {
    
    
        duration = lrintf(next_picture->pkt_duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base));
    }

    if (!next_picture) {
    
    
        //end, flushing
        nb0_frames = nb_frames = mid_pred(ost->last_nb0_frames[0],
                                          ost->last_nb0_frames[1],
                                          ost->last_nb0_frames[2]);
    } else {
    
    
    //这里很重要 delta0 -3.0000076293945312 = -7.62939453125e-06-3
        delta0 = sync_ipts - ost->sync_opts; // delta0 is the "drift" between the input frame (next_picture) and where it would fall in the output.
        
        delta  = delta0 + duration;
		//丢帧要进入到这里
        /* by default, we output a single frame */
        nb0_frames = 0; // tracks the number of times the PREVIOUS frame should be duplicated, mostly for variable framerate (VFR)
        nb_frames = 1;
		
        format_video_sync = video_sync_method;
        if (format_video_sync == VSYNC_AUTO) {
    
    
            if(!strcmp(of->ctx->oformat->name, "avi")) {
    
    
                format_video_sync = VSYNC_VFR;
            } else
            //进入到这里
                format_video_sync = (of->ctx->oformat->flags & AVFMT_VARIABLE_FPS) ? ((of->ctx->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH : VSYNC_VFR) : VSYNC_CFR;
            if (   ist
                && format_video_sync == VSYNC_CFR
                && input_files[ist->file_index]->ctx->nb_streams == 1
                && input_files[ist->file_index]->input_ts_offset == 0) {
    
    
                format_video_sync = VSYNC_VSCFR;
            }
            if (format_video_sync == VSYNC_CFR && copy_ts) {
    
    
                format_video_sync = VSYNC_VSCFR;
            }
        }
        //到这里 值为1
        ost->is_cfr = (format_video_sync == VSYNC_CFR || format_video_sync == VSYNC_VSCFR);

        if (delta0 < 0 &&
            delta > 0 &&
            format_video_sync != VSYNC_PASSTHROUGH &&
            format_video_sync != VSYNC_DROP) {
    
    
            if (delta0 < -0.6) {
    
    
                av_log(NULL, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0);
            } else
                av_log(NULL, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0);
            sync_ipts = ost->sync_opts;
            duration += delta0;
            delta0 = 0;
        }

        switch (format_video_sync) {
    
    
        case VSYNC_VSCFR:
            if (ost->frame_number == 0 && delta0 >= 0.5) {
    
    
                av_log(NULL, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0));
                delta = duration;
                delta0 = 0;
                ost->sync_opts = llrint(sync_ipts);
            }
        case VSYNC_CFR:
            // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c
            if (frame_drop_threshold && delta < frame_drop_threshold && ost->frame_number) {
    
    
                nb_frames = 0;
            } else if (delta < -1.1)
                nb_frames = 0;//进入到这里 delta=-2.00000007685956
            else if (delta > 1.1) {
    
    
                nb_frames = lrintf(delta);
                if (delta0 > 1.1)
                    nb0_frames = llrintf(delta0 - 0.6);
            }
            break;
        case VSYNC_VFR:
            if (delta <= -0.6)
                nb_frames = 0;
            else if (delta > 0.6)
                ost->sync_opts = llrint(sync_ipts);
            break;
        case VSYNC_DROP:
        case VSYNC_PASSTHROUGH:
            ost->sync_opts = llrint(sync_ipts);
            break;
        default:
            av_assert0(0);
        }
    }

    nb_frames = FFMIN(nb_frames, ost->max_frames - ost->frame_number);
    nb0_frames = FFMIN(nb0_frames, nb_frames);

    memmove(ost->last_nb0_frames + 1,
            ost->last_nb0_frames,
            sizeof(ost->last_nb0_frames[0]) * (FF_ARRAY_ELEMS(ost->last_nb0_frames) - 1));
    ost->last_nb0_frames[0] = nb0_frames;


    if (nb0_frames == 0 && ost->last_dropped) {
    
    
    //丢帧的时候进入到这里来了,ost->last_dropped=1 ost->last_frame->pts=0 ost->frame_number=3
        nb_frames_drop++;
        av_log(NULL, AV_LOG_VERBOSE,
               "*** dropping frame %d from stream %d at ts %"PRId64"\n",
               ost->frame_number, ost->st->index, ost->last_frame->pts);
    }
    if (nb_frames > (nb0_frames && ost->last_dropped) + (nb_frames > nb0_frames)) {
    
    
        if (nb_frames > dts_error_threshold * 30) {
    
    
            av_log(NULL, AV_LOG_ERROR, "%d frame duplication too large, skipping\n", nb_frames - 1);
            nb_frames_drop++;
            return;
        }
        nb_frames_dup += nb_frames - (nb0_frames && ost->last_dropped) - (nb_frames > nb0_frames);
        av_log(NULL, AV_LOG_VERBOSE, "*** %d dup!\n", nb_frames - 1);
        if (nb_frames_dup > dup_warning) {
    
    
            av_log(NULL, AV_LOG_WARNING, "More than %d frames duplicated\n", dup_warning);
            dup_warning *= 10;
        }
    }
    // nb_frames == nb0_frames这两个都等于0,那么后面就没有编码的操作了
    ost->last_dropped = nb_frames == nb0_frames && next_picture;

    /* duplicates frame if needed */
    for (i = 0; i < nb_frames; i++) {
    
    
        AVFrame *in_picture;
        int forced_keyframe = 0;
        double pts_time;

        if (i < nb0_frames && ost->last_frame) {
    
    
            in_picture = ost->last_frame;
        } else
            in_picture = next_picture;

        if (!in_picture)
            return;

        in_picture->pts = ost->sync_opts;

        if (!check_recording_time(ost))
            return;

        in_picture->quality = enc->global_quality;
        in_picture->pict_type = 0;

        if (ost->forced_kf_ref_pts == AV_NOPTS_VALUE &&
            in_picture->pts != AV_NOPTS_VALUE)
            ost->forced_kf_ref_pts = in_picture->pts;

        pts_time = in_picture->pts != AV_NOPTS_VALUE ?
            (in_picture->pts - ost->forced_kf_ref_pts) * av_q2d(enc->time_base) : NAN;
        if (ost->forced_kf_index < ost->forced_kf_count &&
            in_picture->pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
    
    
            ost->forced_kf_index++;
            forced_keyframe = 1;
        } else if (ost->forced_keyframes_pexpr) {
    
    
            double res;
            ost->forced_keyframes_expr_const_values[FKF_T] = pts_time;
            res = av_expr_eval(ost->forced_keyframes_pexpr,
                               ost->forced_keyframes_expr_const_values, NULL);
            ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n",
                    ost->forced_keyframes_expr_const_values[FKF_N],
                    ost->forced_keyframes_expr_const_values[FKF_N_FORCED],
                    ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N],
                    ost->forced_keyframes_expr_const_values[FKF_T],
                    ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T],
                    res);
            if (res) {
    
    
                forced_keyframe = 1;
                ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] =
                    ost->forced_keyframes_expr_const_values[FKF_N];
                ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] =
                    ost->forced_keyframes_expr_const_values[FKF_T];
                ost->forced_keyframes_expr_const_values[FKF_N_FORCED] += 1;
            }

            ost->forced_keyframes_expr_const_values[FKF_N] += 1;
        } else if (   ost->forced_keyframes
                   && !strncmp(ost->forced_keyframes, "source", 6)
                   && in_picture->key_frame==1
                   && !i) {
    
    
            forced_keyframe = 1;
        }

        if (forced_keyframe) {
    
    
            in_picture->pict_type = AV_PICTURE_TYPE_I;
            av_log(NULL, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time);
        }

        update_benchmark(NULL);
        if (debug_ts) {
    
    
            av_log(NULL, AV_LOG_INFO, "encoder <- type:video "
                   "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n",
                   av_ts2str(in_picture->pts), av_ts2timestr(in_picture->pts, &enc->time_base),
                   enc->time_base.num, enc->time_base.den);
        }

        ost->frames_encoded++;

        ret = avcodec_send_frame(enc, in_picture);
        if (ret < 0)
            goto error;
        // Make sure Closed Captions will not be duplicated
        av_frame_remove_side_data(in_picture, AV_FRAME_DATA_A53_CC);

        while (1) {
    
    
            av_packet_unref(pkt);
            ret = avcodec_receive_packet(enc, pkt);
            update_benchmark("encode_video %d.%d", ost->file_index, ost->index);
            if (ret == AVERROR(EAGAIN))
                break;
            if (ret < 0)
                goto error;

            if (debug_ts) {
    
    
                av_log(NULL, AV_LOG_INFO, "encoder -> type:video "
                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
                       av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base),
                       av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base));
            }

            if (pkt->pts == AV_NOPTS_VALUE && !(enc->codec->capabilities & AV_CODEC_CAP_DELAY))
                pkt->pts = ost->sync_opts;

            av_packet_rescale_ts(pkt, enc->time_base, ost->mux_timebase);

            if (debug_ts) {
    
    
                av_log(NULL, AV_LOG_INFO, "encoder -> type:video "
                    "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
                    av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->mux_timebase),
                    av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->mux_timebase));
            }

            frame_size = pkt->size;
            output_packet(of, pkt, ost, 0);

            /* if two pass, output log */
            if (ost->logfile && enc->stats_out) {
    
    
                fprintf(ost->logfile, "%s", enc->stats_out);
            }
        }
        ost->sync_opts++;
        /*
         * For video, number of frames in == number of packets out.
         * But there may be reordering, so we can't throw away frames on encoder
         * flush, we need to limit them here, before they go into encoder.
         */
        ost->frame_number++;

        if (vstats_filename && frame_size)
            do_video_stats(ost, frame_size);
    }

    if (!ost->last_frame)
        ost->last_frame = av_frame_alloc();
    av_frame_unref(ost->last_frame);
    if (next_picture && ost->last_frame)
        av_frame_ref(ost->last_frame, next_picture);
    else
        av_frame_free(&ost->last_frame);

    return;
error:
    av_log(NULL, AV_LOG_FATAL, "Video encoding failed\n");
    exit_program(1);
}

在这里插入图片描述

这个关键


Thread 1 "ffmpeg_g" hit Breakpoint 2, do_video_out (of=0x556550aa0800, ost=0x556550b89880, next_picture=0x556550d54740)
    at fftools/ffmpeg.c:1162
1162        sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture);
(gdb) s
adjust_frame_pts_to_encoder_tb (of=0x0, ost=0x0, frame=0x0) at fftools/ffmpeg.c:938
938     {
    
    
(gdb) n
939         double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision
(gdb) 
940         AVCodecContext *enc = ost->enc_ctx;
(gdb) 
941         if (!frame || frame->pts == AV_NOPTS_VALUE ||
(gdb) 
942             !enc || !ost->filter || !ost->filter->graph->graph)
(gdb) 
946             AVFilterContext *filter = ost->filter->filter;
(gdb) 
948             int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
(gdb) 
949             AVRational filter_tb = av_buffersink_get_time_base(filter);
(gdb) 
950             AVRational tb = enc->time_base;
(gdb) 
951             int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16);
(gdb) 
953             tb.den <<= extra_bits;
(gdb) 
955                 av_rescale_q(frame->pts, filter_tb, tb) -
(gdb) p start_time
$53 = 0
(gdb) p filter_tb
$54 = {
    
    num = 1, den = 1200000}
(gdb) p tb
$55 = {
    
    num = 1, den = 1966080}
(gdb) p extra_bits
$56 = 16
(gdb) p frame->pts
$57 = 48000
(gdb) p filter_tb
$58 = {
    
    num = 1, den = 1200000}
(gdb) n
956                 av_rescale_q(start_time, AV_TIME_BASE_Q, tb);
(gdb) 
955                 av_rescale_q(frame->pts, filter_tb, tb) -
(gdb) 
954             float_pts =
(gdb) 
957             float_pts /= 1 << extra_bits;
(gdb) 
959             float_pts += FFSIGN(float_pts) * 1.0 / (1<<17);
(gdb) 
962                 av_rescale_q(frame->pts, filter_tb, enc->time_base) -
(gdb) 
963                 av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base);
(gdb) 
962                 av_rescale_q(frame->pts, filter_tb, enc->time_base) -
(gdb) 
961             frame->pts =
(gdb) 
968         if (debug_ts) {
    
    
(gdb) 
977         return float_pts;
(gdb) p float_pts
$59 = 1.2000045776367188
(gdb) p frame->pts
$60 = 1
(gdb) 
说明:av_rescale_q用于计算Packet的PTS。av_rescale_q的返回值是一个很大的整数,
且每次计算的结果间隔很大。
不同于avcodec_encode_video改变AVCodecContext *avctx的pts(小整数,且间隔小)。
av_rescale_q(a,b,c)是用来把时间戳从一个时基调整到另外一个时基时候用的函数。
它基本的动作是计算a*b/c,但是这个函数还是必需的,因为直接计算会有溢出的情况发生。
AV_TIME_BASE_Q是AV_TIME_BASE作为分母后的版本。
它们是很不相同的:AV_TIME_BASE * time_in_seconds = avcodec_timestamp
而AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds
(注意AV_TIME_BASE_Q实际上是一个AVRational对象,
所以你必需使用avcodec中特定的q函数来处理它)
————————————————
版权声明:本文为CSDN博主「disadministrator」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/disadministrator/article/details/46652081

static double adjust_frame_pts_to_encoder_tb(OutputFile *of, OutputStream *ost,
                                             AVFrame *frame)
{
    
    
    double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision
    AVCodecContext *enc = ost->enc_ctx;
    if (!frame || frame->pts == AV_NOPTS_VALUE ||
        !enc || !ost->filter || !ost->filter->graph->graph)
        goto early_exit;

    {
    
    
    //进入到这里来
        AVFilterContext *filter = ost->filter->filter;
		//start_time=0
        int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
        AVRational filter_tb = av_buffersink_get_time_base(filter);
        AVRational tb = enc->time_base;
        int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16);

        tb.den <<= extra_bits;
        float_pts =
            av_rescale_q(frame->pts, filter_tb, tb) -
            av_rescale_q(start_time, AV_TIME_BASE_Q, tb);
        float_pts /= 1 << extra_bits;
        // avoid exact midoints to reduce the chance of rounding differences, this can be removed in case the fps code is changed to work with integers
        float_pts += FFSIGN(float_pts) * 1.0 / (1<<17);

        frame->pts =
            av_rescale_q(frame->pts, filter_tb, enc->time_base) -
            av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base);
    }

early_exit:

    if (debug_ts) {
    
    
        av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n",
               frame ? av_ts2str(frame->pts) : "NULL",
               frame ? av_ts2timestr(frame->pts, &enc->time_base) : "NULL",
               float_pts,
               enc ? enc->time_base.num : -1,
               enc ? enc->time_base.den : -1);
    }

    return float_pts;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43360707/article/details/128673366