Merge commit '8e2ea691351c5079cdab245ff7bfa5c0f3e3bfe4'
authorJames Almer <jamrial@gmail.com>
Thu, 23 Mar 2017 19:52:42 +0000 (16:52 -0300)
committerJames Almer <jamrial@gmail.com>
Thu, 23 Mar 2017 19:52:42 +0000 (16:52 -0300)
* commit '8e2ea691351c5079cdab245ff7bfa5c0f3e3bfe4':
  lavf: use the new bitstream filter for extracting extradata

Merged-by: James Almer <jamrial@gmail.com>
1  2 
libavformat/internal.h
libavformat/utils.c

@@@ -178,12 -113,24 +178,21 @@@ struct AVStreamInternal 
  
      enum AVCodecID orig_codec_id;
  
 -#if FF_API_LAVF_AVCTX
 -    // whether the deprecated stream codec context needs
 -    // to be filled from the codec parameters
 -    int need_codec_update;
 -#endif
+     /* the context for extracting extradata in find_stream_info()
+      * inited=1/bsf=NULL signals that extracting is not possible (codec not
+      * supported) */
+     struct {
+         AVBSFContext *bsf;
+         AVPacket     *pkt;
+         int inited;
+     } extract_extradata;
 +    /**
 +     * Whether the internal avctx needs to be updated from codecpar (after a late change to codecpar)
 +     */
 +    int need_context_update;
  };
  
 -void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem);
 -
  #ifdef __GNUC__
  #define dynarray_add(tab, nb_ptr, elem)\
  do {\
@@@ -3172,208 -2083,113 +3172,306 @@@ static void compute_chapters_end(AVForm
  
  static int get_std_framerate(int i)
  {
 -    if (i < 60 * 12)
 +    if (i < 30*12)
          return (i + 1) * 1001;
 -    else
 -        return ((const int[]) { 24, 30, 60, 12, 15 })[i - 60 * 12] * 1000 * 12;
 +    i -= 30*12;
 +
 +    if (i < 30)
 +        return (i + 31) * 1001 * 12;
 +    i -= 30;
 +
 +    if (i < 3)
 +        return ((const int[]) { 80, 120, 240})[i] * 1001 * 12;
 +
 +    i -= 3;
 +
 +    return ((const int[]) { 24, 30, 60, 12, 15, 48 })[i] * 1000 * 12;
 +}
 +
 +/* Is the time base unreliable?
 + * This is a heuristic to balance between quick acceptance of the values in
 + * the headers vs. some extra checks.
 + * Old DivX and Xvid often have nonsense timebases like 1fps or 2fps.
 + * MPEG-2 commonly misuses field repeat flags to store different framerates.
 + * And there are "variable" fps files this needs to detect as well. */
 +static int tb_unreliable(AVCodecContext *c)
 +{
 +    if (c->time_base.den >= 101LL * c->time_base.num ||
 +        c->time_base.den <    5LL * c->time_base.num ||
 +        // c->codec_tag == AV_RL32("DIVX") ||
 +        // c->codec_tag == AV_RL32("XVID") ||
 +        c->codec_tag == AV_RL32("mp4v") ||
 +        c->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
 +        c->codec_id == AV_CODEC_ID_GIF ||
 +        c->codec_id == AV_CODEC_ID_HEVC ||
 +        c->codec_id == AV_CODEC_ID_H264)
 +        return 1;
 +    return 0;
 +}
 +
 +int ff_alloc_extradata(AVCodecParameters *par, int size)
 +{
 +    int ret;
 +
 +    if (size < 0 || size >= INT32_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
 +        par->extradata = NULL;
 +        par->extradata_size = 0;
 +        return AVERROR(EINVAL);
 +    }
 +    par->extradata = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
 +    if (par->extradata) {
 +        memset(par->extradata + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
 +        par->extradata_size = size;
 +        ret = 0;
 +    } else {
 +        par->extradata_size = 0;
 +        ret = AVERROR(ENOMEM);
 +    }
 +    return ret;
 +}
 +
 +int ff_get_extradata(AVFormatContext *s, AVCodecParameters *par, AVIOContext *pb, int size)
 +{
 +    int ret = ff_alloc_extradata(par, size);
 +    if (ret < 0)
 +        return ret;
 +    ret = avio_read(pb, par->extradata, size);
 +    if (ret != size) {
 +        av_freep(&par->extradata);
 +        par->extradata_size = 0;
 +        av_log(s, AV_LOG_ERROR, "Failed to read extradata of size %d\n", size);
 +        return ret < 0 ? ret : AVERROR_INVALIDDATA;
 +    }
 +
 +    return ret;
 +}
 +
 +int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t ts)
 +{
 +    int i, j;
 +    int64_t last = st->info->last_dts;
 +
 +    if (   ts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && ts > last
 +       && ts - (uint64_t)last < INT64_MAX) {
 +        double dts = (is_relative(ts) ?  ts - RELATIVE_TS_BASE : ts) * av_q2d(st->time_base);
 +        int64_t duration = ts - last;
 +
 +        if (!st->info->duration_error)
 +            st->info->duration_error = av_mallocz(sizeof(st->info->duration_error[0])*2);
 +        if (!st->info->duration_error)
 +            return AVERROR(ENOMEM);
 +
 +//         if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
 +//             av_log(NULL, AV_LOG_ERROR, "%f\n", dts);
 +        for (i = 0; i<MAX_STD_TIMEBASES; i++) {
 +            if (st->info->duration_error[0][1][i] < 1e10) {
 +                int framerate = get_std_framerate(i);
 +                double sdts = dts*framerate/(1001*12);
 +                for (j= 0; j<2; j++) {
 +                    int64_t ticks = llrint(sdts+j*0.5);
 +                    double error= sdts - ticks + j*0.5;
 +                    st->info->duration_error[j][0][i] += error;
 +                    st->info->duration_error[j][1][i] += error*error;
 +                }
 +            }
 +        }
 +        st->info->duration_count++;
 +        st->info->rfps_duration_sum += duration;
 +
 +        if (st->info->duration_count % 10 == 0) {
 +            int n = st->info->duration_count;
 +            for (i = 0; i<MAX_STD_TIMEBASES; i++) {
 +                if (st->info->duration_error[0][1][i] < 1e10) {
 +                    double a0     = st->info->duration_error[0][0][i] / n;
 +                    double error0 = st->info->duration_error[0][1][i] / n - a0*a0;
 +                    double a1     = st->info->duration_error[1][0][i] / n;
 +                    double error1 = st->info->duration_error[1][1][i] / n - a1*a1;
 +                    if (error0 > 0.04 && error1 > 0.04) {
 +                        st->info->duration_error[0][1][i] = 2e10;
 +                        st->info->duration_error[1][1][i] = 2e10;
 +                    }
 +                }
 +            }
 +        }
 +
 +        // ignore the first 4 values, they might have some random jitter
 +        if (st->info->duration_count > 3 && is_relative(ts) == is_relative(last))
 +            st->info->duration_gcd = av_gcd(st->info->duration_gcd, duration);
 +    }
 +    if (ts != AV_NOPTS_VALUE)
 +        st->info->last_dts = ts;
 +
 +    return 0;
 +}
 +
 +void ff_rfps_calculate(AVFormatContext *ic)
 +{
 +    int i, j;
 +
 +    for (i = 0; i < ic->nb_streams; i++) {
 +        AVStream *st = ic->streams[i];
 +
 +        if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
 +            continue;
 +        // the check for tb_unreliable() is not completely correct, since this is not about handling
 +        // an unreliable/inexact time base, but a time base that is finer than necessary, as e.g.
 +        // ipmovie.c produces.
 +        if (tb_unreliable(st->internal->avctx) && st->info->duration_count > 15 && st->info->duration_gcd > FFMAX(1, st->time_base.den/(500LL*st->time_base.num)) && !st->r_frame_rate.num)
 +            av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, st->time_base.den, st->time_base.num * st->info->duration_gcd, INT_MAX);
 +        if (st->info->duration_count>1 && !st->r_frame_rate.num
 +            && tb_unreliable(st->internal->avctx)) {
 +            int num = 0;
 +            double best_error= 0.01;
 +            AVRational ref_rate = st->r_frame_rate.num ? st->r_frame_rate : av_inv_q(st->time_base);
 +
 +            for (j= 0; j<MAX_STD_TIMEBASES; j++) {
 +                int k;
 +
 +                if (st->info->codec_info_duration &&
 +                    st->info->codec_info_duration*av_q2d(st->time_base) < (1001*11.5)/get_std_framerate(j))
 +                    continue;
 +                if (!st->info->codec_info_duration && get_std_framerate(j) < 1001*12)
 +                    continue;
 +
 +                if (av_q2d(st->time_base) * st->info->rfps_duration_sum / st->info->duration_count < (1001*12.0 * 0.8)/get_std_framerate(j))
 +                    continue;
 +
 +                for (k= 0; k<2; k++) {
 +                    int n = st->info->duration_count;
 +                    double a= st->info->duration_error[k][0][j] / n;
 +                    double error= st->info->duration_error[k][1][j]/n - a*a;
 +
 +                    if (error < best_error && best_error> 0.000000001) {
 +                        best_error= error;
 +                        num = get_std_framerate(j);
 +                    }
 +                    if (error < 0.02)
 +                        av_log(ic, AV_LOG_DEBUG, "rfps: %f %f\n", get_std_framerate(j) / 12.0/1001, error);
 +                }
 +            }
 +            // do not increase frame rate by more than 1 % in order to match a standard rate.
 +            if (num && (!ref_rate.num || (double)num/(12*1001) < 1.01 * av_q2d(ref_rate)))
 +                av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, num, 12*1001, INT_MAX);
 +        }
 +        if (   !st->avg_frame_rate.num
 +            && st->r_frame_rate.num && st->info->rfps_duration_sum
 +            && st->info->codec_info_duration <= 0
 +            && st->info->duration_count > 2
 +            && fabs(1.0 / (av_q2d(st->r_frame_rate) * av_q2d(st->time_base)) - st->info->rfps_duration_sum / (double)st->info->duration_count) <= 1.0
 +            ) {
 +            av_log(ic, AV_LOG_DEBUG, "Setting avg frame rate based on r frame rate\n");
 +            st->avg_frame_rate = st->r_frame_rate;
 +        }
 +
 +        av_freep(&st->info->duration_error);
 +        st->info->last_dts = AV_NOPTS_VALUE;
 +        st->info->duration_count = 0;
 +        st->info->rfps_duration_sum = 0;
 +    }
  }
  
