mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-28 20:53:54 +02:00
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 <anssi.hannula@iki.fi>
This commit is contained in:
parent
01b184e68d
commit
61e1a70278
@ -117,6 +117,9 @@ struct playlist {
|
|||||||
int id3_changed; /* ID3 tag data has changed at some point */
|
int id3_changed; /* ID3 tag data has changed at some point */
|
||||||
ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
|
ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
|
||||||
|
|
||||||
|
int64_t seek_timestamp;
|
||||||
|
int seek_flags;
|
||||||
|
|
||||||
/* Renditions associated with this playlist, if any.
|
/* Renditions associated with this playlist, if any.
|
||||||
* Alternative rendition playlists have a single rendition associated
|
* Alternative rendition playlists have a single rendition associated
|
||||||
* with them, and variant main Media Playlists may have
|
* with them, and variant main Media Playlists may have
|
||||||
@ -164,8 +167,6 @@ typedef struct HLSContext {
|
|||||||
int end_of_segment;
|
int end_of_segment;
|
||||||
int first_packet;
|
int first_packet;
|
||||||
int64_t first_timestamp;
|
int64_t first_timestamp;
|
||||||
int64_t seek_timestamp;
|
|
||||||
int seek_flags;
|
|
||||||
AVIOInterruptCB *interrupt_callback;
|
AVIOInterruptCB *interrupt_callback;
|
||||||
char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
|
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
|
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;
|
return NULL;
|
||||||
reset_packet(&pls->pkt);
|
reset_packet(&pls->pkt);
|
||||||
ff_make_absolute_url(pls->url, sizeof(pls->url), base, url);
|
ff_make_absolute_url(pls->url, sizeof(pls->url), base, url);
|
||||||
|
pls->seek_timestamp = AV_NOPTS_VALUE;
|
||||||
|
|
||||||
pls->is_id3_timestamped = -1;
|
pls->is_id3_timestamped = -1;
|
||||||
pls->id3_mpegts_timestamp = AV_NOPTS_VALUE;
|
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)
|
static int hls_read_header(AVFormatContext *s)
|
||||||
{
|
{
|
||||||
URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque;
|
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_packet = 1;
|
||||||
c->first_timestamp = AV_NOPTS_VALUE;
|
c->first_timestamp = AV_NOPTS_VALUE;
|
||||||
c->seek_timestamp = AV_NOPTS_VALUE;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
@ -1419,21 +1448,21 @@ start:
|
|||||||
get_timebase(pls), AV_TIME_BASE_Q);
|
get_timebase(pls), AV_TIME_BASE_Q);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->seek_timestamp == AV_NOPTS_VALUE)
|
if (pls->seek_timestamp == AV_NOPTS_VALUE)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (pls->pkt.dts == AV_NOPTS_VALUE) {
|
if (pls->pkt.dts == AV_NOPTS_VALUE) {
|
||||||
c->seek_timestamp = AV_NOPTS_VALUE;
|
pls->seek_timestamp = AV_NOPTS_VALUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tb = get_timebase(pls);
|
tb = get_timebase(pls);
|
||||||
ts_diff = av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE,
|
ts_diff = av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE,
|
||||||
tb.den, AV_ROUND_DOWN) -
|
tb.den, AV_ROUND_DOWN) -
|
||||||
c->seek_timestamp;
|
pls->seek_timestamp;
|
||||||
if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY ||
|
if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY ||
|
||||||
pls->pkt.flags & AV_PKT_FLAG_KEY)) {
|
pls->pkt.flags & AV_PKT_FLAG_KEY)) {
|
||||||
c->seek_timestamp = AV_NOPTS_VALUE;
|
pls->seek_timestamp = AV_NOPTS_VALUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
av_free_packet(&pls->pkt);
|
av_free_packet(&pls->pkt);
|
||||||
@ -1498,32 +1527,40 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
int64_t timestamp, int flags)
|
int64_t timestamp, int flags)
|
||||||
{
|
{
|
||||||
HLSContext *c = s->priv_data;
|
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)
|
if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->playlists[0]->finished)
|
||||||
return AVERROR(ENOSYS);
|
return AVERROR(ENOSYS);
|
||||||
|
|
||||||
c->seek_flags = flags;
|
seek_timestamp = stream_index < 0 ? timestamp :
|
||||||
c->seek_timestamp = stream_index < 0 ? timestamp :
|
|
||||||
av_rescale_rnd(timestamp, AV_TIME_BASE,
|
av_rescale_rnd(timestamp, AV_TIME_BASE,
|
||||||
s->streams[stream_index]->time_base.den,
|
s->streams[stream_index]->time_base.den,
|
||||||
flags & AVSEEK_FLAG_BACKWARD ?
|
flags & AVSEEK_FLAG_BACKWARD ?
|
||||||
AV_ROUND_DOWN : AV_ROUND_UP);
|
AV_ROUND_DOWN : AV_ROUND_UP);
|
||||||
timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ?
|
|
||||||
s->streams[stream_index]->time_base.den :
|
if (s->duration < seek_timestamp)
|
||||||
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;
|
|
||||||
return AVERROR(EIO);
|
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++) {
|
for (i = 0; i < c->n_playlists; i++) {
|
||||||
/* Reset reading */
|
/* Reset reading */
|
||||||
struct playlist *pls = c->playlists[i];
|
struct playlist *pls = c->playlists[i];
|
||||||
int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
|
|
||||||
0 : c->first_timestamp;
|
|
||||||
if (pls->input) {
|
if (pls->input) {
|
||||||
ffurl_close(pls->input);
|
ffurl_close(pls->input);
|
||||||
pls->input = NULL;
|
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. */
|
/* Reset the pos, to let the mpegts demuxer know we've seeked. */
|
||||||
pls->pb.pos = 0;
|
pls->pb.pos = 0;
|
||||||
|
|
||||||
/* Locate the segment that contains the target timestamp */
|
pls->seek_timestamp = seek_timestamp;
|
||||||
for (j = 0; j < pls->n_segments; j++) {
|
pls->seek_flags = flags;
|
||||||
if (timestamp >= pos &&
|
|
||||||
timestamp < pos + pls->segments[j]->duration) {
|
/* set closest segment seq_no for playlists not handled above */
|
||||||
pls->cur_seq_no = pls->start_seq_no + j;
|
if (valid_for != i)
|
||||||
ret = 0;
|
find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
pos += pls->segments[j]->duration;
|
|
||||||
}
|
return 0;
|
||||||
if (ret)
|
|
||||||
c->seek_timestamp = AV_NOPTS_VALUE;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hls_probe(AVProbeData *p)
|
static int hls_probe(AVProbeData *p)
|
||||||
|
Loading…
Reference in New Issue
Block a user