@ -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 ?
if ( s - > duration < seek_timestamp )
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 ;
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 ;
}
if ( ret )
c - > seek_timestamp = AV_NOPTS_VALUE ;
}
}
return ret ;
return 0 ;
}
}
static int hls_probe ( AVProbeData * p )
static int hls_probe ( AVProbeData * p )