diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 2326e7a33..4fdeb9c0f 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -1279,6 +1279,7 @@ public: /** * get the hds fragment time, in seconds. */ + // TODO: FIXME: Refine to time unit. virtual double get_hds_fragment(const std::string &vhost); /** * get the hds window time, in seconds. diff --git a/trunk/src/app/srs_app_dash.cpp b/trunk/src/app/srs_app_dash.cpp index 7a45589e3..509410e32 100644 --- a/trunk/src/app/srs_app_dash.cpp +++ b/trunk/src/app/srs_app_dash.cpp @@ -335,7 +335,7 @@ srs_error_t SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFo return refresh_init_mp4(shared_audio, format); } - if (acurrent->duration() >= int64_t(srsu2ms(fragment))) { + if (acurrent->duration() >= fragment) { if ((err = acurrent->reap(audio_dts)) != srs_success) { return srs_error_wrap(err, "reap current"); } @@ -367,7 +367,7 @@ srs_error_t SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFo return refresh_init_mp4(shared_video, format); } - bool reopen = format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame && vcurrent->duration() >= int64_t(srsu2ms(fragment)); + bool reopen = format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame && vcurrent->duration() >= fragment; if (reopen) { if ((err = vcurrent->reap(video_dts)) != srs_success) { return srs_error_wrap(err, "reap current"); diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 4db4ff9eb..563f7a8d8 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -286,7 +286,7 @@ srs_error_t SrsDvrFlvSegmenter::refresh_metadata() } // duration to buf - SrsAmf0Any* dur = SrsAmf0Any::number((double)fragment->duration() / 1000.0); + SrsAmf0Any* dur = SrsAmf0Any::number((double)srsu2ms(fragment->duration()) / 1000.0); SrsAutoFree(SrsAmf0Any, dur); stream.skip(-1 * stream.pos()); @@ -831,7 +831,7 @@ srs_error_t SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) // ignore if duration ok. SrsFragment* fragment = segment->current(); - if (cduration <= 0 || fragment->duration() < int64_t(srsu2ms(cduration))) { + if (cduration <= 0 || fragment->duration() < cduration) { return err; } diff --git a/trunk/src/app/srs_app_fragment.cpp b/trunk/src/app/srs_app_fragment.cpp index 886db1ba7..ee7df6eb2 100644 --- a/trunk/src/app/srs_app_fragment.cpp +++ b/trunk/src/app/srs_app_fragment.cpp @@ -44,16 +44,24 @@ SrsFragment::~SrsFragment() void SrsFragment::append(int64_t dts) { + // The max positive ms is 0x7fffffffffffffff/1000. + static const int64_t maxMS = 0x20c49ba5e353f7LL; + + // We reset negative or overflow dts to zero. + if (dts > maxMS || dts < 0) { + dts = 0; + } + if (start_dts == -1) { start_dts = dts; } // TODO: FIXME: Use cumulus dts. start_dts = srs_min(start_dts, dts); - dur = dts - start_dts; + dur = srs_utime_t(dts - start_dts) * SRS_UTIME_MILLISECONDS; } -int64_t SrsFragment::duration() +srs_utime_t SrsFragment::duration() { return dur; } @@ -127,7 +135,7 @@ srs_error_t SrsFragment::rename() string full_path = fullpath(); string tmp_file = tmppath(); - int tempdur = (int)duration(); + int tempdur = srsu2msi(duration()); if (true) { std::stringstream ss; ss << tempdur; @@ -195,7 +203,7 @@ void SrsFragmentWindow::append(SrsFragment* fragment) void SrsFragmentWindow::shrink(int64_t window) { - int64_t duration = 0; + srs_utime_t duration = 0; int remove_index = -1; @@ -203,7 +211,7 @@ void SrsFragmentWindow::shrink(int64_t window) SrsFragment* fragment = fragments[i]; duration += fragment->duration(); - if (duration > window) { + if (srsu2ms(duration) > window) { remove_index = i; break; } @@ -242,7 +250,7 @@ int64_t SrsFragmentWindow::max_duration() for (it = fragments.begin(); it != fragments.end(); ++it) { SrsFragment* fragment = *it; - v = srs_max(v, fragment->duration()); + v = srs_max(v, srsu2ms(fragment->duration())); } return v; diff --git a/trunk/src/app/srs_app_fragment.hpp b/trunk/src/app/srs_app_fragment.hpp index 2495e9fc1..b1aa15f60 100644 --- a/trunk/src/app/srs_app_fragment.hpp +++ b/trunk/src/app/srs_app_fragment.hpp @@ -36,8 +36,8 @@ class SrsFragment { private: - // The duration in ms. - int64_t dur; + // The duration in srs_utime_t. + srs_utime_t dur; // The full file path of fragment. std::string filepath; // The start DTS in ms of segment. @@ -51,9 +51,8 @@ public: // Append a frame with dts into fragment. // @dts The dts of frame in ms. virtual void append(int64_t dts); - // Get the duration of fragment in ms. - // TODO: FIXME: Refine to time unit. - virtual int64_t duration(); + // Get the duration of fragment in srs_utime_t. + virtual srs_utime_t duration(); // Whether the fragment contains any sequence header. virtual bool is_sequence_header(); // Set whether contains sequence header. diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 8e27cf5c8..72e133ce6 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -54,6 +54,7 @@ using namespace std; #include // drop the segment when duration of ts too small. +// TODO: FIXME: Refine to time unit. #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 // fragment plus the deviation percent. @@ -253,7 +254,7 @@ string SrsHlsMuxer::ts_url() double SrsHlsMuxer::duration() { - return current? current->duration()/1000.0:0; + return current? srsu2ms(current->duration())/1000.0:0; } int SrsHlsMuxer::deviation() @@ -483,14 +484,14 @@ bool SrsHlsMuxer::is_segment_overflow() srs_assert(current); // to prevent very small segment. - if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { + if (srsu2msi(current->duration()) < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { return false; } // use N% deviation, to smoother. double deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * deviation_ts * hls_fragment : 0.0; - return current->duration() >= (hls_fragment + deviation) * 1000; + return srsu2msi(current->duration()) >= (hls_fragment + deviation) * 1000; } bool SrsHlsMuxer::wait_keyframe() @@ -504,14 +505,14 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow() srs_assert(current); // to prevent very small segment. - if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { + if (srsu2msi(current->duration()) < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { return false; } // use N% deviation, to smoother. double deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * deviation_ts * hls_fragment : 0.0; - return current->duration() >= (hls_aof_ratio * hls_fragment + deviation) * 1000; + return srsu2msi(current->duration()) >= (hls_aof_ratio * hls_fragment + deviation) * 1000; } bool SrsHlsMuxer::pure_audio() @@ -591,10 +592,10 @@ srs_error_t SrsHlsMuxer::segment_close() // when too small, it maybe not enough data to play. // when too large, it maybe timestamp corrupt. // make the segment more acceptable, when in [min, max_td * 2], it's ok. - if (current->duration() >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS && (int)current->duration() <= max_td * 2 * 1000) { + if (srsu2msi(current->duration()) >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS && (int)srsu2msi(current->duration()) <= max_td * 2 * 1000) { // use async to call the http hooks, for it will cause thread switch. if ((err = async->execute(new SrsDvrAsyncCallOnHls(_srs_context->get_id(), req, current->fullpath(), - current->uri, m3u8, m3u8_url, current->sequence_no, current->duration() / 1000.0))) != srs_success) { + current->uri, m3u8, m3u8_url, current->sequence_no, srsu2msi(current->duration()) / 1000.0))) != srs_success) { return srs_error_wrap(err, "segment close"); } @@ -617,7 +618,8 @@ srs_error_t SrsHlsMuxer::segment_close() // reuse current segment index. _sequence_no--; - srs_trace("Drop ts segment, sequence_no=%d, uri=%s, duration=%" PRId64 "ms", current->sequence_no, current->uri.c_str(), current->duration()); + srs_trace("Drop ts segment, sequence_no=%d, uri=%s, duration=%dms", + current->sequence_no, current->uri.c_str(), srsu2msi(current->duration())); // rename from tmp to real path if ((err = current->unlink_tmpfile()) != srs_success) { @@ -778,13 +780,13 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file) // "#EXTINF:4294967295.208,\n" ss.precision(3); ss.setf(std::ios::fixed, std::ios::floatfield); - ss << "#EXTINF:" << segment->duration() / 1000.0 << ", no desc" << SRS_CONSTS_LF; + ss << "#EXTINF:" << srsu2msi(segment->duration()) / 1000.0 << ", no desc" << SRS_CONSTS_LF; // {file name}\n std::string seg_uri = segment->uri; if (true) { std::stringstream stemp; - stemp << (int)(segment->duration()); + stemp << srsu2msi(segment->duration()); seg_uri = srs_string_replace(seg_uri, "[duration]", stemp.str()); } //ss << segment->uri << SRS_CONSTS_LF; diff --git a/trunk/src/utest/srs_utest.hpp b/trunk/src/utest/srs_utest.hpp index 26691c743..f9222fe43 100644 --- a/trunk/src/utest/srs_utest.hpp +++ b/trunk/src/utest/srs_utest.hpp @@ -24,6 +24,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SRS_UTEST_PUBLIC_SHARED_HPP #define SRS_UTEST_PUBLIC_SHARED_HPP +// Public all private and protected members. +#define private public +#define protected public + /* #include */ @@ -36,10 +40,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // we add an empty macro for upp to show the smart tips. #define VOID -// Public all private and protected members. -#define private public -#define protected public - // the asserts of gtest: // * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual // * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 diff --git a/trunk/src/utest/srs_utest_app.cpp b/trunk/src/utest/srs_utest_app.cpp index f7f46730f..28f9fcd2f 100644 --- a/trunk/src/utest/srs_utest_app.cpp +++ b/trunk/src/utest/srs_utest_app.cpp @@ -25,6 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace std; #include +#include // Disable coroutine test for OSX. #if !defined(SRS_OSX) @@ -307,3 +308,72 @@ VOID TEST(AppCoroutineTest, StartThread) } #endif + +VOID TEST(AppFragmentTest, CheckDuration) +{ + if (true) { + SrsFragment frg; + EXPECT_EQ(-1, frg.start_dts); + EXPECT_EQ(0, frg.dur); + EXPECT_FALSE(frg.sequence_header); + } + + if (true) { + SrsFragment frg; + + frg.append(0); + EXPECT_EQ(0, frg.duration()); + + frg.append(10); + EXPECT_EQ(10 * SRS_UTIME_MILLISECONDS, frg.duration()); + + frg.append(99); + EXPECT_EQ(99 * SRS_UTIME_MILLISECONDS, frg.duration()); + + frg.append(0x7fffffffLL); + EXPECT_EQ(0x7fffffffLL * SRS_UTIME_MILLISECONDS, frg.duration()); + + frg.append(0xffffffffLL); + EXPECT_EQ(0xffffffffLL * SRS_UTIME_MILLISECONDS, frg.duration()); + + frg.append(0x20c49ba5e353f7LL); + EXPECT_EQ(0x20c49ba5e353f7LL * SRS_UTIME_MILLISECONDS, frg.duration()); + } + + if (true) { + SrsFragment frg; + + frg.append(0); + EXPECT_EQ(0, frg.duration()); + + frg.append(0x7fffffffffffffffLL); + EXPECT_EQ(0, frg.duration()); + } + + if (true) { + SrsFragment frg; + + frg.append(100); + EXPECT_EQ(0, frg.duration()); + + frg.append(10); + EXPECT_EQ(0, frg.duration()); + + frg.append(100); + EXPECT_EQ(90 * SRS_UTIME_MILLISECONDS, frg.duration()); + } + + if (true) { + SrsFragment frg; + + frg.append(-10); + EXPECT_EQ(0, frg.duration()); + + frg.append(-5); + EXPECT_EQ(0, frg.duration()); + + frg.append(10); + EXPECT_EQ(10 * SRS_UTIME_MILLISECONDS, frg.duration()); + } +} +