ffmpeg读取文件时长

主要介绍的函数:estimate_timingsestimate_timings_from_ptsupdate_stream_timings.

estimate_timings

 static void estimate_timings(AVFormatContext *ic, int64_t old_offset)

{
    int64_t file_size;

    /* get the file size, if possible */
    if (ic->iformat->flags & AVFMT_NOFILE) {
        file_size = 0;
    } else {
        file_size = avio_size(ic->pb);
        file_size = FFMAX(0, file_size);
    }
    if ((!strcmp(ic->iformat->name, "mpeg") ||
         !strcmp(ic->iformat->name, "mpegts")) &&
        file_size && ic->pb->seekable) {

        /* get accurate estimate from the PTSes */

          //mpeg,mpegts文件通过pts获取时长

        estimate_timings_from_pts(ic, old_offset);     
        ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;

    } else if (has_duration(ic)) {

            //MP4等会进入这种情况。

        /* at least one component has timings - we use them for all
         * the components */
        fill_all_stream_timings(ic);
        ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;

    } else {

            //不准确,通过文件大小除以码率的方式获取,如aac音频文件。

        /* less precise: use bitrate info */
        estimate_timings_from_bit_rate(ic);
        ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE;
    }
        update_stream_timings(ic);   //得到时长后,即可得到码率。
    {
        int i;
        AVStream av_unused *st;
        for (i = 0; i < ic->nb_streams; i++) {
            st = ic->streams[i];
            av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %0.3f duration: %0.3f\n", i,
                   (double) st->start_time * av_q2d(st->time_base),
                   (double) st->duration   * av_q2d(st->time_base));
        }
        av_log(ic, AV_LOG_TRACE,
                "format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n",
                (double) ic->start_time / AV_TIME_BASE,
                (double) ic->duration   / AV_TIME_BASE,
                (int64_t)ic->bit_rate / 1000);
    }
}

获取ts的时长 estimate_timings_from_pts

static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)

{

   AVPacket pkt1, *pkt = &pkt1;

   AVStream *st;

   int num, den, read_size, i, ret;

   int found_duration = 0;

   int is_end;

   int64_t filesize, offset, duration;

   int retry = 0;

   /* flush packet queue */

   flush_packet_queue(ic);

   for (i = 0; i < ic->nb_streams; i++) {

       st = ic->streams[i];

       if (st->start_time == AV_NOPTS_VALUE &&

           st->first_dts == AV_NOPTS_VALUE &&

           st->codecpar->codec_type !=AVMEDIA_TYPE_UNKNOWN)

           av_log(ic, AV_LOG_WARNING,

                   "start time for stream%d is not set in estimate_timings_from_pts\n", i);

       if (st->parser) {

           av_parser_close(st->parser);

            st->parser = NULL;

       }

    }

   av_opt_set(ic, "skip_changes", "1",AV_OPT_SEARCH_CHILDREN);

   /* estimate the end time (duration) */

   /* XXX: may need to support wrapping */

   filesize = ic->pb ? avio_size(ic->pb) : 0;

   do {

        is_end = found_duration;

                   //DURATION_MAX_READ_SIZE在高码率时,会到不到最大PTS,以为B帧是先解码后播放,所以PTS不是递增的,导致得到时长变小。

       offset = filesize - (DURATION_MAX_READ_SIZE << retry);

       if (offset < 0)

           offset = 0;

                   //从剩下的250K中找。

       avio_seek(ic->pb, offset, SEEK_SET);

       read_size = 0;

       for (;;) {

           if (read_size >= DURATION_MAX_READ_SIZE << (FFMAX(retry - 1,0)))

                break;

 

           do {

                ret = ff_read_packet(ic, pkt);

           } while (ret == AVERROR(EAGAIN));

            if (ret != 0)

                break;

           read_size += pkt->size;

           st         =ic->streams[pkt->stream_index];

           if (pkt->pts != AV_NOPTS_VALUE &&

                (st->start_time !=AV_NOPTS_VALUE ||

                 st->first_dts  != AV_NOPTS_VALUE)) {

                if (pkt->duration == 0) {

                  // pkt->duration都是0

                   ff_compute_frame_duration(ic, &num, &den, st, st->parser,pkt);

                    if (den && num) {

                        //pkt->duration等于单帧的时长,如当1秒25帧时为3600。

                        pkt->duration =av_rescale_rnd(1,

                                           num* (int64_t) st->time_base.den,

                                           den* (int64_t) st->time_base.num,

                                           AV_ROUND_DOWN);

                    }

                }

                duration = pkt->pts +pkt->duration;

                found_duration = 1;

                if (st->start_time !=AV_NOPTS_VALUE)

                    duration -=st->start_time;  

                   //timelen=当前PTS-开始PTS+单帧时长。音频算音频的,视频算视频的

                else

                    duration -=st->first_dts;

                if (duration > 0) {

                    if (st->duration ==AV_NOPTS_VALUE || st->info->last_duration<= 0 ||

                        (st->duration < duration &&FFABS(duration - st->info->last_duration) < 60LL*st->time_base.den/ st->time_base.num))

                    st->duration =duration;

                   //st->duration为最大PTS-开始PTS+单帧时长

                   st->info->last_duration = duration;

                }

           }

           av_packet_unref(pkt);

       }

       /* check if all audio/video streams have valid duration */

       if (!is_end) {

           is_end = 1;

           for (i = 0; i < ic->nb_streams; i++) {

               st = ic->streams[i];

                switch(st->codecpar->codec_type) {

                    case AVMEDIA_TYPE_VIDEO:

                    case AVMEDIA_TYPE_AUDIO:

                        if (st->duration ==AV_NOPTS_VALUE)

                            is_end = 0;

                }

           }

       }

    }while (!is_end &&

            offset &&

            ++retry <= DURATION_MAX_RETRY);

   av_opt_set(ic, "skip_changes", "0",AV_OPT_SEARCH_CHILDREN);

   /* warn about audio/video streams which duration could not be estimated*/

   for (i = 0; i < ic->nb_streams; i++) {

       st = ic->streams[i];

       if (st->duration == AV_NOPTS_VALUE) {

           switch (st->codecpar->codec_type) {

           case AVMEDIA_TYPE_VIDEO:

           case AVMEDIA_TYPE_AUDIO:

                if (st->start_time !=AV_NOPTS_VALUE || st->first_dts  !=AV_NOPTS_VALUE) {

                    av_log(ic, AV_LOG_DEBUG,"stream %d : no PTS found at end of file, duration not set\n", i);

                } else

                   av_log(ic,AV_LOG_DEBUG, "stream %d : no TS found at start of file, duration notset\n", i);

           }

       }

    }

   fill_all_stream_timings(ic);

   avio_seek(ic->pb, old_offset, SEEK_SET);

   for (i = 0; i < ic->nb_streams; i++) {

       int j;

       st              =ic->streams[i];

       st->cur_dts     =st->first_dts;

       st->last_IP_pts = AV_NOPTS_VALUE;

       st->last_dts_for_order_check = AV_NOPTS_VALUE;

       for (j = 0; j < MAX_REORDER_DELAY + 1; j++)

           st->pts_buffer[j] =AV_NOPTS_VALUE;

    }

}

