正在显示
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 | } |
| @@ -541,12 +545,25 @@ int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) | @@ -541,12 +545,25 @@ int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) | ||
| 541 | { | 545 | { |
| 542 | int ret = ERROR_SUCCESS; | 546 | int ret = ERROR_SUCCESS; |
| 543 | 547 | ||
| 544 | - // foreach msg, collect the duration. | ||
| 545 | - if (segment->starttime < 0 || segment->starttime > msg->header.timestamp) { | 548 | + // we must assumpt that the stream timestamp is monotonically increase, |
| 549 | + // that is, always use time jitter to correct the timestamp. | ||
| 550 | + | ||
| 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" |
-
请 注册 或 登录 后发表评论