@ -19,8 +19,22 @@
# include <stdlib.h>
# include <sstream>
# include <unistd.h>
using namespace std ;
string srs_time_to_utc_format_str ( srs_utime_t u )
{
time_t s = srsu2s ( u ) ;
struct tm t ;
srs_assert ( gmtime_r ( & s , & t ) ! = NULL ) ;
char print_buf [ 256 ] ;
size_t ret = strftime ( print_buf , sizeof ( print_buf ) , " %Y-%m-%dT%H:%M:%SZ " , & t ) ;
return std : : string ( print_buf , ret ) ;
}
SrsInitMp4 : : SrsInitMp4 ( )
{
fw = new SrsFileWriter ( ) ;
@ -65,20 +79,22 @@ SrsFragmentedMp4::~SrsFragmentedMp4()
srs_freep ( fw ) ;
}
srs_error_t SrsFragmentedMp4 : : initialize ( SrsRequest * r , bool video , SrsMpdWriter * mpd , uint32_t tid )
srs_error_t SrsFragmentedMp4 : : initialize ( SrsRequest * r , bool video , int64_t time , SrsMpdWriter * mpd , uint32_t tid )
{
srs_error_t err = srs_success ;
string file_home ;
string file_name ;
int64_t sequence_number ;
srs_utime_t basetime ;
if ( ( err = mpd - > get_fragment ( video , file_home , file_name , sequence_number , basetime ) ) ! = srs_success ) {
return srs_error_wrap ( err , " get fragment " ) ;
if ( ( err = mpd - > get_fragment ( video , file_home , file_name , time , sequence_number ) ) ! = srs_success ) {
return srs_error_wrap ( err , " get fragment, seq=%u, home=%s, file=%s " ,
( uint32_t ) sequence_number , file_home . c_str ( ) , file_name . c_str ( ) ) ;
}
string home = _srs_config - > get_dash_path ( r - > vhost ) ;
set_path ( home + " / " + file_home + " / " + file_name ) ;
// Set number of the fragment, use in mpd SegmentTemplate@startNumber later.
set_number ( sequence_number ) ;
if ( ( err = create_dir ( ) ) ! = srs_success ) {
return srs_error_wrap ( err , " create dir " ) ;
@ -89,8 +105,8 @@ srs_error_t SrsFragmentedMp4::initialize(SrsRequest* r, bool video, SrsMpdWriter
return srs_error_wrap ( err , " Open fmp4 failed, path=%s " , path_tmp . c_str ( ) ) ;
}
if ( ( err = enc - > initialize ( fw , ( uint32_t ) sequence_number , base time, tid ) ) ! = srs_success ) {
return srs_error_wrap ( err , " init encoder " ) ;
if ( ( err = enc - > initialize ( fw , ( uint32_t ) sequence_number , time, tid ) ) ! = srs_success ) {
return srs_error_wrap ( err , " init encoder , seq=%u, time=% " PRId64 " , tid=%u " , ( uint32_t ) sequence_number , time , tid ) ;
}
return err ;
@ -146,13 +162,29 @@ SrsMpdWriter::SrsMpdWriter()
{
req = NULL ;
timeshit = update_period = fragment = 0 ;
last_update_mpd = 0 ;
window_size_ = 0 ;
availability_start_time_ = 0 ;
video_number_ = 0 ;
audio_number_ = 0 ;
}
SrsMpdWriter : : ~ SrsMpdWriter ( )
{
}
void SrsMpdWriter : : dispose ( )
{
if ( req ) {
string mpd_path = srs_path_build_stream ( mpd_file , req - > vhost , req - > app , req - > stream ) ;
string full_path = home + " / " + mpd_path ;
if ( unlink ( full_path . c_str ( ) ) < 0 ) {
srs_warn ( " ignore remove mpd failed, %s " , full_path . c_str ( ) ) ;
}
}
}
srs_error_t SrsMpdWriter : : initialize ( SrsRequest * r )
{
req = r ;
@ -171,8 +203,10 @@ srs_error_t SrsMpdWriter::on_publish()
string mpd_path = srs_path_build_stream ( mpd_file , req - > vhost , req - > app , req - > stream ) ;
fragment_home = srs_path_dirname ( mpd_path ) + " / " + req - > stream ;
window_size_ = _srs_config - > get_dash_window_size ( r - > vhost ) ;
srs_trace ( " DASH: Config fragment=% " PRId64 " , period=% " PRId64 , fragment , update_period ) ;
srs_trace ( " DASH: Config fragment=%dms, period=%dms, window=%d, timeshit=%dms, home=%s, mpd=%s " ,
srsu2msi ( fragment ) , srsu2msi ( update_period ) , window_size_ , srsu2msi ( timeshit ) , home . c_str ( ) , mpd_file . c_str ( ) ) ;
return srs_success ;
}
@ -181,15 +215,14 @@ void SrsMpdWriter::on_unpublish()
{
}
srs_error_t SrsMpdWriter : : write ( SrsFormat * format )
srs_error_t SrsMpdWriter : : write ( SrsFormat * format , SrsFragmentWindow * afragments , SrsFragmentWindow * vfragments )
{
srs_error_t err = srs_success ;
// MPD is not expired?
if ( last_update_mpd ! = 0 & & srs_get_system_time ( ) - last_update_mpd < update_period ) {
// TODO: FIXME: pure audio/video support.
if ( afragments- > empty ( ) | | vfragments - > empty ( ) ) {
return err ;
}
last_update_mpd = srs_get_system_time ( ) ;
string mpd_path = srs_path_build_stream ( mpd_file , req - > vhost , req - > app , req - > stream ) ;
string full_path = home + " / " + mpd_path ;
@ -201,37 +234,65 @@ srs_error_t SrsMpdWriter::write(SrsFormat* format)
return srs_error_wrap ( err , " Create MPD home failed, home=%s " , full_home . c_str ( ) ) ;
}
double last_duration = srsu2s ( srs_max ( vfragments - > at ( vfragments - > size ( ) - 1 ) - > duration ( ) , afragments - > at ( afragments - > size ( ) - 1 ) - > duration ( ) ) ) ;
stringstream ss ;
ss < < " <?xml version= \" 1.0 \" encoding= \" utf-8 \" ?> " < < endl
< < " <MPD profiles= \" urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash-if-simple \" " < < endl
< < " ns1:schemaLocation= \" urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd \" " < < endl
< < " xmlns= \" urn:mpeg:dash:schema:mpd:2011 \" xmlns:ns1= \" http://www.w3.org/2001/XMLSchema-instance \" " < < endl
< < " type= \" dynamic \" minimumUpdatePeriod= \" PT " < < update_period / SRS_UTIME_SECONDS < < " S \" " < < endl
< < " timeShiftBufferDepth= \" PT " < < timeshit / SRS_UTIME_SECONDS < < " S \" availabilityStartTime= \" 1970-01-01T00:00:00Z \" " < < endl
< < " maxSegmentDuration= \" PT " < < fragment / SRS_UTIME_SECONDS < < " S \" minBufferTime= \" PT " < < fragment / SRS_UTIME_SECONDS < < " S \" > " < < endl
< < " <BaseURL> " < < req - > stream < < " / " < < " </BaseURL> " < < endl
< < " <Period start= \" PT0S \" > " < < endl ;
if ( format - > acodec ) {
ss < < " <AdaptationSet mimeType= \" audio/mp4 \" segmentAlignment= \" true \" startWithSAP= \" 1 \" > " < < endl ;
ss < < " <SegmentTemplate duration= \" " < < fragment / SRS_UTIME_SECONDS < < " \" "
< < " initialization= \" $RepresentationID$-init.mp4 \" "
< < " media= \" $RepresentationID$-$Number$.m4s \" /> " < < endl ;
ss < < " <Representation id= \" audio \" bandwidth= \" 48000 \" codecs= \" mp4a.40.2 \" /> " < < endl ;
ss < < " </AdaptationSet> " < < endl ;
}
if ( format - > vcodec ) {
< < " <MPD profiles= \" urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash-if-simple \" " < < endl
< < " ns1:schemaLocation= \" urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd \" " < < endl
< < " xmlns= \" urn:mpeg:dash:schema:mpd:2011 \" xmlns:ns1= \" http://www.w3.org/2001/XMLSchema-instance \" " < < endl
< < " type= \" dynamic \" " < < endl
< < " minimumUpdatePeriod= \" PT " < < srs_fmt ( " %.3f " , srsu2s ( update_period ) ) < < " S \" " < < endl
< < " timeShiftBufferDepth= \" PT " < < srs_fmt ( " %.3f " , last_duration * window_size_ ) < < " S \" " < < endl
< < " availabilityStartTime= \" " < < srs_time_to_utc_format_str ( availability_start_time_ ) < < " \" " < < endl
< < " publishTime= \" " < < srs_time_to_utc_format_str ( srs_get_system_time ( ) ) < < " \" " < < endl
< < " minBufferTime= \" PT " < < srs_fmt ( " %.3f " , 2 * last_duration ) < < " S \" > " < < endl ;
ss < < " <BaseURL> " < < req - > stream < < " / " < < " </BaseURL> " < < endl ;
ss < < " <Period start= \" PT0S \" > " < < endl ;
if ( format - > acodec & & ! afragments - > empty ( ) ) {
int start_index = srs_max ( 0 , afragments - > size ( ) - window_size_ ) ;
ss < < " <AdaptationSet mimeType= \" audio/mp4 \" segmentAlignment= \" true \" startWithSAP= \" 1 \" > " < < endl ;
ss < < " <Representation id= \" audio \" bandwidth= \" 48000 \" codecs= \" mp4a.40.2 \" > " < < endl ;
ss < < " <SegmentTemplate initialization= \" $RepresentationID$-init.mp4 \" "
< < " media= \" $RepresentationID$-$Number$.m4s \" "
< < " startNumber= \" " < < afragments - > at ( start_index ) - > number ( ) < < " \" "
< < " timescale= \" 1000 \" > " < < endl ;
ss < < " <SegmentTimeline> " < < endl ;
for ( int i = start_index ; i < afragments - > size ( ) ; + + i ) {
ss < < " <S t= \" " < < srsu2ms ( afragments - > at ( i ) - > get_start_dts ( ) ) < < " \" "
< < " d= \" " < < srsu2ms ( afragments - > at ( i ) - > duration ( ) ) < < " \" /> " < < endl ;
}
ss < < " </SegmentTimeline> " < < endl ;
ss < < " </SegmentTemplate> " < < endl ;
ss < < " </Representation> " < < endl ;
ss < < " </AdaptationSet> " < < endl ;
}
if ( format - > vcodec & & ! vfragments - > empty ( ) ) {
int start_index = srs_max ( 0 , vfragments - > size ( ) - window_size_ ) ;
int w = format - > vcodec - > width ;
int h = format - > vcodec - > height ;
ss < < " <AdaptationSet mimeType= \" video/mp4 \" segmentAlignment= \" true \" startWithSAP= \" 1 \" > " < < endl ;
ss < < " <SegmentTemplate duration= \" " < < fragment / SRS_UTIME_SECONDS < < " \" "
< < " initialization= \" $RepresentationID$-init.mp4 \" "
< < " media= \" $RepresentationID$-$Number$.m4s \" /> " < < endl ;
ss < < " <Representation id= \" video \" bandwidth= \" 800000 \" codecs= \" avc1.64001e \" "
< < " width= \" " < < w < < " \" height= \" " < < h < < " \" /> " < < endl ;
ss < < " </AdaptationSet> " < < endl ;
ss < < " <AdaptationSet mimeType= \" video/mp4 \" segmentAlignment= \" true \" startWithSAP= \" 1 \" > " < < endl ;
ss < < " <Representation id= \" video \" bandwidth= \" 800000 \" codecs= \" avc1.64001e \" " < < " width= \" " < < w < < " \" height= \" " < < h < < " \" > " < < endl ;
ss < < " <SegmentTemplate initialization= \" $RepresentationID$-init.mp4 \" "
< < " media= \" $RepresentationID$-$Number$.m4s \" "
< < " startNumber= \" " < < vfragments - > at ( start_index ) - > number ( ) < < " \" "
< < " timescale= \" 1000 \" > " < < endl ;
ss < < " <SegmentTimeline> " < < endl ;
for ( int i = start_index ; i < vfragments - > size ( ) ; + + i ) {
ss < < " <S t= \" " < < srsu2ms ( vfragments - > at ( i ) - > get_start_dts ( ) ) < < " \" "
< < " d= \" " < < srsu2ms ( vfragments - > at ( i ) - > duration ( ) ) < < " \" /> " < < endl ;
}
ss < < " </SegmentTimeline> " < < endl ;
ss < < " </SegmentTemplate> " < < endl ;
ss < < " </Representation> " < < endl ;
ss < < " </AdaptationSet> " < < endl ;
}
ss < < " </Period> " < < endl
< < " </MPD> " < < endl ;
ss < < " </Period> " < < endl ;
ss < < " </MPD> " < < endl ;
SrsFileWriter * fw = new SrsFileWriter ( ) ;
SrsAutoFree ( SrsFileWriter , fw ) ;
@ -255,7 +316,7 @@ srs_error_t SrsMpdWriter::write(SrsFormat* format)
return err ;
}
srs_error_t SrsMpdWriter : : get_fragment ( bool video , std : : string & home , std : : string & file_name , int64_t & sn , srs_utime_t & basetime )
srs_error_t SrsMpdWriter : : get_fragment ( bool video , std : : string & home , std : : string & file_name , int64_t time , int64_t & sn )
{
srs_error_t err = srs_success ;
@ -264,32 +325,42 @@ srs_error_t SrsMpdWriter::get_fragment(bool video, std::string& home, std::strin
// We name the segment as advanced N segments, because when we are generating segment at the current time,
// the player may also request the current segment.
srs_assert ( fragment ) ;
int64_t number = ( srs_update_system_time ( ) / fragment + 1 ) ;
// TOOD: FIXME: Should keep the segments continuous, or player may fail.
sn = number ;
// The base time aligned with sn.
basetime = sn * fragment ;
if ( video ) {
sn = video_number_ + + ;
file_name = " video- " + srs_int2str ( sn ) + " .m4s " ;
} else {
sn = audio_number_ + + ;
file_name = " audio- " + srs_int2str ( sn ) + " .m4s " ;
}
return err ;
}
void SrsMpdWriter : : set_availability_start_time ( srs_utime_t t )
{
availability_start_time_ = t ;
}
srs_utime_t SrsMpdWriter : : get_availability_start_time ( )
{
return availability_start_time_ ;
}
SrsDashController : : SrsDashController ( )
{
req = NULL ;
video_tack_id = 0 ;
audio_track_id = 1 ;
format_ = NULL ;
// trackid start from 1, because some player will check if track id is greater than 0
video_track_id = 1 ;
audio_track_id = 2 ;
mpd = new SrsMpdWriter ( ) ;
vcurrent = acurrent = NULL ;
vfragments = new SrsFragmentWindow ( ) ;
afragments = new SrsFragmentWindow ( ) ;
audio_dts = video_dts = 0 ;
first_dts_ = - 1 ;
video_reaped_ = false ;
fragment = 0 ;
}
@ -302,6 +373,28 @@ SrsDashController::~SrsDashController()
srs_freep ( afragments ) ;
}
void SrsDashController : : dispose ( )
{
srs_error_t err = srs_success ;
vfragments - > dispose ( ) ;
afragments - > dispose ( ) ;
if ( vcurrent & & ( err = vcurrent - > unlink_tmpfile ( ) ) ! = srs_success ) {
srs_warn ( " Unlink tmp video m4s failed %s " , srs_error_desc ( err ) . c_str ( ) ) ;
srs_freep ( err ) ;
}
if ( acurrent & & ( err = acurrent - > unlink_tmpfile ( ) ) ! = srs_success ) {
srs_warn ( " Unlink tmp audio m4s failed %s " , srs_error_desc ( err ) . c_str ( ) ) ;
srs_freep ( err ) ;
}
mpd - > dispose ( ) ;
srs_trace ( " gracefully dispose dash %s " , req ? req - > get_stream_url ( ) . c_str ( ) : " " ) ;
}
srs_error_t SrsDashController : : initialize ( SrsRequest * r )
{
srs_error_t err = srs_success ;
@ -329,16 +422,17 @@ srs_error_t SrsDashController::on_publish()
}
srs_freep ( vcurrent ) ;
vcurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = vcurrent - > initialize ( req , true , mpd , video_tack_id ) ) ! = srs_success ) {
return srs_error_wrap ( err , " video fragment " ) ;
}
srs_freep ( vfragments ) ;
vfragments = new SrsFragmentWindow ( ) ;
srs_freep ( acurrent ) ;
acurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = acurrent - > initialize ( req , false , mpd , audio_track_id ) ) ! = srs_success ) {
return srs_error_wrap ( err , " audio fragment " ) ;
}
srs_freep ( afragments ) ;
afragments = new SrsFragmentWindow ( ) ;
audio_dts = 0 ;
video_dts = 0 ;
first_dts_ = - 1 ;
video_reaped_ = false ;
return err ;
}
@ -349,28 +443,63 @@ void SrsDashController::on_unpublish()
srs_error_t err = srs_success ;
if ( ( err = vcurrent - > reap ( video_dts ) ) ! = srs_success ) {
srs_warn ( " reap video err %s" , srs_error_desc ( err ) . c_str ( ) ) ;
if ( vcurrent & & ( err = vcurrent - > reap ( video_dts ) ) ! = srs_success ) {
srs_warn ( " reap video dts=%" PRId64 " err %s" , video_dts , srs_error_desc ( err ) . c_str ( ) ) ;
srs_freep ( err ) ;
}
srs_freep ( vcurrent ) ;
if ( ( err = acurrent - > reap ( audio_dts ) ) ! = srs_success ) {
srs_warn ( " reap audio err %s " , srs_error_desc ( err ) . c_str ( ) ) ;
if ( vcurrent & & vcurrent - > duration ( ) ) {
vfragments - > append ( vcurrent ) ;
vcurrent = NULL ;
}
if ( acurrent & & ( err = acurrent - > reap ( audio_dts ) ) ! = srs_success ) {
srs_warn ( " reap audio dts=% " PRId64 " err %s " , audio_dts , srs_error_desc ( err ) . c_str ( ) ) ;
srs_freep ( err ) ;
}
srs_freep ( acurrent ) ;
if ( acurrent - > duration ( ) > 0 ) {
afragments - > append ( acurrent ) ;
acurrent = NULL ;
}
if ( ( err = refresh_mpd ( format_ ) ) ! = srs_success ) {
srs_warn ( " Refresh the MPD failed, err=%s " , srs_error_desc ( err ) . c_str ( ) ) ;
srs_error_reset ( err ) ;
}
}
srs_error_t SrsDashController : : on_audio ( SrsSharedPtrMessage * shared_audio , SrsFormat * format )
{
srs_error_t err = srs_success ;
format_ = format ;
if ( format - > is_aac_sequence_header ( ) ) {
return refresh_init_mp4 ( shared_audio , format ) ;
}
if ( acurrent - > duration ( ) > = fragment ) {
audio_dts = shared_audio - > timestamp ;
if ( ! acurrent ) {
acurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = acurrent - > initialize ( req , false , audio_dts * SRS_UTIME_MILLISECONDS , mpd , audio_track_id ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Initialize the audio fragment failed " ) ;
}
}
if ( first_dts_ = = - 1 ) {
first_dts_ = audio_dts ;
mpd - > set_availability_start_time ( srs_get_system_time ( ) - first_dts_ * SRS_UTIME_MILLISECONDS ) ;
}
// TODO: FIXME: Support pure audio streaming.
if ( video_reaped_ ) {
// The video is reaped, audio must be reaped right now to align the timestamp of video.
video_reaped_ = false ;
// Append current timestamp to calculate right duration.
acurrent - > append ( shared_audio - > timestamp ) ;
if ( ( err = acurrent - > reap ( audio_dts ) ) ! = srs_success ) {
return srs_error_wrap ( err , " reap current " ) ;
}
@ -378,19 +507,37 @@ srs_error_t SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFo
afragments - > append ( acurrent ) ;
acurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = acurrent - > initialize ( req , false , mpd, audio_track_id ) ) ! = srs_success ) {
if ( ( err = acurrent - > initialize ( req , false , audio_dts * SRS_UTIME_MILLISECONDS , mpd, audio_track_id ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Initialize the audio fragment failed " ) ;
}
if ( ( err = refresh_mpd ( format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Refresh the MPD failed " ) ;
}
}
if ( ( err = acurrent - > write ( shared_audio , format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Write audio to fragment failed " ) ;
}
if ( ( err = refresh_mpd ( format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Refresh the MPD failed " ) ;
srs_utime_t fragment = _srs_config - > get_dash_fragment ( req - > vhost ) ;
int window_size = _srs_config - > get_dash_window_size ( req - > vhost ) ;
int dash_window = 2 * window_size * fragment ;
if ( afragments - > size ( ) > window_size ) {
int w = 0 ;
for ( int i = afragments - > size ( ) - window_size ; i < afragments - > size ( ) ; + + i ) {
w + = afragments - > at ( i ) - > duration ( ) ;
}
dash_window = srs_max ( dash_window , w ) ;
// shrink the segments.
afragments - > shrink ( dash_window ) ;
}
bool dash_cleanup = _srs_config - > get_dash_cleanup ( req - > vhost ) ;
// remove the m4s file.
afragments - > clear_expired ( dash_cleanup ) ;
return err ;
}
@ -398,32 +545,72 @@ srs_error_t SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFo
{
srs_error_t err = srs_success ;
format_ = format ;
if ( format - > is_avc_sequence_header ( ) ) {
return refresh_init_mp4 ( shared_video , format ) ;
}
video_dts = shared_video - > timestamp ;
if ( ! vcurrent ) {
vcurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = vcurrent - > initialize ( req , true , video_dts * SRS_UTIME_MILLISECONDS , mpd , video_track_id ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Initialize the video fragment failed " ) ;
}
}
if ( first_dts_ = = - 1 ) {
first_dts_ = video_dts ;
mpd - > set_availability_start_time ( srs_get_system_time ( ) - first_dts_ * SRS_UTIME_MILLISECONDS ) ;
}
bool reopen = format - > video - > frame_type = = SrsVideoAvcFrameTypeKeyFrame & & vcurrent - > duration ( ) > = fragment ;
if ( reopen ) {
// Append current timestamp to calculate right duration.
vcurrent - > append ( shared_video - > timestamp ) ;
if ( ( err = vcurrent - > reap ( video_dts ) ) ! = srs_success ) {
return srs_error_wrap ( err , " reap current " ) ;
}
// Mark the video has reaped, audio will reaped when recv next frame.
video_reaped_ = true ;
vfragments - > append ( vcurrent ) ;
vcurrent = new SrsFragmentedMp4 ( ) ;
if ( ( err = vcurrent - > initialize ( req , true , mpd , video_tack_id ) ) ! = srs_success ) {
if ( ( err = vcurrent - > initialize ( req , true , video_dts * SRS_UTIME_MILLISECONDS , mpd, video_t r ack_id) ) ! = srs_success ) {
return srs_error_wrap ( err , " Initialize the video fragment failed " ) ;
}
if ( ( err = refresh_mpd ( format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Refresh the MPD failed " ) ;
}
}
if ( ( err = vcurrent - > write ( shared_video , format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Write video to fragment failed " ) ;
}
if ( ( err = refresh_mpd ( format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Refresh the MPD failed " ) ;
srs_utime_t fragment = _srs_config - > get_dash_fragment ( req - > vhost ) ;
int window_size = _srs_config - > get_dash_window_size ( req - > vhost ) ;
int dash_window = 2 * window_size * fragment ;
if ( vfragments - > size ( ) > window_size ) {
int w = 0 ;
for ( int i = vfragments - > size ( ) - window_size ; i < vfragments - > size ( ) ; + + i ) {
w + = vfragments - > at ( i ) - > duration ( ) ;
}
dash_window = srs_max ( dash_window , w ) ;
// shrink the segments.
vfragments - > shrink ( dash_window ) ;
}
bool dash_cleanup = _srs_config - > get_dash_cleanup ( req - > vhost ) ;
// remove the m4s file.
vfragments - > clear_expired ( dash_cleanup ) ;
return err ;
}
@ -436,7 +623,7 @@ srs_error_t SrsDashController::refresh_mpd(SrsFormat* format)
return err ;
}
if ( ( err = mpd - > write ( format )) ! = srs_success ) {
if ( ( err = mpd - > write ( format , afragments , vfragments )) ! = srs_success ) {
return srs_error_wrap ( err , " write mpd " ) ;
}
@ -470,7 +657,7 @@ srs_error_t SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFor
init_mp4 - > set_path ( path ) ;
int tid = msg - > is_video ( ) ? video_t ack_id: audio_track_id ;
int tid = msg - > is_video ( ) ? video_t r ack_id : audio_track_id ;
if ( ( err = init_mp4 - > write ( format , msg - > is_video ( ) , tid ) ) ! = srs_success ) {
return srs_error_wrap ( err , " write init " ) ;
}
@ -479,7 +666,7 @@ srs_error_t SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFor
return srs_error_wrap ( err , " rename init " ) ;
}
srs_trace ( " DASH: Refresh media success, file=%s" , path . c_str ( ) ) ;
srs_trace ( " DASH: Refresh media type=%s, file=%s" , ( msg - > is_video ( ) ? " video " : " audio " ) , path . c_str ( ) ) ;
return err ;
}
@ -491,6 +678,8 @@ SrsDash::SrsDash()
controller = new SrsDashController ( ) ;
enabled = false ;
disposable_ = false ;
last_update_time_ = 0 ;
}
SrsDash : : ~ SrsDash ( )
@ -498,6 +687,53 @@ SrsDash::~SrsDash()
srs_freep ( controller ) ;
}
void SrsDash : : dispose ( )
{
if ( enabled ) {
on_unpublish ( ) ;
}
// Ignore when dash_dispose disabled.
srs_utime_t dash_dispose = _srs_config - > get_dash_dispose ( req - > vhost ) ;
if ( ! dash_dispose ) {
return ;
}
controller - > dispose ( ) ;
}
srs_error_t SrsDash : : cycle ( )
{
srs_error_t err = srs_success ;
if ( last_update_time_ < = 0 ) {
last_update_time_ = srs_get_system_time ( ) ;
}
if ( ! req ) {
return err ;
}
srs_utime_t dash_dispose = _srs_config - > get_dash_dispose ( req - > vhost ) ;
if ( dash_dispose < = 0 ) {
return err ;
}
if ( srs_get_system_time ( ) - last_update_time_ < = dash_dispose ) {
return err ;
}
last_update_time_ = srs_get_system_time ( ) ;
if ( ! disposable_ ) {
return err ;
}
disposable_ = false ;
srs_trace ( " dash cycle to dispose dash %s, timeout=%dms " , req - > get_stream_url ( ) . c_str ( ) , dash_dispose ) ;
dispose ( ) ;
return err ;
}
srs_error_t SrsDash : : initialize ( SrsOriginHub * h , SrsRequest * r )
{
srs_error_t err = srs_success ;
@ -526,10 +762,16 @@ srs_error_t SrsDash::on_publish()
}
enabled = true ;
// update the dash time, for dash_dispose.
last_update_time_ = srs_get_system_time ( ) ;
if ( ( err = controller - > on_publish ( ) ) ! = srs_success ) {
return srs_error_wrap ( err , " controller " ) ;
}
// ok, the dash can be dispose, or need to be dispose.
disposable_ = true ;
return err ;
}
@ -545,6 +787,9 @@ srs_error_t SrsDash::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* form
return err ;
}
// update the dash time, for dash_dispose.
last_update_time_ = srs_get_system_time ( ) ;
if ( ( err = controller - > on_audio ( shared_audio , format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Consume audio failed " ) ;
}
@ -564,6 +809,9 @@ srs_error_t SrsDash::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* form
return err ;
}
// update the dash time, for dash_dispose.
last_update_time_ = srs_get_system_time ( ) ;
if ( ( err = controller - > on_video ( shared_video , format ) ) ! = srs_success ) {
return srs_error_wrap ( err , " Consume video failed " ) ;
}