@ -37,6 +37,9 @@
# include "avformat.h"
# include "avio_internal.h"
# include "hlsplaylist.h"
# if CONFIG_HTTP_PROTOCOL
# include "http.h"
# endif
# include "internal.h"
# include "isom.h"
# include "os_support.h"
@ -103,7 +106,10 @@ typedef struct DASHContext {
const char * utc_timing_url ;
const char * user_agent ;
int hls_playlist ;
int http_persistent ;
int master_playlist_created ;
AVIOContext * mpd_out ;
AVIOContext * m3u8_out ;
} DASHContext ;
static struct codec_string {
@ -117,6 +123,36 @@ static struct codec_string {
{ 0 , NULL }
} ;
static int dashenc_io_open ( AVFormatContext * s , AVIOContext * * pb , char * filename ,
AVDictionary * * options ) {
DASHContext * c = s - > priv_data ;
int http_base_proto = filename ? ff_is_http_proto ( filename ) : 0 ;
int err = AVERROR_MUXER_NOT_FOUND ;
if ( ! * pb | | ! http_base_proto | | ! c - > http_persistent ) {
err = s - > io_open ( s , pb , filename , AVIO_FLAG_WRITE , options ) ;
# if CONFIG_HTTP_PROTOCOL
} else {
URLContext * http_url_context = ffio_geturlcontext ( * pb ) ;
av_assert0 ( http_url_context ) ;
err = ff_http_do_new_request ( http_url_context , filename ) ;
# endif
}
return err ;
}
static void dashenc_io_close ( AVFormatContext * s , AVIOContext * * pb , char * filename ) {
DASHContext * c = s - > priv_data ;
int http_base_proto = filename ? ff_is_http_proto ( filename ) : 0 ;
if ( ! http_base_proto | | ! c - > http_persistent ) {
ff_format_io_close ( s , pb ) ;
# if CONFIG_HTTP_PROTOCOL
} else {
avio_flush ( * pb ) ;
# endif
}
}
static void set_codec_str ( AVFormatContext * s , AVCodecParameters * par ,
char * str , int size )
{
@ -218,6 +254,8 @@ static void set_http_options(AVDictionary **options, DASHContext *c)
{
if ( c - > user_agent )
av_dict_set ( options , " user_agent " , c - > user_agent , 0 ) ;
if ( c - > http_persistent )
av_dict_set_int ( options , " multiple_requests " , 1 , 0 ) ;
}
static void get_hls_playlist_name ( char * playlist_name , int string_size ,
@ -273,9 +311,10 @@ static void dash_free(AVFormatContext *s)
av_freep ( & c - > streams ) ;
}
static void output_segment_list ( OutputStream * os , AVIOContext * out , DASHContext * c ,
static void output_segment_list ( OutputStream * os , AVIOContext * out , AVFormatContext * s ,
int representation_id , int final )
{
DASHContext * c = s - > priv_data ;
int i , start_index = 0 , start_number = 1 ;
if ( c - > window_size ) {
start_index = FFMAX ( os - > nb_segments - c - > window_size , 0 ) ;
@ -339,7 +378,6 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
int timescale = os - > ctx - > streams [ 0 ] - > time_base . den ;
char temp_filename_hls [ 1024 ] ;
char filename_hls [ 1024 ] ;
AVIOContext * out_hls = NULL ;
AVDictionary * http_opts = NULL ;
int target_duration = 0 ;
int ret = 0 ;
@ -352,7 +390,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
snprintf ( temp_filename_hls , sizeof ( temp_filename_hls ) , use_rename ? " %s.tmp " : " %s " , filename_hls ) ;
set_http_options ( & http_opts , c ) ;
avio_open2 ( & out_hls , temp_filename_hls , AVIO_FLAG_WRITE , NULL , & http_opts ) ;
dashenc_io_open ( s , & c - > m3u8_out , temp_filename_hls , & http_opts ) ;
av_dict_free ( & http_opts ) ;
for ( i = start_index ; i < os - > nb_segments ; i + + ) {
Segment * seg = os - > segments [ i ] ;
@ -361,15 +399,15 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
target_duration = lrint ( duration ) ;
}
ff_hls_write_playlist_header ( out_hls , 6 , - 1 , target_duration ,
ff_hls_write_playlist_header ( c - > m3u8_out , 6 , - 1 , target_duration ,
start_number , PLAYLIST_TYPE_NONE ) ;
ff_hls_write_init_file ( out_hls , os - > initfile , c - > single_file ,
ff_hls_write_init_file ( c - > m3u8_out , os - > initfile , c - > single_file ,
os - > init_range_length , os - > init_start_pos ) ;
for ( i = start_index ; i < os - > nb_segments ; i + + ) {
Segment * seg = os - > segments [ i ] ;
ret = ff_hls_write_file_entry ( out_hls , 0 , c - > single_file ,
ret = ff_hls_write_file_entry ( c - > m3u8_out , 0 , c - > single_file ,
( double ) seg - > duration / timescale , 0 ,
seg - > range_length , seg - > start_pos , NULL ,
c - > single_file ? os - > initfile : seg - > file ,
@ -380,9 +418,10 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
}
if ( final )
ff_hls_write_end_list ( out_hls ) ;
ff_hls_write_end_list ( c - > m3u8_out ) ;
dashenc_io_close ( s , & c - > m3u8_out , temp_filename_hls ) ;
avio_close ( out_hls ) ;
if ( use_rename )
if ( avpriv_io_move ( temp_filename_hls , filename_hls ) < 0 ) {
av_log ( os - > ctx , AV_LOG_WARNING , " renaming file %s to %s failed \n \n " , temp_filename_hls , filename_hls ) ;
@ -498,7 +537,7 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
avio_printf ( out , " \t \t \t \t <AudioChannelConfiguration schemeIdUri= \" urn:mpeg:dash:23003:3:audio_channel_configuration:2011 \" value= \" %d \" /> \n " ,
s - > streams [ i ] - > codecpar - > channels ) ;
}
output_segment_list ( os , out , c , i , final ) ;
output_segment_list ( os , out , s , i , final ) ;
avio_printf ( out , " \t \t \t </Representation> \n " ) ;
}
avio_printf ( out , " \t \t </AdaptationSet> \n " ) ;
@ -657,11 +696,12 @@ static int write_manifest(AVFormatContext *s, int final)
snprintf ( temp_filename , sizeof ( temp_filename ) , use_rename ? " %s.tmp " : " %s " , s - > filename ) ;
set_http_options ( & opts , c ) ;
ret = s - > io_open ( s , & out , temp_filename , AVIO_FLAG_WRITE , & opts ) ;
ret = dashenc_io_open ( s , & c - > mpd_ out, temp_filename , & opts ) ;
if ( ret < 0 ) {
av_log ( s , AV_LOG_ERROR , " Unable to open %s for writing \n " , temp_filename ) ;
return ret ;
}
out = c - > mpd_out ;
av_dict_free ( & opts ) ;
avio_printf ( out , " <?xml version= \" 1.0 \" encoding= \" utf-8 \" ?> \n " ) ;
avio_printf ( out , " <MPD xmlns:xsi= \" http://www.w3.org/2001/XMLSchema-instance \" \n "
@ -728,7 +768,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf ( out , " </MPD> \n " ) ;
avio_flush ( out ) ;
ff_format _io_close( s , & out ) ;
dashenc _io_close( s , & c - > mpd_ out, temp_filename ) ;
if ( use_rename ) {
if ( ( ret = avpriv_io_move ( temp_filename , s - > filename ) ) < 0 )
@ -1115,7 +1155,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
snprintf ( full_path , sizeof ( full_path ) , " %s%s " , c - > dirname , filename ) ;
snprintf ( temp_path , sizeof ( temp_path ) , use_rename ? " %s.tmp " : " %s " , full_path ) ;
set_http_options ( & opts , c ) ;
ret = s - > io_open ( s , & os - > out , temp_path , AVIO_FLAG_WRITE , & opts ) ;
ret = dashenc_ io_open( s , & os - > out , temp_path , & opts ) ;
if ( ret < 0 )
break ;
av_dict_free ( & opts ) ;
@ -1133,7 +1173,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
if ( c - > single_file ) {
find_index_range ( s , full_path , os - > pos , & index_length ) ;
} else {
ff_format _io_close( s , & os - > out ) ;
dashenc _io_close( s , & os - > out , temp_path ) ;
if ( use_rename ) {
ret = avpriv_io_move ( temp_path , full_path ) ;
@ -1330,6 +1370,7 @@ static const AVOption options[] = {
{ " media_seg_name " , " DASH-templated name to used for the media segments " , OFFSET ( media_seg_name ) , AV_OPT_TYPE_STRING , { . str = " chunk-stream$RepresentationID$-$Number%05d$.m4s " } , 0 , 0 , E } ,
{ " utc_timing_url " , " URL of the page that will return the UTC timestamp in ISO format " , OFFSET ( utc_timing_url ) , AV_OPT_TYPE_STRING , { 0 } , 0 , 0 , E } ,
{ " http_user_agent " , " override User-Agent field in HTTP header " , OFFSET ( user_agent ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 0 , E } ,
{ " http_persistent " , " Use persistent HTTP connections " , OFFSET ( http_persistent ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , E } ,
{ " hls_playlist " , " Generate HLS playlist files(master.m3u8, media_%d.m3u8) " , OFFSET ( hls_playlist ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , E } ,
{ NULL } ,
} ;