winlin

fix #381, support reap hls/ts by gop or not. 2.0.160.

@@ -562,6 +562,7 @@ Supported operating systems and hardware: @@ -562,6 +562,7 @@ Supported operating systems and hardware:
562 562
563 ### SRS 2.0 history 563 ### SRS 2.0 history
564 564
  565 +* 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.
565 * v2.0, 2015-04-10, enhanced on_hls_notify, support HTTP GET when reap ts. 566 * v2.0, 2015-04-10, enhanced on_hls_notify, support HTTP GET when reap ts.
566 * v2.0, 2015-04-10, refine the hls deviation for floor algorithm. 567 * v2.0, 2015-04-10, refine the hls deviation for floor algorithm.
567 * 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. 568 * 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.
@@ -618,6 +618,11 @@ vhost with-hls.srs.com { @@ -618,6 +618,11 @@ vhost with-hls.srs.com {
618 # @remark only used when on_hls_notify is config. 618 # @remark only used when on_hls_notify is config.
619 # default: 64 619 # default: 64
620 hls_nb_notify 64; 620 hls_nb_notify 64;
  621 + # whether wait keyframe to reap segment,
  622 + # if off, reap segment when duration exceed the fragment,
  623 + # if on, reap segment when duration exceed and got keyframe.
  624 + # default: on
  625 + hls_wait_keyframe on;
621 626
622 # on_hls, never config in here, should config in http_hooks. 627 # on_hls, never config in here, should config in http_hooks.
623 # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com 628 # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com
@@ -1487,7 +1487,7 @@ int SrsConfig::check_config() @@ -1487,7 +1487,7 @@ int SrsConfig::check_config()
1487 string m = conf->at(j)->name.c_str(); 1487 string m = conf->at(j)->name.c_str();
1488 if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" 1488 if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error"
1489 && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" 1489 && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec"
1490 - && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" 1490 + && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe"
1491 ) { 1491 ) {
1492 ret = ERROR_SYSTEM_CONFIG_INVALID; 1492 ret = ERROR_SYSTEM_CONFIG_INVALID;
1493 srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); 1493 srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret);
@@ -3451,6 +3451,23 @@ bool SrsConfig::get_hls_cleanup(string vhost) @@ -3451,6 +3451,23 @@ bool SrsConfig::get_hls_cleanup(string vhost)
3451 return SRS_CONF_PERFER_TRUE(conf->arg0()); 3451 return SRS_CONF_PERFER_TRUE(conf->arg0());
3452 } 3452 }
3453 3453
  3454 +bool SrsConfig::get_hls_wait_keyframe(string vhost)
  3455 +{
  3456 + SrsConfDirective* hls = get_hls(vhost);
  3457 +
  3458 + if (!hls) {
  3459 + return SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME;
  3460 + }
  3461 +
  3462 + SrsConfDirective* conf = hls->get("hls_wait_keyframe");
  3463 +
  3464 + if (!conf || conf->arg0().empty()) {
  3465 + return SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME;
  3466 + }
  3467 +
  3468 + return SRS_CONF_PERFER_TRUE(conf->arg0());
  3469 +}
  3470 +