update_stream_timings

static void update_stream_timings(AVFormatContext *ic)
{
    int64_t start_time, start_time1, start_time_text, end_time, end_time1;
    int64_t duration, duration1, filesize;
    int i;
    AVStream *st;
    AVProgram *p;

    start_time = INT64_MAX;
    start_time_text = INT64_MAX;
    end_time   = INT64_MIN;
    duration   = INT64_MIN;
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) {
            start_time1 = av_rescale_q(st->start_time, st->time_base,
                                       AV_TIME_BASE_Q);
            if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
                if (start_time1 < start_time_text)
                    start_time_text = start_time1;
            } else
                start_time = FFMIN(start_time, start_time1);
            end_time1 = av_rescale_q_rnd(st->duration, st->time_base,
                                         AV_TIME_BASE_Q,
                                         AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
            if (end_time1 != AV_NOPTS_VALUE && start_time1 <= INT64_MAX - end_time1) {
                end_time1 += start_time1;
                end_time = FFMAX(end_time, end_time1);
            }
            for (p = NULL; (p = av_find_program_from_stream(ic, p, i)); ) {
                if (p->start_time == AV_NOPTS_VALUE || p->start_time > start_time1)
                    p->start_time = start_time1;
                if (p->end_time < end_time1)
                    p->end_time = end_time1;
            }
        }

        if (st->duration != AV_NOPTS_VALUE) {

            //时间转换

            duration1 = av_rescale_q(st->duration, st->time_base,
                                     AV_TIME_BASE_Q);
            duration  = FFMAX(duration, duration1);
        }

    }

  // 如下,比如有三个音轨 ,这样duration就是最大的5015500。


    if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE))
        start_time = start_time_text;
    else if (start_time > start_time_text)
        av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE);

    if (start_time != INT64_MAX) {
        ic->start_time = start_time;
        if (end_time != INT64_MIN) {
            if (ic->nb_programs) {
                for (i = 0; i < ic->nb_programs; i++) {
                    p = ic->programs[i];
                    if (p->start_time != AV_NOPTS_VALUE && p->end_time > p->start_time)
                        duration = FFMAX(duration, p->end_time - p->start_time);
                }
            } else
                duration = FFMAX(duration, end_time - start_time);
        }
    }
    if (duration != INT64_MIN && duration > 0 && ic->duration == AV_NOPTS_VALUE) {
        ic->duration = duration;
    }
    if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration > 0) {
        /* compute the bitrate */
        double bitrate = (double) filesize * 8.0 * AV_TIME_BASE /
                         (double) ic->duration;
        if (bitrate >= 0 && bitrate <= INT64_MAX)
            ic->bit_rate = bitrate;
    }
}

你可能感兴趣的:(ffmpeg读取文件时长)