|
|
|
@@ -167,6 +167,7 @@ typedef struct HLSContext { |
|
|
|
int end_of_segment; |
|
|
|
int first_packet; |
|
|
|
int64_t first_timestamp; |
|
|
|
int64_t cur_timestamp; |
|
|
|
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 |
|
|
|
@@ -948,6 +949,13 @@ cleanup: |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
static int64_t default_reload_interval(struct playlist *pls) |
|
|
|
{ |
|
|
|
return pls->n_segments > 0 ? |
|
|
|
pls->segments[pls->n_segments - 1]->duration : |
|
|
|
pls->target_duration; |
|
|
|
} |
|
|
|
|
|
|
|
static int read_data(void *opaque, uint8_t *buf, int buf_size) |
|
|
|
{ |
|
|
|
struct playlist *v = opaque; |
|
|
|
@@ -962,9 +970,7 @@ restart: |
|
|
|
if (!v->input) { |
|
|
|
/* If this is a live stream and the reload interval has elapsed since |
|
|
|
* the last playlist reload, reload the playlists now. */ |
|
|
|
int64_t reload_interval = v->n_segments > 0 ? |
|
|
|
v->segments[v->n_segments - 1]->duration : |
|
|
|
v->target_duration; |
|
|
|
int64_t reload_interval = default_reload_interval(v); |
|
|
|
|
|
|
|
reload: |
|
|
|
if (!v->finished && |
|
|
|
@@ -1141,6 +1147,43 @@ static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls, |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int select_cur_seq_no(HLSContext *c, struct playlist *pls) |
|
|
|
{ |
|
|
|
int seq_no; |
|
|
|
|
|
|
|
if (!pls->finished && !c->first_packet && |
|
|
|
av_gettime() - pls->last_load_time >= default_reload_interval(pls)) |
|
|
|
/* reload the playlist since it was suspended */ |
|
|
|
parse_playlist(c, pls->url, pls, NULL); |
|
|
|
|
|
|
|
/* If playback is already in progress (we are just selecting a new |
|
|
|
* playlist) and this is a complete file, find the matching segment |
|
|
|
* by counting durations. */ |
|
|
|
if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) { |
|
|
|
find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no); |
|
|
|
return seq_no; |
|
|
|
} |
|
|
|
|
|
|
|
if (!pls->finished) { |
|
|
|
if (!c->first_packet && /* we are doing a segment selection during playback */ |
|
|
|
c->cur_seq_no >= pls->start_seq_no && |
|
|
|
c->cur_seq_no < pls->start_seq_no + pls->n_segments) |
|
|
|
/* While spec 3.4.3 says that we cannot assume anything about the |
|
|
|
* content at the same sequence number on different playlists, |
|
|
|
* in practice this seems to work and doing it otherwise would |
|
|
|
* require us to download a segment to inspect its timestamps. */ |
|
|
|
return c->cur_seq_no; |
|
|
|
|
|
|
|
/* If this is a live stream with more than 3 segments, start at the |
|
|
|
* third last segment. */ |
|
|
|
if (pls->n_segments > 3) |
|
|
|
return pls->start_seq_no + pls->n_segments - 3; |
|
|
|
} |
|
|
|
|
|
|
|
/* Otherwise just start on the first segment. */ |
|
|
|
return pls->start_seq_no; |
|
|
|
} |
|
|
|
|
|
|
|
static int hls_read_header(AVFormatContext *s) |
|
|
|
{ |
|
|
|
URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque; |
|
|
|
@@ -1149,6 +1192,10 @@ static int hls_read_header(AVFormatContext *s) |
|
|
|
|
|
|
|
c->interrupt_callback = &s->interrupt_callback; |
|
|
|
|
|
|
|
c->first_packet = 1; |
|
|
|
c->first_timestamp = AV_NOPTS_VALUE; |
|
|
|
c->cur_timestamp = AV_NOPTS_VALUE; |
|
|
|
|
|
|
|
// if the URL context is good, read important options we must broker later |
|
|
|
if (u && u->prot->priv_data_class) { |
|
|
|
// get the previous user agent & set back to null if string size is zero |
|
|
|
@@ -1231,12 +1278,7 @@ static int hls_read_header(AVFormatContext *s) |
|
|
|
pls->index = i; |
|
|
|
pls->needed = 1; |
|
|
|
pls->parent = s; |
|
|
|
|
|
|
|
/* If this is a live stream with more than 3 segments, start at the |
|
|
|
* third last segment. */ |
|
|
|
pls->cur_seq_no = pls->start_seq_no; |
|
|
|
if (!pls->finished && pls->n_segments > 3) |
|
|
|
pls->cur_seq_no = pls->start_seq_no + pls->n_segments - 3; |
|
|
|
pls->cur_seq_no = select_cur_seq_no(c, pls); |
|
|
|
|
|
|
|
pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE); |
|
|
|
ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls, |
|
|
|
@@ -1330,9 +1372,6 @@ static int hls_read_header(AVFormatContext *s) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
c->first_packet = 1; |
|
|
|
c->first_timestamp = AV_NOPTS_VALUE; |
|
|
|
|
|
|
|
return 0; |
|
|
|
fail: |
|
|
|
free_playlist_list(c); |
|
|
|
@@ -1361,9 +1400,14 @@ static int recheck_discard_flags(AVFormatContext *s, int first) |
|
|
|
if (pls->cur_needed && !pls->needed) { |
|
|
|
pls->needed = 1; |
|
|
|
changed = 1; |
|
|
|
pls->cur_seq_no = c->cur_seq_no; |
|
|
|
pls->cur_seq_no = select_cur_seq_no(c, pls); |
|
|
|
pls->pb.eof_reached = 0; |
|
|
|
av_log(s, AV_LOG_INFO, "Now receiving playlist %d\n", i); |
|
|
|
if (c->cur_timestamp != AV_NOPTS_VALUE) { |
|
|
|
/* catch up */ |
|
|
|
pls->seek_timestamp = c->cur_timestamp; |
|
|
|
pls->seek_flags = AVSEEK_FLAG_ANY; |
|
|
|
} |
|
|
|
av_log(s, AV_LOG_INFO, "Now receiving playlist %d, segment %d\n", i, pls->cur_seq_no); |
|
|
|
} else if (first && !pls->cur_needed && pls->needed) { |
|
|
|
if (pls->input) |
|
|
|
ffurl_close(pls->input); |
|
|
|
@@ -1505,9 +1549,16 @@ start: |
|
|
|
} |
|
|
|
/* If we got a packet, return it */ |
|
|
|
if (minplaylist >= 0) { |
|
|
|
*pkt = c->playlists[minplaylist]->pkt; |
|
|
|
pkt->stream_index += c->playlists[minplaylist]->stream_offset; |
|
|
|
struct playlist *pls = c->playlists[minplaylist]; |
|
|
|
*pkt = pls->pkt; |
|
|
|
pkt->stream_index += pls->stream_offset; |
|
|
|
reset_packet(&c->playlists[minplaylist]->pkt); |
|
|
|
|
|
|
|
if (pkt->dts != AV_NOPTS_VALUE) |
|
|
|
c->cur_timestamp = av_rescale_q(pkt->dts, |
|
|
|
pls->ctx->streams[pls->pkt.stream_index]->time_base, |
|
|
|
AV_TIME_BASE_Q); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
return AVERROR_EOF; |
|
|
|
@@ -1581,6 +1632,8 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, |
|
|
|
find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no); |
|
|
|
} |
|
|
|
|
|
|
|
c->cur_timestamp = seek_timestamp; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|