From 61e1a702783cafb4d8a4705424f3a2357b81c3c8 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 30 Dec 2013 11:13:56 +0200 Subject: [PATCH] avformat/hls: track seeking on a per-playlist basis Seeking needs to be tracked on a per-playlist basis, since the resyncing code in hls_read_packet() has to sync each playlist to the seek timestamp instead of stopping after the first playlist has reached it. Signed-off-by: Anssi Hannula --- libavformat/hls.c | 106 ++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index dd683e40d9..4e037c1b9a 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -117,6 +117,9 @@ struct playlist { int id3_changed; /* ID3 tag data has changed at some point */ ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */ + int64_t seek_timestamp; + int seek_flags; + /* Renditions associated with this playlist, if any. * Alternative rendition playlists have a single rendition associated * with them, and variant main Media Playlists may have @@ -164,8 +167,6 @@ typedef struct HLSContext { int end_of_segment; int first_packet; int64_t first_timestamp; - int64_t seek_timestamp; - int seek_flags; AVIOInterruptCB *interrupt_callback; char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context @@ -254,6 +255,7 @@ static struct playlist *new_playlist(HLSContext *c, const char *url, return NULL; reset_packet(&pls->pkt); ff_make_absolute_url(pls->url, sizeof(pls->url), base, url); + pls->seek_timestamp = AV_NOPTS_VALUE; pls->is_id3_timestamped = -1; pls->id3_mpegts_timestamp = AV_NOPTS_VALUE; @@ -1111,6 +1113,34 @@ static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pl } } +/* if timestamp was in valid range: returns 1 and sets seq_no + * if not: returns 0 and sets seq_no to closest segment */ +static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls, + int64_t timestamp, int *seq_no) +{ + int i; + int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? + 0 : c->first_timestamp; + + if (timestamp < pos) { + *seq_no = pls->start_seq_no; + return 0; + } + + for (i = 0; i < pls->n_segments; i++) { + int64_t diff = pos + pls->segments[i]->duration - timestamp; + if (diff > 0) { + *seq_no = pls->start_seq_no + i; + return 1; + } + pos += pls->segments[i]->duration; + } + + *seq_no = pls->start_seq_no + pls->n_segments - 1; + + return 0; +} + static int hls_read_header(AVFormatContext *s) { URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque; @@ -1302,7 +1332,6 @@ static int hls_read_header(AVFormatContext *s) c->first_packet = 1; c->first_timestamp = AV_NOPTS_VALUE; - c->seek_timestamp = AV_NOPTS_VALUE; return 0; fail: @@ -1419,21 +1448,21 @@ start: get_timebase(pls), AV_TIME_BASE_Q); } - if (c->seek_timestamp == AV_NOPTS_VALUE) + if (pls->seek_timestamp == AV_NOPTS_VALUE) break; if (pls->pkt.dts == AV_NOPTS_VALUE) { - c->seek_timestamp = AV_NOPTS_VALUE; + pls->seek_timestamp = AV_NOPTS_VALUE; break; } tb = get_timebase(pls); ts_diff = av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE, tb.den, AV_ROUND_DOWN) - - c->seek_timestamp; - if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY || + pls->seek_timestamp; + if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY || pls->pkt.flags & AV_PKT_FLAG_KEY)) { - c->seek_timestamp = AV_NOPTS_VALUE; + pls->seek_timestamp = AV_NOPTS_VALUE; break; } av_free_packet(&pls->pkt); @@ -1498,32 +1527,40 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { HLSContext *c = s->priv_data; - int i, j, ret; + int i; + int64_t seek_timestamp; + int valid_for = -1; if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->playlists[0]->finished) return AVERROR(ENOSYS); - c->seek_flags = flags; - c->seek_timestamp = stream_index < 0 ? timestamp : - av_rescale_rnd(timestamp, AV_TIME_BASE, - s->streams[stream_index]->time_base.den, - flags & AVSEEK_FLAG_BACKWARD ? - AV_ROUND_DOWN : AV_ROUND_UP); - timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ? - s->streams[stream_index]->time_base.den : - AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? - AV_ROUND_DOWN : AV_ROUND_UP); - if (s->duration < c->seek_timestamp) { - c->seek_timestamp = AV_NOPTS_VALUE; + seek_timestamp = stream_index < 0 ? timestamp : + av_rescale_rnd(timestamp, AV_TIME_BASE, + s->streams[stream_index]->time_base.den, + flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + + if (s->duration < seek_timestamp) return AVERROR(EIO); + + for (i = 0; i < c->n_playlists; i++) { + /* check first that the timestamp is valid for some playlist */ + struct playlist *pls = c->playlists[i]; + int seq_no; + if (find_timestamp_in_playlist(c, pls, seek_timestamp, &seq_no)) { + /* set segment now so we do not need to search again below */ + pls->cur_seq_no = seq_no; + valid_for = i; + break; + } } - ret = AVERROR(EIO); + if (valid_for < 0) + return AVERROR(EIO); + for (i = 0; i < c->n_playlists; i++) { /* Reset reading */ struct playlist *pls = c->playlists[i]; - int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? - 0 : c->first_timestamp; if (pls->input) { ffurl_close(pls->input); pls->input = NULL; @@ -1536,20 +1573,15 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, /* Reset the pos, to let the mpegts demuxer know we've seeked. */ pls->pb.pos = 0; - /* Locate the segment that contains the target timestamp */ - for (j = 0; j < pls->n_segments; j++) { - if (timestamp >= pos && - timestamp < pos + pls->segments[j]->duration) { - pls->cur_seq_no = pls->start_seq_no + j; - ret = 0; - break; - } - pos += pls->segments[j]->duration; - } - if (ret) - c->seek_timestamp = AV_NOPTS_VALUE; + pls->seek_timestamp = seek_timestamp; + pls->seek_flags = flags; + + /* set closest segment seq_no for playlists not handled above */ + if (valid_for != i) + find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no); } - return ret; + + return 0; } static int hls_probe(AVProbeData *p)