ffmpeg | branch: master | James Almer <jamr...@gmail.com> | Thu Mar 23 16:52:42 2017 -0300| [1c193ac1f9cfe703d6a1c36795f309ba5d14bf6e] | committer: James Almer
Merge commit '8e2ea691351c5079cdab245ff7bfa5c0f3e3bfe4' * commit '8e2ea691351c5079cdab245ff7bfa5c0f3e3bfe4': lavf: use the new bitstream filter for extracting extradata Merged-by: James Almer <jamr...@gmail.com> > http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=1c193ac1f9cfe703d6a1c36795f309ba5d14bf6e --- libavformat/internal.h | 9 ++++ libavformat/utils.c | 123 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 13 deletions(-) diff --git a/libavformat/internal.h b/libavformat/internal.h index 63a1724..c856945 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -178,6 +178,15 @@ struct AVStreamInternal { enum AVCodecID orig_codec_id; + /* 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) */ diff --git a/libavformat/utils.c b/libavformat/utils.c index a059046..e851251 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -3370,6 +3370,104 @@ void ff_rfps_calculate(AVFormatContext *ic) } } +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 = 0, ret = 0, j; @@ -3529,8 +3627,10 @@ FF_ENABLE_DEPRECATION_WARNINGS 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 && !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) && @@ -3680,17 +3780,10 @@ FF_ENABLE_DEPRECATION_WARNINGS if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ff_rfps_add_frame(ic, st, pkt->dts); #endif - 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 (!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 @@ -3950,6 +4043,8 @@ find_stream_info_err: 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", @@ -4137,6 +4232,8 @@ static void free_stream(AVStream **pst) 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); ====================================================================== diff --cc libavformat/internal.h index 63a1724,52cd29b..c856945 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@@ -178,12 -113,24 +178,21 @@@ struct AVStreamInternal enum AVCodecID orig_codec_id; + /* 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; + -#if FF_API_LAVF_AVCTX - // whether the deprecated stream codec context needs - // to be filled from the codec parameters - int need_codec_update; -#endif + /** + * 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 {\ diff --cc libavformat/utils.c index a059046,1c93326..e851251 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@@ -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; @@@ -3668,29 -2430,11 +3768,22 @@@ 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 FF_API_R_FRAME_RATE + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + ff_rfps_add_frame(ic, st, pkt->dts); +#endif - 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 (!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); _______________________________________________ ffmpeg-cvslog mailing list ffmpeg-cvslog@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog