正在显示
5 个修改的文件
包含
185 行增加
和
24 行删除
@@ -47,6 +47,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -47,6 +47,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
47 | #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" | 47 | #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" |
48 | #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" | 48 | #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" |
49 | #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" | 49 | #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" |
50 | +// chnvideo hss | ||
51 | +#define SRS_CONF_DEFAULT_DVR_PLAN_HSS "hss" | ||
50 | #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION | 52 | #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION |
51 | #define SRS_CONF_DEFAULT_DVR_DURATION 30 | 53 | #define SRS_CONF_DEFAULT_DVR_DURATION 30 |
52 | // in ms, for HLS aac sync time. | 54 | // in ms, for HLS aac sync time. |
@@ -299,18 +299,20 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | @@ -299,18 +299,20 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | ||
299 | 299 | ||
300 | SrsFlvSegment::SrsFlvSegment() | 300 | SrsFlvSegment::SrsFlvSegment() |
301 | { | 301 | { |
302 | - current_flv_path = ""; | ||
303 | - segment_has_keyframe = false; | 302 | + path = ""; |
303 | + has_keyframe = false; | ||
304 | duration = 0; | 304 | duration = 0; |
305 | starttime = -1; | 305 | starttime = -1; |
306 | stream_starttime = 0; | 306 | stream_starttime = 0; |
307 | + stream_previous_pkt_time = -1; | ||
308 | + stream_duration = 0; | ||
307 | } | 309 | } |
308 | 310 | ||
309 | void SrsFlvSegment::reset() | 311 | void SrsFlvSegment::reset() |
310 | { | 312 | { |
311 | - segment_has_keyframe = false; | ||
312 | - duration = 0; | 313 | + has_keyframe = false; |
313 | starttime = -1; | 314 | starttime = -1; |
315 | + duration = 0; | ||
314 | } | 316 | } |
315 | 317 | ||
316 | SrsDvrPlan::SrsDvrPlan() | 318 | SrsDvrPlan::SrsDvrPlan() |
@@ -357,7 +359,7 @@ int SrsDvrPlan::on_publish() | @@ -357,7 +359,7 @@ int SrsDvrPlan::on_publish() | ||
357 | return ret; | 359 | return ret; |
358 | } | 360 | } |
359 | 361 | ||
360 | - // jitter. | 362 | + // jitter when publish, ensure whole stream start from 0. |
361 | srs_freep(jitter); | 363 | srs_freep(jitter); |
362 | jitter = new SrsRtmpJitter(); | 364 | jitter = new SrsRtmpJitter(); |
363 | 365 | ||
@@ -365,7 +367,9 @@ int SrsDvrPlan::on_publish() | @@ -365,7 +367,9 @@ int SrsDvrPlan::on_publish() | ||
365 | srs_update_system_time_ms(); | 367 | srs_update_system_time_ms(); |
366 | 368 | ||
367 | // when republish, stream starting. | 369 | // when republish, stream starting. |
370 | + segment->stream_previous_pkt_time = -1; | ||
368 | segment->stream_starttime = srs_get_system_time_ms(); | 371 | segment->stream_starttime = srs_get_system_time_ms(); |
372 | + segment->stream_duration = 0; | ||
369 | 373 | ||
370 | if ((ret = open_new_segment()) != ERROR_SUCCESS) { | 374 | if ((ret = open_new_segment()) != ERROR_SUCCESS) { |
371 | return ret; | 375 | return ret; |
@@ -470,7 +474,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video) | @@ -470,7 +474,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video) | ||
470 | #ifdef SRS_AUTO_HTTP_CALLBACK | 474 | #ifdef SRS_AUTO_HTTP_CALLBACK |
471 | bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size); | 475 | bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size); |
472 | if (is_key_frame) { | 476 | if (is_key_frame) { |
473 | - segment->segment_has_keyframe = true; | 477 | + segment->has_keyframe = true; |
474 | } | 478 | } |
475 | srs_verbose("dvr video is key: %d", is_key_frame); | 479 | srs_verbose("dvr video is key: %d", is_key_frame); |
476 | #endif | 480 | #endif |
@@ -504,7 +508,7 @@ int SrsDvrPlan::flv_open(string stream, string path) | @@ -504,7 +508,7 @@ int SrsDvrPlan::flv_open(string stream, string path) | ||
504 | return ret; | 508 | return ret; |
505 | } | 509 | } |
506 | 510 | ||
507 | - segment->current_flv_path = path; | 511 | + segment->path = path; |
508 | 512 | ||
509 | srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str()); | 513 | srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str()); |
510 | return ret; | 514 | return ret; |
@@ -518,16 +522,16 @@ int SrsDvrPlan::flv_close() | @@ -518,16 +522,16 @@ int SrsDvrPlan::flv_close() | ||
518 | return ret; | 522 | return ret; |
519 | } | 523 | } |
520 | 524 | ||
521 | - std::string tmp_file = segment->current_flv_path + ".tmp"; | ||
522 | - if (rename(tmp_file.c_str(), segment->current_flv_path.c_str()) < 0) { | 525 | + std::string tmp_file = segment->path + ".tmp"; |
526 | + if (rename(tmp_file.c_str(), segment->path.c_str()) < 0) { | ||
523 | ret = ERROR_SYSTEM_FILE_RENAME; | 527 | ret = ERROR_SYSTEM_FILE_RENAME; |
524 | srs_error("rename flv file failed, %s => %s. ret=%d", | 528 | srs_error("rename flv file failed, %s => %s. ret=%d", |
525 | - tmp_file.c_str(), segment->current_flv_path.c_str(), ret); | 529 | + tmp_file.c_str(), segment->path.c_str(), ret); |
526 | return ret; | 530 | return ret; |
527 | } | 531 | } |
528 | 532 | ||
529 | #ifdef SRS_AUTO_HTTP_CALLBACK | 533 | #ifdef SRS_AUTO_HTTP_CALLBACK |
530 | - if (segment->segment_has_keyframe) { | 534 | + if (segment->has_keyframe) { |
531 | if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) { | 535 | if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) { |
532 | return ret; | 536 | return ret; |
533 | } | 537 | } |
@@ -540,13 +544,26 @@ int SrsDvrPlan::flv_close() | @@ -540,13 +544,26 @@ int SrsDvrPlan::flv_close() | ||
540 | int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) | 544 | int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) |
541 | { | 545 | { |
542 | int ret = ERROR_SUCCESS; | 546 | int ret = ERROR_SUCCESS; |
547 | + | ||
548 | + // we must assumpt that the stream timestamp is monotonically increase, | ||
549 | + // that is, always use time jitter to correct the timestamp. | ||
543 | 550 | ||
544 | - // foreach msg, collect the duration. | ||
545 | - if (segment->starttime < 0 || segment->starttime > msg->header.timestamp) { | 551 | + // set the segment starttime at first time |
552 | + if (segment->starttime < 0) { | ||
546 | segment->starttime = msg->header.timestamp; | 553 | segment->starttime = msg->header.timestamp; |
547 | } | 554 | } |
548 | - segment->duration += msg->header.timestamp - segment->starttime; | ||
549 | - segment->starttime = msg->header.timestamp; | 555 | + |
556 | + // no previous packet or timestamp overflow. | ||
557 | + if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->header.timestamp) { | ||
558 | + segment->stream_previous_pkt_time = msg->header.timestamp; | ||
559 | + } | ||
560 | + | ||
561 | + // collect segment and stream duration, timestamp overflow is ok. | ||
562 | + segment->duration += msg->header.timestamp - segment->stream_previous_pkt_time; | ||
563 | + segment->stream_duration += msg->header.timestamp - segment->stream_previous_pkt_time; | ||
564 | + | ||
565 | + // update previous packet time | ||
566 | + segment->stream_previous_pkt_time = msg->header.timestamp; | ||
550 | 567 | ||
551 | return ret; | 568 | return ret; |
552 | } | 569 | } |
@@ -579,6 +596,8 @@ SrsDvrPlan* SrsDvrPlan::create_plan(string vhost) | @@ -579,6 +596,8 @@ SrsDvrPlan* SrsDvrPlan::create_plan(string vhost) | ||
579 | return new SrsDvrSegmentPlan(); | 596 | return new SrsDvrSegmentPlan(); |
580 | } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) { | 597 | } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) { |
581 | return new SrsDvrSessionPlan(); | 598 | return new SrsDvrSessionPlan(); |
599 | + } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_HSS) { | ||
600 | + return new SrsDvrHssPlan(); | ||
582 | } else { | 601 | } else { |
583 | return new SrsDvrSessionPlan(); | 602 | return new SrsDvrSessionPlan(); |
584 | } | 603 | } |
@@ -683,6 +702,111 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | @@ -683,6 +702,111 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) | ||
683 | return ret; | 702 | return ret; |
684 | } | 703 | } |
685 | 704 | ||
705 | +SrsDvrHssPlan::SrsDvrHssPlan() | ||
706 | +{ | ||
707 | + segment_duration = -1; | ||
708 | + start_deviation = 0; | ||
709 | + expect_reap_time = 0; | ||
710 | +} | ||
711 | + | ||
712 | +SrsDvrHssPlan::~SrsDvrHssPlan() | ||
713 | +{ | ||
714 | +} | ||
715 | + | ||
716 | +int SrsDvrHssPlan::initialize(SrsSource* source, SrsRequest* req) | ||
717 | +{ | ||
718 | + int ret = ERROR_SUCCESS; | ||
719 | + | ||
720 | + if ((ret = SrsDvrPlan::initialize(source, req)) != ERROR_SUCCESS) { | ||
721 | + return ret; | ||
722 | + } | ||
723 | + | ||
724 | + // TODO: FIXME: support reload | ||
725 | + segment_duration = _srs_config->get_dvr_duration(req->vhost); | ||
726 | + // to ms | ||
727 | + segment_duration *= 1000; | ||
728 | + | ||
729 | + return ret; | ||
730 | +} | ||
731 | + | ||
732 | +int SrsDvrHssPlan::on_publish() | ||
733 | +{ | ||
734 | + int ret = ERROR_SUCCESS; | ||
735 | + | ||
736 | + // if already opened, continue to dvr. | ||
737 | + // the segment plan maybe keep running longer than the encoder. | ||
738 | + // for example, segment running, encoder restart, | ||
739 | + // the segment plan will just continue going and donot open new segment. | ||
740 | + if (fs->is_open()) { | ||
741 | + dvr_enabled = true; | ||
742 | + return ret; | ||
743 | + } | ||
744 | + | ||
745 | + if ((ret = SrsDvrPlan::on_publish()) != ERROR_SUCCESS) { | ||
746 | + return ret; | ||
747 | + } | ||
748 | + | ||
749 | + // expect reap flv time | ||
750 | + expect_reap_time = segment->stream_starttime + segment_duration; | ||
751 | + // the start deviation used ensure the segment starttime in nature clock. | ||
752 | + start_deviation = segment->stream_starttime % 1000; | ||
753 | + | ||
754 | + return ret; | ||
755 | +} | ||
756 | + | ||
757 | +void SrsDvrHssPlan::on_unpublish() | ||
758 | +{ | ||
759 | + // support multiple publish. | ||
760 | + if (!dvr_enabled) { | ||
761 | + return; | ||
762 | + } | ||
763 | + dvr_enabled = false; | ||
764 | +} | ||
765 | + | ||
766 | +int SrsDvrHssPlan::update_duration(SrsSharedPtrMessage* msg) | ||
767 | +{ | ||
768 | + int ret = ERROR_SUCCESS; | ||
769 | + | ||
770 | + if ((ret = SrsDvrPlan::update_duration(msg)) != ERROR_SUCCESS) { | ||
771 | + return ret; | ||
772 | + } | ||
773 | + | ||
774 | + srs_assert(segment); | ||
775 | + | ||
776 | + // if not initialized, ignore reap. | ||
777 | + if (expect_reap_time <= 0 | ||
778 | + || segment->stream_starttime <= 0 | ||
779 | + || segment->stream_duration <= 0 | ||
780 | + ) { | ||
781 | + return ret; | ||
782 | + } | ||
783 | + | ||
784 | + // reap if exceed atc expect time. | ||
785 | + if (segment->stream_starttime + segment->stream_duration > expect_reap_time) { | ||
786 | + srs_warn("hss reap start=%"PRId64", duration=%"PRId64", expect=%"PRId64 | ||
787 | + ", segment(start=%"PRId64", adjust=%"PRId64", duration=%"PRId64", file=%s", | ||
788 | + segment->stream_starttime, segment->stream_duration, expect_reap_time, | ||
789 | + segment->stream_starttime + segment->starttime, | ||
790 | + segment->stream_starttime + segment->starttime - start_deviation, | ||
791 | + segment->duration, segment->path.c_str()); | ||
792 | + | ||
793 | + // update expect reap time | ||
794 | + expect_reap_time += segment_duration; | ||
795 | + | ||
796 | + if ((ret = flv_close()) != ERROR_SUCCESS) { | ||
797 | + segment->reset(); | ||
798 | + return ret; | ||
799 | + } | ||
800 | + on_unpublish(); | ||
801 | + | ||
802 | + if ((ret = open_new_segment()) != ERROR_SUCCESS) { | ||
803 | + return ret; | ||
804 | + } | ||
805 | + } | ||
806 | + | ||
807 | + return ret; | ||
808 | +} | ||
809 | + | ||
686 | SrsDvr::SrsDvr(SrsSource* source) | 810 | SrsDvr::SrsDvr(SrsSource* source) |
687 | { | 811 | { |
688 | _source = source; | 812 | _source = source; |
@@ -114,22 +114,34 @@ class SrsFlvSegment | @@ -114,22 +114,34 @@ class SrsFlvSegment | ||
114 | { | 114 | { |
115 | public: | 115 | public: |
116 | /** | 116 | /** |
117 | - * current flv file path. | 117 | + * current segment flv file path. |
118 | */ | 118 | */ |
119 | - std::string current_flv_path; | 119 | + std::string path; |
120 | /** | 120 | /** |
121 | * whether current segment has keyframe. | 121 | * whether current segment has keyframe. |
122 | */ | 122 | */ |
123 | - bool segment_has_keyframe; | 123 | + bool has_keyframe; |
124 | /** | 124 | /** |
125 | - * current segment duration and starttime. | 125 | + * current segment starttime, RTMP pkt time. |
126 | */ | 126 | */ |
127 | - int64_t duration; | ||
128 | int64_t starttime; | 127 | int64_t starttime; |
129 | /** | 128 | /** |
130 | - * stream start time, to generate atc pts. | 129 | + * current segment duration |
130 | + */ | ||
131 | + int64_t duration; | ||
132 | + /** | ||
133 | + * stream start time, to generate atc pts. abs time. | ||
131 | */ | 134 | */ |
132 | int64_t stream_starttime; | 135 | int64_t stream_starttime; |
136 | + /** | ||
137 | + * stream duration, to generate atc segment. | ||
138 | + */ | ||
139 | + int64_t stream_duration; | ||
140 | + /** | ||
141 | + * previous stream RTMP pkt time, used to calc the duration. | ||
142 | + * for the RTMP timestamp will overflow. | ||
143 | + */ | ||
144 | + int64_t stream_previous_pkt_time; | ||
133 | public: | 145 | public: |
134 | SrsFlvSegment(); | 146 | SrsFlvSegment(); |
135 | virtual void reset(); | 147 | virtual void reset(); |
@@ -214,6 +226,28 @@ private: | @@ -214,6 +226,28 @@ private: | ||
214 | }; | 226 | }; |
215 | 227 | ||
216 | /** | 228 | /** |
229 | +* hss plan: use atc time to reap flv segment | ||
230 | +*/ | ||
231 | +class SrsDvrHssPlan : public SrsDvrPlan | ||
232 | +{ | ||
233 | +private: | ||
234 | + // in config, in ms | ||
235 | + int segment_duration; | ||
236 | + // the deviation of starttime of the nature clock time. | ||
237 | + int start_deviation; | ||
238 | + int64_t expect_reap_time; | ||
239 | +public: | ||
240 | + SrsDvrHssPlan(); | ||
241 | + virtual ~SrsDvrHssPlan(); | ||
242 | +public: | ||
243 | + virtual int initialize(SrsSource* source, SrsRequest* req); | ||
244 | + virtual int on_publish(); | ||
245 | + virtual void on_unpublish(); | ||
246 | +private: | ||
247 | + virtual int update_duration(SrsSharedPtrMessage* msg); | ||
248 | +}; | ||
249 | + | ||
250 | +/** | ||
217 | * dvr(digital video recorder) to record RTMP stream to flv file. | 251 | * dvr(digital video recorder) to record RTMP stream to flv file. |
218 | * TODO: FIXME: add utest for it. | 252 | * TODO: FIXME: add utest for it. |
219 | */ | 253 | */ |
@@ -462,10 +462,11 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s | @@ -462,10 +462,11 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s | ||
462 | { | 462 | { |
463 | int ret = ERROR_SUCCESS; | 463 | int ret = ERROR_SUCCESS; |
464 | 464 | ||
465 | + srs_assert(segment); | ||
465 | srs_trace("flv segment %s, atc_start=%"PRId64", " | 466 | srs_trace("flv segment %s, atc_start=%"PRId64", " |
466 | "has_key=%d, starttime=%"PRId64", duration=%d", | 467 | "has_key=%d, starttime=%"PRId64", duration=%d", |
467 | - segment->current_flv_path.c_str(), segment->stream_starttime, | ||
468 | - segment->segment_has_keyframe, segment->starttime, (int)segment->duration); | 468 | + segment->path.c_str(), segment->stream_starttime, |
469 | + segment->has_keyframe, segment->starttime, (int)segment->duration); | ||
469 | 470 | ||
470 | SrsHttpUri uri; | 471 | SrsHttpUri uri; |
471 | if ((ret = uri.initialize(url)) != ERROR_SUCCESS) { | 472 | if ((ret = uri.initialize(url)) != ERROR_SUCCESS) { |
@@ -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 "0" | 32 | #define VERSION_MAJOR "0" |
33 | #define VERSION_MINOR "9" | 33 | #define VERSION_MINOR "9" |
34 | -#define VERSION_REVISION "73" | 34 | +#define VERSION_REVISION "74" |
35 | #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION | 35 | #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION |
36 | // server info. | 36 | // server info. |
37 | #define RTMP_SIG_SRS_KEY "srs" | 37 | #define RTMP_SIG_SRS_KEY "srs" |
-
请 注册 或 登录 后发表评论