3454 SrsConfDirective *SrsConfig::get_hds(const string &vhost) 3471 SrsConfDirective *SrsConfig::get_hds(const string &vhost)
3455 { 3472 {
3456 SrsConfDirective* conf = get_vhost(vhost); 3473 SrsConfDirective* conf = get_vhost(vhost);
@@ -63,6 +63,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -63,6 +63,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63 #define SRS_CONF_DEFAULT_HLS_ACODEC "aac" 63 #define SRS_CONF_DEFAULT_HLS_ACODEC "aac"
64 #define SRS_CONF_DEFAULT_HLS_VCODEC "h264" 64 #define SRS_CONF_DEFAULT_HLS_VCODEC "h264"
65 #define SRS_CONF_DEFAULT_HLS_CLEANUP true 65 #define SRS_CONF_DEFAULT_HLS_CLEANUP true
  66 +#define SRS_CONF_DEFAULT_HLS_WAIT_KEYFRAME true
66 #define SRS_CONF_DEFAULT_HLS_NB_NOTIFY 64 67 #define SRS_CONF_DEFAULT_HLS_NB_NOTIFY 64
67 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html/[app]/[stream].[timestamp].flv" 68 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html/[app]/[stream].[timestamp].flv"
68 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" 69 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
@@ -961,6 +962,10 @@ public: @@ -961,6 +962,10 @@ public:
961 */ 962 */
962 virtual bool get_hls_cleanup(std::string vhost); 963 virtual bool get_hls_cleanup(std::string vhost);
963 /** 964 /**
  965 + * whether reap the ts when got keyframe.
  966 + */
  967 + virtual bool get_hls_wait_keyframe(std::string vhost);
  968 + /**
964 * get the size of bytes to read from cdn network, for the on_hls_notify callback, 969 * get the size of bytes to read from cdn network, for the on_hls_notify callback,
965 * that is, to read max bytes of the bytes from the callback, or timeout or error. 970 * that is, to read max bytes of the bytes from the callback, or timeout or error.
966 */ 971 */
@@ -267,6 +267,7 @@ SrsHlsMuxer::SrsHlsMuxer() @@ -267,6 +267,7 @@ SrsHlsMuxer::SrsHlsMuxer()
267 hls_aof_ratio = 1.0; 267 hls_aof_ratio = 1.0;
268 deviation_ts = 0; 268 deviation_ts = 0;
269 hls_cleanup = true; 269 hls_cleanup = true;
  270 + hls_wait_keyframe = true;
270 previous_floor_ts = 0; 271 previous_floor_ts = 0;
271 accept_floor_ts = 0; 272 accept_floor_ts = 0;
272 hls_ts_floor = false; 273 hls_ts_floor = false;
@@ -335,7 +336,7 @@ int SrsHlsMuxer::initialize(ISrsHlsHandler* h) @@ -335,7 +336,7 @@ int SrsHlsMuxer::initialize(ISrsHlsHandler* h)
335 336
336 int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, 337 int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
337 string path, string m3u8_file, string ts_file, double fragment, double window, 338 string path, string m3u8_file, string ts_file, double fragment, double window,
338 - bool ts_floor, double aof_ratio, bool cleanup 339 + bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe
339 ) { 340 ) {
340 int ret = ERROR_SUCCESS; 341 int ret = ERROR_SUCCESS;
341 342
@@ -349,6 +350,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, @@ -349,6 +350,7 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
349 hls_aof_ratio = aof_ratio; 350 hls_aof_ratio = aof_ratio;
350 hls_ts_floor = ts_floor; 351 hls_ts_floor = ts_floor;
351 hls_cleanup = cleanup; 352 hls_cleanup = cleanup;
  353 + hls_wait_keyframe = wait_keyframe;
352 previous_floor_ts = 0; 354 previous_floor_ts = 0;
353 accept_floor_ts = 0; 355 accept_floor_ts = 0;
354 hls_window = window; 356 hls_window = window;
@@ -542,6 +544,11 @@ bool SrsHlsMuxer::is_segment_overflow() @@ -542,6 +544,11 @@ bool SrsHlsMuxer::is_segment_overflow()
542 return current->duration >= hls_fragment + deviation; 544 return current->duration >= hls_fragment + deviation;
543 } 545 }
544 546
  547 +bool SrsHlsMuxer::wait_keyframe()
  548 +{
  549 + return hls_wait_keyframe;
  550 +}
  551 +
545 bool SrsHlsMuxer::is_segment_absolutely_overflow() 552 bool SrsHlsMuxer::is_segment_absolutely_overflow()
546 { 553 {
547 // @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-83553950 554 // @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 @@ -862,6 +869,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
862 std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost); 869 std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost);
863 std::string ts_file = _srs_config->get_hls_ts_file(vhost); 870 std::string ts_file = _srs_config->get_hls_ts_file(vhost);
864 bool cleanup = _srs_config->get_hls_cleanup(vhost); 871 bool cleanup = _srs_config->get_hls_cleanup(vhost);
  872 + bool wait_keyframe = _srs_config->get_hls_wait_keyframe(vhost);