+ static int extract_extradata_init(AVStream *st)
+ {
+     AVStreamInternal *i = st->internal;
+     const AVBitStreamFilter *f;
+     int ret;
+     f = av_bsf_get_by_name("extract_extradata");
+     if (!f)
+         goto finish;
+     i->extract_extradata.pkt = av_packet_alloc();
+     if (!i->extract_extradata.pkt)
+         return AVERROR(ENOMEM);
+     ret = av_bsf_alloc(f, &i->extract_extradata.bsf);
+     if (ret < 0)
+         goto fail;
+     ret = avcodec_parameters_copy(i->extract_extradata.bsf->par_in,
+                                   st->codecpar);
+     if (ret < 0)
+         goto fail;
+     i->extract_extradata.bsf->time_base_in = st->time_base;
+     /* if init fails here, we assume extracting extradata is just not
+      * supported for this codec, so we return success */
+     ret = av_bsf_init(i->extract_extradata.bsf);
+     if (ret < 0) {
+         av_bsf_free(&i->extract_extradata.bsf);
+         ret = 0;
+     }
+ finish:
+     i->extract_extradata.inited = 1;
+     return 0;
+ fail:
+     av_bsf_free(&i->extract_extradata.bsf);
+     av_packet_free(&i->extract_extradata.pkt);
+     return ret;
+ }
+ static int extract_extradata(AVStream *st, AVPacket *pkt)
+ {
+     AVStreamInternal *i = st->internal;
+     AVPacket *pkt_ref;
+     int ret;
+     if (!i->extract_extradata.inited) {
+         ret = extract_extradata_init(st);
+         if (ret < 0)
+             return ret;
+     }
+     if (i->extract_extradata.inited && !i->extract_extradata.bsf)
+         return 0;
+     pkt_ref = i->extract_extradata.pkt;
+     ret = av_packet_ref(pkt_ref, pkt);
+     if (ret < 0)
+         return ret;
+     ret = av_bsf_send_packet(i->extract_extradata.bsf, pkt_ref);
+     if (ret < 0) {
+         av_packet_unref(pkt_ref);
+         return ret;
+     }
+     while (ret >= 0 && !i->avctx->extradata) {
+         int extradata_size;
+         uint8_t *extradata;
+         ret = av_bsf_receive_packet(i->extract_extradata.bsf, pkt_ref);
+         if (ret < 0) {
+             if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
+                 return ret;
+             continue;
+         }
+         extradata = av_packet_get_side_data(pkt_ref, AV_PKT_DATA_NEW_EXTRADATA,
+                                             &extradata_size);
+         if (extradata) {
+             i->avctx->extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
+             if (!i->avctx->extradata) {
+                 av_packet_unref(pkt_ref);
+                 return AVERROR(ENOMEM);
+             }
+             memcpy(i->avctx->extradata, extradata, extradata_size);
+             i->avctx->extradata_size = extradata_size;
+         }
+         av_packet_unref(pkt_ref);
+     }
+     return 0;
+ }
  int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
  {
 -    int i, count, ret, read_size, j;
 +    int i, count = 0, ret = 0, j;
 +    int64_t read_size;
      AVStream *st;
      AVCodecContext *avctx;
      AVPacket pkt1, *pkt;
@@@ -3514,27 -2285,20 +3612,29 @@@ FF_ENABLE_DEPRECATION_WARNING
               * the correct fps. */
              if (av_q2d(st->time_base) > 0.0005)
                  fps_analyze_framecount *= 2;
 +            if (!tb_unreliable(st->internal->avctx))
 +                fps_analyze_framecount = 0;
              if (ic->fps_probe_size >= 0)
                  fps_analyze_framecount = ic->fps_probe_size;
 +            if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
 +                fps_analyze_framecount = 0;
              /* variable fps and no guess at the real fps */
 -            if (!st->avg_frame_rate.num &&
 -                st->codec_info_nb_frames < fps_analyze_framecount &&
 -                st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
 -                break;
 +            if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
 +                st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 +                int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
 +                    st->info->codec_info_duration_fields/2 :
 +                    st->info->duration_count;
 +                if (count < fps_analyze_framecount)
 +                    break;
 +            }
-             if (st->parser && st->parser->parser->split &&
-                 !st->internal->avctx->extradata)
+             if (!st->codecpar->extradata &&
+                 !st->internal->avctx->extradata &&
+                 (!st->internal->extract_extradata.inited ||
+                  st->internal->extract_extradata.bsf))
                  break;
              if (st->first_dts == AV_NOPTS_VALUE &&
 -                st->codec_info_nb_frames < ic->max_ts_probe &&
 +                !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
 +                st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
                  (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                   st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
                  break;
                      av_packet_unref(pkt);
                  break;
              }
 +            if (pkt->duration) {
 +                if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && pkt->pts >= st->start_time) {
 +                    st->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, st->info->codec_info_duration + pkt->duration);
 +                } else
 +                    st->info->codec_info_duration += pkt->duration;
 +                st->info->codec_info_duration_fields += st->parser && st->need_parsing && avctx->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2;
 +            }
          }
