X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Futils.c;h=98110ab4b4915870b117df46d058f9ec9245c7e6;hb=9ee91c2f53dbc7cc61e65805d57e0a805b5752d7;hp=29a8fbea761b4c6bee54141cbb90c94b27059fab;hpb=bc874daea8273e2471f5a6f914cdc9cf97371a1c;p=ffmpeg diff --git a/libavformat/utils.c b/libavformat/utils.c index 29a8fbea761..98110ab4b49 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -48,6 +48,9 @@ int match_ext(const char *filename, const char *extensions) const char *ext, *p; char ext1[32], *q; + if(!filename) + return 0; + ext = strrchr(filename, '.'); if (ext) { ext++; @@ -264,6 +267,8 @@ void fifo_write(FifoBuffer *f, uint8_t *buf, int size, uint8_t **wptr_ptr) int filename_number_test(const char *filename) { char buf[1024]; + if(!filename) + return -1; return get_frame_filename(buf, sizeof(buf), filename, 1); } @@ -301,9 +306,9 @@ AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened) * open a media file from an IO stream. 'fmt' must be specified. */ -static const char* format_to_name(void* class_ptr) +static const char* format_to_name(void* ptr) { - AVFormatContext* fc = (AVFormatContext*) class_ptr; + AVFormatContext* fc = (AVFormatContext*) ptr; if(fc->iformat) return fc->iformat->name; else if(fc->oformat) return fc->oformat->name; else return "NULL"; @@ -316,7 +321,7 @@ AVFormatContext *av_alloc_format_context(void) AVFormatContext *ic; ic = av_mallocz(sizeof(AVFormatContext)); if (!ic) return ic; - ic->class = av_format_context_class; + ic->av_class = &av_format_context_class; return ic; } @@ -350,13 +355,6 @@ int av_open_input_stream(AVFormatContext **ic_ptr, ic->priv_data = NULL; } - /* default pts settings is MPEG like */ - av_set_pts_info(ic, 33, 1, 90000); - ic->last_pkt_pts = AV_NOPTS_VALUE; - ic->last_pkt_dts = AV_NOPTS_VALUE; - ic->last_pkt_stream_pts = AV_NOPTS_VALUE; - ic->last_pkt_stream_dts = AV_NOPTS_VALUE; - err = ic->iformat->read_header(ic, ap); if (err < 0) goto fail; @@ -494,7 +492,7 @@ int av_read_packet(AVFormatContext *s, AVPacket *pkt) wrapping is handled by considering the next PTS/DTS as a delta to the previous value. We handle the delta as a fraction to avoid any rounding errors. */ -static inline int64_t convert_timestamp_units(AVFormatContext *s, +static inline int64_t convert_timestamp_units(AVStream *s, int64_t *plast_pkt_pts, int *plast_pkt_pts_frac, int64_t *plast_pkt_stream_pts, @@ -510,17 +508,18 @@ static inline int64_t convert_timestamp_units(AVFormatContext *s, shift = 64 - s->pts_wrap_bits; delta_pts = ((stream_pts - *plast_pkt_stream_pts) << shift) >> shift; /* XXX: overflow possible but very unlikely as it is a delta */ - delta_pts = delta_pts * AV_TIME_BASE * s->pts_num; - pts = *plast_pkt_pts + (delta_pts / s->pts_den); - pts_frac = *plast_pkt_pts_frac + (delta_pts % s->pts_den); - if (pts_frac >= s->pts_den) { - pts_frac -= s->pts_den; + delta_pts = delta_pts * AV_TIME_BASE * s->time_base.num; + pts = *plast_pkt_pts + (delta_pts / s->time_base.den); + pts_frac = *plast_pkt_pts_frac + (delta_pts % s->time_base.den); + if (pts_frac >= s->time_base.den) { + pts_frac -= s->time_base.den; pts++; } } else { /* no previous pts, so no wrapping possible */ - pts = (int64_t)(((double)stream_pts * AV_TIME_BASE * s->pts_num) / - (double)s->pts_den); +// pts = av_rescale(stream_pts, (int64_t)AV_TIME_BASE * s->time_base.num, s->time_base.den); + pts = (int64_t)(((double)stream_pts * AV_TIME_BASE * s->time_base.num) / + (double)s->time_base.den); pts_frac = 0; } *plast_pkt_stream_pts = stream_pts; @@ -747,15 +746,17 @@ static int av_read_frame_internal(AVFormatContext *s, AVPacket *pkt) /* no more packets: really terminates parsing */ return ret; } + + st = s->streams[s->cur_pkt.stream_index]; /* convert the packet time stamp units and handle wrapping */ - s->cur_pkt.pts = convert_timestamp_units(s, - &s->last_pkt_pts, &s->last_pkt_pts_frac, - &s->last_pkt_stream_pts, + s->cur_pkt.pts = convert_timestamp_units(st, + &st->last_pkt_pts, &st->last_pkt_pts_frac, + &st->last_pkt_stream_pts, s->cur_pkt.pts); - s->cur_pkt.dts = convert_timestamp_units(s, - &s->last_pkt_dts, &s->last_pkt_dts_frac, - &s->last_pkt_stream_dts, + s->cur_pkt.dts = convert_timestamp_units(st, + &st->last_pkt_dts, &st->last_pkt_dts_frac, + &st->last_pkt_stream_dts, s->cur_pkt.dts); #if 0 if (s->cur_pkt.stream_index == 0) { @@ -767,14 +768,10 @@ static int av_read_frame_internal(AVFormatContext *s, AVPacket *pkt) (double)s->cur_pkt.dts / AV_TIME_BASE); } #endif - + /* duration field */ - if (s->cur_pkt.duration != 0) { - s->cur_pkt.duration = ((int64_t)s->cur_pkt.duration * AV_TIME_BASE * s->pts_num) / - s->pts_den; - } + s->cur_pkt.duration = av_rescale(s->cur_pkt.duration, AV_TIME_BASE * (int64_t)st->time_base.num, st->time_base.den); - st = s->streams[s->cur_pkt.stream_index]; s->cur_st = st; s->cur_ptr = s->cur_pkt.data; s->cur_len = s->cur_pkt.size; @@ -915,6 +912,9 @@ int av_add_index_entry(AVStream *st, memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index)); } st->nb_index_entries++; + }else{ + if(ie->pos == pos && distance < ie->min_distance) //dont reduce the distance + distance= ie->min_distance; } }else{ index= st->nb_index_entries++; @@ -994,6 +994,156 @@ int av_index_search_timestamp(AVStream *st, int wanted_timestamp) return a; } +#define DEBUG_SEEK + +int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts){ + AVInputFormat *avif= s->iformat; + int64_t pos_min, pos_max, pos, pos_limit; + int64_t ts_min, ts_max, ts; + int64_t start_pos; + int index, no_change; + AVStream *st; + + if (stream_index < 0) { + stream_index = av_find_default_stream_index(s); + if (stream_index < 0) + return -1; + } + +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "read_seek: %d %lld\n", stream_index, target_ts); +#endif + + ts_max= + ts_min= AV_NOPTS_VALUE; + pos_limit= -1; //gcc falsely says it may be uninitalized + + st= s->streams[stream_index]; + if(st->index_entries){ + AVIndexEntry *e; + + index= av_index_search_timestamp(st, target_ts); + e= &st->index_entries[index]; + + if(e->timestamp <= target_ts || e->pos == e->min_distance){ + pos_min= e->pos; + ts_min= e->timestamp; +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "unsing cached pos_min=0x%llx dts_min=%lld\n", + pos_min,ts_min); +#endif + }else{ + assert(index==0); + } + index++; + if(index < st->nb_index_entries){ + e= &st->index_entries[index]; + assert(e->timestamp >= target_ts); + pos_max= e->pos; + ts_max= e->timestamp; + pos_limit= pos_max - e->min_distance; +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "unsing cached pos_max=0x%llx pos_limit=0x%llx dts_max=%lld\n", + pos_max,pos_limit, ts_max); +#endif + } + } + + if(ts_min == AV_NOPTS_VALUE){ + pos_min = s->data_offset; + ts_min = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX); + if (ts_min == AV_NOPTS_VALUE) + return -1; + } + + if(ts_max == AV_NOPTS_VALUE){ + int step= 1024; + pos_max = url_filesize(url_fileno(&s->pb)) - 1; + do{ + pos_max -= step; + ts_max = avif->read_timestamp(s, stream_index, &pos_max, pos_max + step); + step += step; + }while(ts_max == AV_NOPTS_VALUE && pos_max >= step); + if (ts_max == AV_NOPTS_VALUE) + return -1; + + for(;;){ + int64_t tmp_pos= pos_max + 1; + int64_t tmp_ts= avif->read_timestamp(s, stream_index, &tmp_pos, INT64_MAX); + if(tmp_ts == AV_NOPTS_VALUE) + break; + ts_max= tmp_ts; + pos_max= tmp_pos; + } + pos_limit= pos_max; + } + + no_change=0; + while (pos_min < pos_limit) { +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_DEBUG, "pos_min=0x%llx pos_max=0x%llx dts_min=%lld dts_max=%lld\n", + pos_min, pos_max, + ts_min, ts_max); +#endif + assert(pos_limit <= pos_max); + + if(no_change==0){ + int64_t approximate_keyframe_distance= pos_max - pos_limit; + // interpolate position (better than dichotomy) + pos = (int64_t)((double)(pos_max - pos_min) * + (double)(target_ts - ts_min) / + (double)(ts_max - ts_min)) + pos_min - approximate_keyframe_distance; + }else if(no_change==1){ + // bisection, if interpolation failed to change min or max pos last time + pos = (pos_min + pos_limit)>>1; + }else{ + // linear search if bisection failed, can only happen if there are very few or no keframes between min/max + pos=pos_min; + } + if(pos <= pos_min) + pos= pos_min + 1; + else if(pos > pos_limit) + pos= pos_limit; + start_pos= pos; + + ts = avif->read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1 + if(pos == pos_max) + no_change++; + else + no_change=0; +#ifdef DEBUG_SEEK +av_log(s, AV_LOG_DEBUG, "%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit, start_pos, no_change); +#endif + assert(ts != AV_NOPTS_VALUE); + if (target_ts < ts) { + pos_limit = start_pos - 1; + pos_max = pos; + ts_max = ts; + } else { + pos_min = pos; + ts_min = ts; + /* check if we are lucky */ + if (target_ts == ts) + break; + } + } + + pos = pos_min; +#ifdef DEBUG_SEEK + pos_min = pos; + ts_min = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX); + pos_min++; + ts_max = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX); + av_log(s, AV_LOG_DEBUG, "pos=0x%llx %lld<=%lld<=%lld\n", + pos, ts_min, target_ts, ts_max); +#endif + /* do the seek */ + url_fseek(&s->pb, pos, SEEK_SET); + st->cur_dts = ts_min; + + return 0; +} + static int av_seek_frame_generic(AVFormatContext *s, int stream_index, int64_t timestamp) { @@ -1044,8 +1194,11 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp) if (ret >= 0) { return 0; } - - return av_seek_frame_generic(s, stream_index, timestamp); + + if(s->iformat->read_timestamp) + return av_seek_frame_binary(s, stream_index, timestamp); + else + return av_seek_frame_generic(s, stream_index, timestamp); } /*******************************************************/ @@ -1194,7 +1347,7 @@ static void av_estimate_timings_from_pts(AVFormatContext *ic) st = ic->streams[pkt->stream_index]; if (pkt->pts != AV_NOPTS_VALUE) { if (st->start_time == AV_NOPTS_VALUE) - st->start_time = (int64_t)((double)pkt->pts * ic->pts_num * (double)AV_TIME_BASE / ic->pts_den); + st->start_time = av_rescale(pkt->pts, st->time_base.num * (int64_t)AV_TIME_BASE, st->time_base.den); } av_free_packet(pkt); } @@ -1237,7 +1390,7 @@ static void av_estimate_timings_from_pts(AVFormatContext *ic) read_size += pkt->size; st = ic->streams[pkt->stream_index]; if (pkt->pts != AV_NOPTS_VALUE) { - end_time = (int64_t)((double)pkt->pts * ic->pts_num * (double)AV_TIME_BASE / ic->pts_den); + end_time = av_rescale(pkt->pts, st->time_base.num * (int64_t)AV_TIME_BASE, st->time_base.den); duration = end_time - st->start_time; if (duration > 0) { if (st->duration == AV_NOPTS_VALUE || @@ -1476,6 +1629,7 @@ int av_find_stream_info(AVFormatContext *ic) (st->codec.codec_id == CODEC_ID_FLV1 || st->codec.codec_id == CODEC_ID_H264 || st->codec.codec_id == CODEC_ID_H263 || + st->codec.codec_id == CODEC_ID_VORBIS || (st->codec.codec_id == CODEC_ID_MPEG4 && !st->need_parsing))) try_decode_frame(st, pkt->data, pkt->size); @@ -1614,6 +1768,14 @@ AVStream *av_new_stream(AVFormatContext *s, int id) st->id = id; st->start_time = AV_NOPTS_VALUE; st->duration = AV_NOPTS_VALUE; + + /* default pts settings is MPEG like */ + av_set_pts_info(st, 33, 1, 90000); + st->last_pkt_pts = AV_NOPTS_VALUE; + st->last_pkt_dts = AV_NOPTS_VALUE; + st->last_pkt_stream_pts = AV_NOPTS_VALUE; + st->last_pkt_stream_dts = AV_NOPTS_VALUE; + s->streams[s->nb_streams++] = st; return st; } @@ -1652,8 +1814,6 @@ int av_write_header(AVFormatContext *s) int ret, i; AVStream *st; - /* default pts settings is MPEG like */ - av_set_pts_info(s, 33, 1, 90000); ret = s->oformat->write_header(s); if (ret < 0) return ret; @@ -1665,11 +1825,11 @@ int av_write_header(AVFormatContext *s) switch (st->codec.codec_type) { case CODEC_TYPE_AUDIO: av_frac_init(&st->pts, 0, 0, - (int64_t)s->pts_num * st->codec.sample_rate); + (int64_t)st->time_base.num * st->codec.sample_rate); break; case CODEC_TYPE_VIDEO: av_frac_init(&st->pts, 0, 0, - (int64_t)s->pts_num * st->codec.frame_rate); + (int64_t)st->time_base.num * st->codec.frame_rate); break; default: break; @@ -1696,9 +1856,15 @@ int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf, int ret, frame_size; st = s->streams[stream_index]; - pts_mask = (1LL << s->pts_wrap_bits) - 1; - ret = s->oformat->write_packet(s, stream_index, buf, size, - st->pts.val & pts_mask); + pts_mask = (1LL << st->pts_wrap_bits) - 1; + + /* HACK/FIXME we skip all zero size audio packets so a encoder can pass pts by outputing zero size packets */ + if(st->codec.codec_type==CODEC_TYPE_AUDIO && size==0) + ret = 0; + else + ret = s->oformat->write_packet(s, stream_index, buf, size, + st->pts.val & pts_mask); + if (ret < 0) return ret; @@ -1706,14 +1872,15 @@ int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf, switch (st->codec.codec_type) { case CODEC_TYPE_AUDIO: frame_size = get_audio_frame_size(&st->codec, size); - if (frame_size >= 0) { - av_frac_add(&st->pts, - (int64_t)s->pts_den * frame_size); + + /* HACK/FIXME, we skip the initial 0-size packets as they are most likely equal to the encoder delay, + but it would be better if we had the real timestamps from the encoder */ + if (frame_size >= 0 && (size || st->pts.num!=st->pts.den>>1 || st->pts.val)) { + av_frac_add(&st->pts, (int64_t)st->time_base.den * frame_size); } break; case CODEC_TYPE_VIDEO: - av_frac_add(&st->pts, - (int64_t)s->pts_den * st->codec.frame_rate_base); + av_frac_add(&st->pts, (int64_t)st->time_base.den * st->codec.frame_rate_base); break; default: break; @@ -1745,13 +1912,13 @@ void dump_format(AVFormatContext *ic, int i, flags; char buf[256]; - av_log(ic, AV_LOG_DEBUG, "%s #%d, %s, %s '%s':\n", + av_log(NULL, AV_LOG_DEBUG, "%s #%d, %s, %s '%s':\n", is_output ? "Output" : "Input", index, is_output ? ic->oformat->name : ic->iformat->name, is_output ? "to" : "from", url); if (!is_output) { - av_log(ic, AV_LOG_DEBUG, " Duration: "); + av_log(NULL, AV_LOG_DEBUG, " Duration: "); if (ic->duration != AV_NOPTS_VALUE) { int hours, mins, secs, us; secs = ic->duration / AV_TIME_BASE; @@ -1760,23 +1927,23 @@ void dump_format(AVFormatContext *ic, secs %= 60; hours = mins / 60; mins %= 60; - av_log(ic, AV_LOG_DEBUG, "%02d:%02d:%02d.%01d", hours, mins, secs, + av_log(NULL, AV_LOG_DEBUG, "%02d:%02d:%02d.%01d", hours, mins, secs, (10 * us) / AV_TIME_BASE); } else { - av_log(ic, AV_LOG_DEBUG, "N/A"); + av_log(NULL, AV_LOG_DEBUG, "N/A"); } - av_log(ic, AV_LOG_DEBUG, ", bitrate: "); + av_log(NULL, AV_LOG_DEBUG, ", bitrate: "); if (ic->bit_rate) { - av_log(ic, AV_LOG_DEBUG,"%d kb/s", ic->bit_rate / 1000); + av_log(NULL, AV_LOG_DEBUG,"%d kb/s", ic->bit_rate / 1000); } else { - av_log(ic, AV_LOG_DEBUG, "N/A"); + av_log(NULL, AV_LOG_DEBUG, "N/A"); } - av_log(ic, AV_LOG_DEBUG, "\n"); + av_log(NULL, AV_LOG_DEBUG, "\n"); } for(i=0;inb_streams;i++) { AVStream *st = ic->streams[i]; avcodec_string(buf, sizeof(buf), &st->codec, is_output); - av_log(ic, AV_LOG_DEBUG, " Stream #%d.%d", index, i); + av_log(NULL, AV_LOG_DEBUG, " Stream #%d.%d", index, i); /* the pid is an important information, so we display it */ /* XXX: add a generic system */ if (is_output) @@ -1784,9 +1951,9 @@ void dump_format(AVFormatContext *ic, else flags = ic->iformat->flags; if (flags & AVFMT_SHOW_IDS) { - av_log(ic, AV_LOG_DEBUG, "[0x%x]", st->id); + av_log(NULL, AV_LOG_DEBUG, "[0x%x]", st->id); } - av_log(ic, AV_LOG_DEBUG, ": %s\n", buf); + av_log(NULL, AV_LOG_DEBUG, ": %s\n", buf); } } @@ -1864,7 +2031,7 @@ int parse_frame_rate(int *frame_rate, int *frame_rate_base, const char *arg) } else { /* Finally we give up and parse it as double */ - *frame_rate_base = DEFAULT_FRAME_RATE_BASE; + *frame_rate_base = DEFAULT_FRAME_RATE_BASE; //FIXME use av_d2q() *frame_rate = (int)(strtod(arg, 0) * (*frame_rate_base) + 0.5); } if (!*frame_rate || !*frame_rate_base) @@ -1899,6 +2066,8 @@ int64_t parse_date(const char *datestr, int duration) const char *q; int is_utc, len; char lastch; + +#undef time time_t now = time(0); len = strlen(datestr); @@ -2202,12 +2371,12 @@ void url_split(char *proto, int proto_size, * @param pts_num numerator to convert to seconds (MPEG: 1) * @param pts_den denominator to convert to seconds (MPEG: 90000) */ -void av_set_pts_info(AVFormatContext *s, int pts_wrap_bits, +void av_set_pts_info(AVStream *s, int pts_wrap_bits, int pts_num, int pts_den) { s->pts_wrap_bits = pts_wrap_bits; - s->pts_num = pts_num; - s->pts_den = pts_den; + s->time_base.num = pts_num; + s->time_base.den = pts_den; } /* fraction handling */