865 // the audio overflow, for pure audio to reap segment. 873 // the audio overflow, for pure audio to reap segment.
866 double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost); 874 double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost);
867 // whether use floor(timestamp/hls_fragment) for variable timestamp 875 // whether use floor(timestamp/hls_fragment) for variable timestamp
@@ -873,7 +881,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment @@ -873,7 +881,7 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
873 // open muxer 881 // open muxer
874 if ((ret = muxer->update_config(req, entry_prefix, 882 if ((ret = muxer->update_config(req, entry_prefix,
875 path, m3u8_file, ts_file, hls_fragment, hls_window, ts_floor, hls_aof_ratio, 883 path, m3u8_file, ts_file, hls_fragment, hls_window, ts_floor, hls_aof_ratio,
876 - cleanup)) != ERROR_SUCCESS 884 + cleanup, wait_keyframe)) != ERROR_SUCCESS
877 ) { 885 ) {
878 srs_error("m3u8 muxer update config failed. ret=%d", ret); 886 srs_error("m3u8 muxer update config failed. ret=%d", ret);
879 return ret; 887 return ret;
@@ -883,9 +891,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment @@ -883,9 +891,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
883 srs_error("m3u8 muxer open segment failed. ret=%d", ret); 891 srs_error("m3u8 muxer open segment failed. ret=%d", ret);
884 return ret; 892 return ret;
885 } 893 }
886 - srs_trace("hls: win=%.2f, frag=%.2f, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d", 894 + srs_trace("hls: win=%.2f, frag=%.2f, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d, clean=%d, waitk=%d",
887 hls_window, hls_fragment, entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(), 895 hls_window, hls_fragment, entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(),
888 - ts_file.c_str(), hls_aof_ratio, ts_floor); 896 + ts_file.c_str(), hls_aof_ratio, ts_floor, cleanup, wait_keyframe);
889 897
890 return ret; 898 return ret;
891 } 899 }
@@ -972,17 +980,25 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t @@ -972,17 +980,25 @@ int SrsHlsCache::write_video(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t
972 return ret; 980 return ret;
973 } 981 }
974 982
975 - // new segment when:  
976 - // 1. base on gop(IDR).  
977 - // 2. some gops duration overflow.  
978 - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && muxer->is_segment_overflow()) {  
979 - if (!sample->has_idr) {  
980 - srs_warn("hls: ts starts without IDR, first nalu=%d, idr=%d", sample->first_nalu_type, sample->has_idr);  
981 - }  
982 - if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) { 983 + // when segment overflow, reap if possible.
  984 + if (muxer->is_segment_overflow()) {
  985 + // do reap ts if any of:
  986 + // a. wait keyframe and got keyframe.
  987 + // b. always reap when not wait keyframe.
  988 + if (!muxer->wait_keyframe() || sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) {
  989 + // when wait keyframe, there must exists idr frame in sample.
  990 + if (!sample->has_idr && muxer->wait_keyframe()) {
  991 + srs_warn("hls: ts starts without IDR, first nalu=%d, idr=%d", sample->first_nalu_type, sample->has_idr);
  992 + }
  993 +
  994 + // reap the segment, which will also flush the video.
  995 + if ((ret = reap_segment("video", muxer, cache->video->dts)) != ERROR_SUCCESS) {
  996 + return ret;
  997 + }
  998 +
  999 + // the video must be flushed, just return.
983 return ret; 1000 return ret;
984 } 1001 }
985 - return ret;  
986 } 1002 }
987 1003
988 // flush video when got one 1004 // flush video when got one
@@ -207,6 +207,7 @@ private: @@ -207,6 +207,7 @@ private:
207 std::string hls_path; 207 std::string hls_path;
208 std::string hls_ts_file; 208 std::string hls_ts_file;
209 bool hls_cleanup; 209 bool hls_cleanup;
  210 + bool hls_wait_keyframe;
210 std::string m3u8_dir; 211 std::string m3u8_dir;
211 double hls_aof_ratio; 212 double hls_aof_ratio;
212 double hls_fragment; 213 double hls_fragment;
@@ -270,7 +271,7 @@ public: @@ -270,7 +271,7 @@ public:
270 virtual int update_config(SrsRequest* r, std::string entry_prefix, 271 virtual int update_config(SrsRequest* r, std::string entry_prefix,
271 std::string path, std::string m3u8_file, std::string ts_file, 272 std::string path, std::string m3u8_file, std::string ts_file,
272 double fragment, double window, bool ts_floor, double aof_ratio, 273 double fragment, double window, bool ts_floor, double aof_ratio,
273 - bool cleanup); 274 + bool cleanup, bool wait_keyframe);
274 /** 275 /**
275 * open a new segment(a new ts file), 276 * open a new segment(a new ts file),
276 * @param segment_start_dts use to calc the segment duration, 277 * @param segment_start_dts use to calc the segment duration,
@@ -284,6 +285,10 @@ public: @@ -284,6 +285,10 @@ public:
284 */ 285 */
285 virtual bool is_segment_overflow(); 286 virtual bool is_segment_overflow();
286 /** 287 /**
  288 + * whether wait keyframe to reap the ts.
  289 + */
  290 + virtual bool wait_keyframe();
  291 + /**
287 * whether segment absolutely overflow, for pure audio to reap segment, 292 * whether segment absolutely overflow, for pure audio to reap segment,
288 * that is whether the current segment duration>=2*(the segment in config) 293 * that is whether the current segment duration>=2*(the segment in config)
289 * @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184 294 * @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-71155184
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // current release version 31 // current release version
32 #define VERSION_MAJOR 2 32 #define VERSION_MAJOR 2
33 #define VERSION_MINOR 0 33 #define VERSION_MINOR 0
34 -#define VERSION_REVISION 159 34 +#define VERSION_REVISION 160
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"