@ -63,6 +63,7 @@ typedef enum HLSFlags {
HLS_DISCONT_START = ( 1 < < 3 ) ,
HLS_OMIT_ENDLIST = ( 1 < < 4 ) ,
HLS_SPLIT_BY_TIME = ( 1 < < 5 ) ,
HLS_APPEND_LIST = ( 1 < < 6 ) ,
} HLSFlags ;
typedef enum {
@ -265,6 +266,14 @@ static int hls_encryption_start(AVFormatContext *s)
return 0 ;
}
static int read_chomp_line ( AVIOContext * s , char * buf , int maxlen )
{
int len = ff_get_line ( s , buf , maxlen ) ;
while ( len > 0 & & av_isspace ( buf [ len - 1 ] ) )
buf [ - - len ] = ' \0 ' ;
return len ;
}
static int hls_mux_init ( AVFormatContext * s )
{
HLSContext * hls = s - > priv_data ;
@ -389,6 +398,54 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double
return 0 ;
}
static int parse_playlist ( AVFormatContext * s , const char * url )
{
HLSContext * hls = s - > priv_data ;
AVIOContext * in ;
int ret = 0 , is_segment = 0 ;
int64_t new_start_pos ;
char line [ 1024 ] ;
const char * ptr ;
if ( ( ret = ffio_open_whitelist ( & in , url , AVIO_FLAG_READ ,
& s - > interrupt_callback , NULL ,
s - > protocol_whitelist , s - > protocol_blacklist ) ) < 0 )
return ret ;
read_chomp_line ( in , line , sizeof ( line ) ) ;
if ( strcmp ( line , " #EXTM3U " ) ) {
ret = AVERROR_INVALIDDATA ;
goto fail ;
}
while ( ! avio_feof ( in ) ) {
read_chomp_line ( in , line , sizeof ( line ) ) ;
if ( av_strstart ( line , " #EXT-X-MEDIA-SEQUENCE: " , & ptr ) ) {
hls - > sequence = atoi ( ptr ) ;
} else if ( av_strstart ( line , " #EXTINF: " , & ptr ) ) {
is_segment = 1 ;
hls - > duration = atof ( ptr ) ;
} else if ( av_strstart ( line , " # " , NULL ) ) {
continue ;
} else if ( line [ 0 ] ) {
if ( is_segment ) {
is_segment = 0 ;
new_start_pos = avio_tell ( hls - > avf - > pb ) ;
hls - > size = new_start_pos - hls - > start_pos ;
av_strlcpy ( hls - > avf - > filename , line , sizeof ( line ) ) ;
ret = hls_append_segment ( s , hls , hls - > duration , hls - > start_pos , hls - > size ) ;
if ( ret < 0 )
goto fail ;
hls - > start_pos = new_start_pos ;
}
}
}
fail :
avio_close ( in ) ;
return ret ;
}
static void hls_free_segments ( HLSSegment * p )
{
HLSSegment * en ;
@ -752,6 +809,10 @@ static int hls_write_header(AVFormatContext *s)
if ( ( ret = hls_mux_init ( s ) ) < 0 )
goto fail ;
if ( hls - > flags & HLS_APPEND_LIST ) {
parse_playlist ( s , s - > filename ) ;
}
if ( ( ret = hls_start ( s ) ) < 0 )
goto fail ;
@ -927,6 +988,7 @@ static const AVOption options[] = {
{ " discont_start " , " start the playlist with a discontinuity tag " , 0 , AV_OPT_TYPE_CONST , { . i64 = HLS_DISCONT_START } , 0 , UINT_MAX , E , " flags " } ,
{ " omit_endlist " , " Do not append an endlist when ending stream " , 0 , AV_OPT_TYPE_CONST , { . i64 = HLS_OMIT_ENDLIST } , 0 , UINT_MAX , E , " flags " } ,
{ " split_by_time " , " split the hls segment by time which user set by hls_time " , 0 , AV_OPT_TYPE_CONST , { . i64 = HLS_SPLIT_BY_TIME } , 0 , UINT_MAX , E , " flags " } ,
{ " append_list " , " append the new segments into old hls segment list " , 0 , AV_OPT_TYPE_CONST , { . i64 = HLS_APPEND_LIST } , 0 , UINT_MAX , E , " flags " } ,
{ " use_localtime " , " set filename expansion with strftime at segment creation " , OFFSET ( use_localtime ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , E } ,
{ " use_localtime_mkdir " , " create last directory component in strftime-generated filename " , OFFSET ( use_localtime_mkdir ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , E } ,
{ " hls_playlist_type " , " set the HLS playlist type " , OFFSET ( pl_type ) , AV_OPT_TYPE_INT , { . i64 = PLAYLIST_TYPE_NONE } , 0 , PLAYLIST_TYPE_NB - 1 , E , " pl_type " } ,