From dcac9c69d5d9d606f2128e552c0447dcefecca5a Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 13 Apr 2015 10:32:32 +0800 Subject: [PATCH] fix #381, support reap hls/ts by gop or not. 2.0.160. --- README.md | 1 + trunk/conf/full.conf | 5 ++++ trunk/src/app/srs_app_config.cpp | 19 ++++++++++++++- trunk/src/app/srs_app_config.hpp | 5 ++++ trunk/src/app/srs_app_hls.cpp | 42 ++++++++++++++++++++++---------- trunk/src/app/srs_app_hls.hpp | 7 +++++- trunk/src/core/srs_core.hpp | 2 +- 7 files changed, 65 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1b7c0a7c2..430bb4bc7 100755 --- a/README.md +++ b/README.md @@ -562,6 +562,7 @@ Supported operating systems and hardware: ### SRS 2.0 history +* v2.0, 2015-04-13, for [#381](https://github.com/winlinvip/simple-rtmp-server/issues/381), support reap hls/ts by gop or not. 2.0.160. * v2.0, 2015-04-10, enhanced on_hls_notify, support HTTP GET when reap ts. * v2.0, 2015-04-10, refine the hls deviation for floor algorithm. * v2.0, 2015-04-08, for [#375](https://github.com/winlinvip/simple-rtmp-server/issues/375), fix hls bug, keep cc continous between ts files. 2.0.159. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 7bcae649d..ed31509bd 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -618,6 +618,11 @@ vhost with-hls.srs.com { # @remark only used when on_hls_notify is config. # default: 64 hls_nb_notify 64; + # whether wait keyframe to reap segment, + # if off, reap segment when duration exceed the fragment, + # if on, reap segment when duration exceed and got keyframe. + # default: on + hls_wait_keyframe on; # on_hls, never config in here, should config in http_hooks. # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 527084b66..a58620299 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1487,7 +1487,7 @@ int SrsConfig::check_config() string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" - && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" + && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); @@ -3451,6 +3451,23 @@ bool SrsConfig::get_hls_cleanup(string vhost) return SRS_CONF_PERFER_TRUE(conf->arg0()); } +bool SrsConfig::get_hls_wait_keyframe(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME; + } + + SrsConfDirective* conf = hls->get("hls_wait_keyframe"); + + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + SrsConfDirective *SrsConfig::get_hds(const string &vhost) { SrsConfDirective* conf = get_vhost(vhost); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 3c829ab72..66f924854 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -63,6 +63,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_HLS_ACODEC "aac" #define SRS_CONF_DEFAULT_HLS_VCODEC "h264" #define SRS_CONF_DEFAULT_HLS_CLEANUP true +#define SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME true #define SRS_CONF_DEFAULT_HLS_NB_NOTIFY 64 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html/[app]/[stream].[timestamp].flv" #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" @@ -960,6 +961,10 @@ public: * whether cleanup the old ts files. */ virtual bool get_hls_cleanup(std::string vhost); + /** + * whether reap the ts when got keyframe. + */ + virtual bool get_hls_wait_keyframe(std::string vhost); /** * get the size of bytes to read from cdn network, for the on_hls_notify callback, * that is, to read max bytes of the bytes from the callback, or timeout or error. diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index d63d5f408..9eccccda2 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -267,6 +267,7 @@ SrsHlsMuxer::SrsHlsMuxer() hls_aof_ratio = 1.0; deviation_ts = 0; hls_cleanup = true; + hls_wait_keyframe = true; previous_floor_ts = 0; accept_floor_ts = 0; hls_ts_floor = false; @@ -335,7 +336,7 @@ int SrsHlsMuxer::initialize(ISrsHlsHandler* h) int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, string m3u8_file, string ts_file, double fragment, double window, - bool ts_floor, double aof_ratio, bool cleanup + bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe ) { int ret = ERROR_SUCCESS; @@ -349,6 +350,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, hls_aof_ratio = aof_ratio; hls_ts_floor = ts_floor; hls_cleanup = cleanup; + hls_wait_keyframe = wait_keyframe; previous_floor_ts = 0; accept_floor_ts = 0; hls_window = window; @@ -542,6 +544,11 @@ bool SrsHlsMuxer::is_segment_overflow() return current->duration >= hls_fragment + deviation; } +bool SrsHlsMuxer::wait_keyframe() +{ + return hls_wait_keyframe; +} + bool SrsHlsMuxer::is_segment_absolutely_overflow() { // @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-83553950 @@ -862,6 +869,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost); std::string ts_file = _srs_config->get_hls_ts_file(vhost); bool cleanup = _srs_config->get_hls_cleanup(vhost); + bool wait_keyframe = _srs_config->get_hls_wait_keyframe(vhost); // the audio overflow, for pure audio to reap segment. double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost); // whether use floor(timestamp/hls_fragment) for variable timestamp @@ -873,7 +881,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment // open muxer if ((ret = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment, hls_window, ts_floor, hls_aof_ratio, - cleanup)) != ERROR_SUCCESS + cleanup, wait_keyframe)) != ERROR_SUCCESS ) { srs_error("m3u8 muxer update config failed. ret=%d", ret); return ret; @@ -883,9 +891,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment srs_error("m3u8 muxer open segment failed. ret=%d", ret); return ret; } - srs_trace("hls: win=%.2f, frag=%.2f, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d", + srs_trace("hls: win=%.2f, frag=%.2f, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d, clean=%d, waitk=%d", hls_window, hls_fragment, entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(), - ts_file.c_str(), hls_aof_ratio, ts_floor); + ts_file.c_str(), hls_aof_ratio, ts_floor, cleanup, wait_keyframe); return ret; } @@ -972,17 +980,25 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t return ret; } - // new segment when: - // 1. base on gop(IDR). - // 2. some gops duration overflow. - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && muxer->is_segment_overflow()) { - if (!sample->has_idr) { - srs_warn("hls: ts starts without IDR, first nalu=%d, idr=%d", sample->first_nalu_type, sample->has_idr); - } - if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) { + // when segment overflow, reap if possible. + if (muxer->is_segment_overflow()) { + // do reap ts if any of: + // a. wait keyframe and got keyframe. + // b. always reap when not wait keyframe. + if (!muxer->wait_keyframe() || sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) { + // when wait keyframe, there must exists idr frame in sample. + if (!sample->has_idr && muxer->wait_keyframe()) { + srs_warn("hls: ts starts without IDR, first nalu=%d, idr=%d", sample->first_nalu_type, sample->has_idr); + } + + // reap the segment, which will also flush the video. + if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) { + return ret; + } + + // the video must be flushed, just return. return ret; } - return ret; } // flush video when got one diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 2ef8861c6..c84ee4ecf 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -207,6 +207,7 @@ private: std::string hls_path; std::string hls_ts_file; bool hls_cleanup; + bool hls_wait_keyframe; std::string m3u8_dir; double hls_aof_ratio; double hls_fragment; @@ -270,7 +271,7 @@ public: virtual int update_config(SrsRequest* r, std::string entry_prefix, std::string path, std::string m3u8_file, std::string ts_file, double fragment, double window, bool ts_floor, double aof_ratio, - bool cleanup); + bool cleanup, bool wait_keyframe); /** * open a new segment(a new ts file), * @param segment_start_dts use to calc the segment duration, @@ -283,6 +284,10 @@ public: * that is whether the current segment duration>=(the segment in config) */ virtual bool is_segment_overflow(); + /** + * whether wait keyframe to reap the ts. + */ + virtual bool wait_keyframe(); /** * whether segment absolutely overflow, for pure audio to reap segment, * that is whether the current segment duration>=2*(the segment in config) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 23e6f4dec..c339a2c63 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 159 +#define VERSION_REVISION 160 // server info. #define RTMP_SIG_SRS_KEY "SRS"