正在显示
7 个修改的文件
包含
65 行增加
和
16 行删除
@@ -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" |
-
请 注册 或 登录 后发表评论