-         if (st->parser && st->parser->parser->split && !avctx->extradata) {
-             int i = st->parser->parser->split(avctx, pkt->data, pkt->size);
-             if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
-                 avctx->extradata_size = i;
-                 avctx->extradata      = av_mallocz(avctx->extradata_size +
-                                                    AV_INPUT_BUFFER_PADDING_SIZE);
-                 if (!avctx->extradata)
-                     return AVERROR(ENOMEM);
-                 memcpy(avctx->extradata, pkt->data,
-                        avctx->extradata_size);
-             }
 +#if FF_API_R_FRAME_RATE
 +        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
 +            ff_rfps_add_frame(ic, st, pkt->dts);
 +#endif
+         if (!st->internal->avctx->extradata) {
+             ret = extract_extradata(st, pkt);
+             if (ret < 0)
+                 goto find_stream_info_err;
          }
  
          /* If still no information, we try to open the codec and to
@@@ -3944,16 -2556,14 +4037,18 @@@ FF_ENABLE_DEPRECATION_WARNING
          st->internal->avctx_inited = 0;
      }
  
 -    estimate_timings(ic, old_offset);
 -
  find_stream_info_err:
      for (i = 0; i < ic->nb_streams; i++) {
 +        st = ic->streams[i];
 +        if (st->info)
 +            av_freep(&st->info->duration_error);
          av_freep(&ic->streams[i]->info);
+         av_bsf_free(&ic->streams[i]->internal->extract_extradata.bsf);
+         av_packet_free(&ic->streams[i]->internal->extract_extradata.pkt);
      }
 +    if (ic->pb)
 +        av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n",
 +               avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
      return ret;
  }
  
@@@ -4133,10 -2670,8 +4228,12 @@@ static void free_stream(AVStream **pst
  
      if (st->internal) {
          avcodec_free_context(&st->internal->avctx);
 +        for (i = 0; i < st->internal->nb_bsfcs; i++) {
 +            av_bsf_free(&st->internal->bsfcs[i]);
 +            av_freep(&st->internal->bsfcs);
 +        }
+         av_bsf_free(&st->internal->extract_extradata.bsf);
+         av_packet_free(&st->internal->extract_extradata.pkt);
      }
      av_freep(&st->internal);