From b8e7f4cc9763a473a9abd8e20d832a734881f99d Mon Sep 17 00:00:00 2001 From: "stefan@webrtc.org" Date: Fri, 12 Apr 2013 11:56:23 +0000 Subject: [PATCH] Change capture interface to use NTP capture time. Move NTP functionality to Clock. BUG=1563 TEST=trybots and vie_auto_test --automated Review URL: https://webrtc-codereview.appspot.com/1313005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3842 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../remote_bitrate_estimator/rtp_to_ntp.cc | 18 ++-- .../modules/rtp_rtcp/source/rtcp_receiver.cc | 4 +- webrtc/modules/rtp_rtcp/source/rtcp_sender.cc | 4 +- webrtc/modules/rtp_rtcp/source/rtp_utility.h | 5 - .../include/video_capture_defines.h | 1 + .../video_capture/video_capture_impl.cc | 9 +- .../video_capture/video_capture_impl.h | 1 + webrtc/system_wrappers/interface/clock.h | 13 ++- webrtc/system_wrappers/source/clock.cc | 93 ++++++++++++------- .../system_wrappers/source/clock_unittest.cc | 26 ++++++ .../source/system_wrappers_tests.gyp | 1 + webrtc/video_engine/include/vie_capture.h | 2 + 12 files changed, 117 insertions(+), 60 deletions(-) create mode 100644 webrtc/system_wrappers/source/clock_unittest.cc diff --git a/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc b/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc index 642c02d918..5ed38c9fc5 100644 --- a/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc +++ b/webrtc/modules/remote_bitrate_estimator/rtp_to_ntp.cc @@ -8,7 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/remote_bitrate_estimator/include/rtp_to_ntp.h" +#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h" + +#include "webrtc/system_wrappers/interface/clock.h" #include @@ -57,12 +59,6 @@ bool CompensateForWrapAround(uint32_t new_timestamp, return true; } -// Converts an NTP timestamp to a millisecond timestamp. -int64_t NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) { - const double ntp_frac_ms = static_cast(ntp_frac) / kNtpFracPerMs; - return ntp_secs * 1000 + ntp_frac_ms + 0.5; -} - // Converts |rtp_timestamp| to the NTP time base using the NTP and RTP timestamp // pairs in |rtcp|. The converted timestamp is returned in // |rtp_timestamp_in_ms|. This function compensates for wrap arounds in RTP @@ -71,10 +67,10 @@ bool RtpToNtpMs(int64_t rtp_timestamp, const synchronization::RtcpList& rtcp, int64_t* rtp_timestamp_in_ms) { assert(rtcp.size() == 2); - int64_t rtcp_ntp_ms_new = synchronization::NtpToMs(rtcp.front().ntp_secs, - rtcp.front().ntp_frac); - int64_t rtcp_ntp_ms_old = synchronization::NtpToMs(rtcp.back().ntp_secs, - rtcp.back().ntp_frac); + int64_t rtcp_ntp_ms_new = Clock::NtpToMs(rtcp.front().ntp_secs, + rtcp.front().ntp_frac); + int64_t rtcp_ntp_ms_old = Clock::NtpToMs(rtcp.back().ntp_secs, + rtcp.back().ntp_frac); int64_t rtcp_timestamp_new = rtcp.front().rtp_timestamp; int64_t rtcp_timestamp_old = rtcp.back().rtp_timestamp; if (!CompensateForWrapAround(rtcp_timestamp_new, diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc index ebe61295ed..3b9659a599 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -519,8 +519,8 @@ RTCPReceiver::HandleReportBlock(const RTCPUtility::RTCPPacket& rtcpPacket, _clock->CurrentNtp(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac); // time when we received this in MS - uint32_t receiveTimeMS = ModuleRTPUtility::ConvertNTPTimeToMS( - lastReceivedRRNTPsecs, lastReceivedRRNTPfrac); + uint32_t receiveTimeMS = Clock::NtpToMs(lastReceivedRRNTPsecs, + lastReceivedRRNTPfrac); // Estimate RTT uint32_t d = (delaySinceLastSendReport & 0x0000ffff) * 1000; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc index afaab90511..67e34def32 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc @@ -626,7 +626,7 @@ RTCPSender::BuildSR(uint8_t* rtcpbuffer, _lastRTCPTime[i+1] =_lastRTCPTime[i]; } - _lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac); + _lastRTCPTime[0] = Clock::NtpToMs(NTPsec, NTPfrac); _lastSendReport[0] = (NTPsec << 16) + (NTPfrac >> 16); uint32_t freqHz = 90000; // For video @@ -2042,7 +2042,7 @@ int32_t RTCPSender::AddReportBlocks(uint8_t* rtcpbuffer, } if (received) { // answer to the one that sends to me - _lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac); + _lastRTCPTime[0] = Clock::NtpToMs(NTPsec, NTPfrac); // Remote SSRC ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.h b/webrtc/modules/rtp_rtcp/source/rtp_utility.h index 7614ca66b7..6c6e9b85a2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.h @@ -70,11 +70,6 @@ namespace ModuleRTPUtility uint32_t NTPfrac, uint32_t freq); - // Return the time in milliseconds corresponding to the specified - // NTP timestamp. - uint32_t ConvertNTPTimeToMS(uint32_t NTPsec, - uint32_t NTPfrac); - uint32_t pow2(uint8_t exp); // Returns a pointer to the payload data given a packet. diff --git a/webrtc/modules/video_capture/include/video_capture_defines.h b/webrtc/modules/video_capture/include/video_capture_defines.h index bb20082e2c..223db70554 100644 --- a/webrtc/modules/video_capture/include/video_capture_defines.h +++ b/webrtc/modules/video_capture/include/video_capture_defines.h @@ -117,6 +117,7 @@ struct VideoFrameI420 class VideoCaptureExternal { public: + // |capture_time| must be specified in the NTP time format in milliseconds. virtual int32_t IncomingFrame(uint8_t* videoFrame, int32_t videoFrameLength, const VideoCaptureCapability& frameInfo, diff --git a/webrtc/modules/video_capture/video_capture_impl.cc b/webrtc/modules/video_capture/video_capture_impl.cc index 9b6ae0eeb4..fb0be7997e 100644 --- a/webrtc/modules/video_capture/video_capture_impl.cc +++ b/webrtc/modules/video_capture/video_capture_impl.cc @@ -18,6 +18,7 @@ #include "trace.h" #include "trace_event.h" #include "video_capture_config.h" +#include "webrtc/system_wrappers/interface/clock.h" #include @@ -193,11 +194,15 @@ int32_t VideoCaptureImpl::DeliverCapturedFrame(I420VideoFrame& captureFrame, } // Set the capture time + int64_t internal_capture_time = TickTime::MillisecondTimestamp(); if (capture_time != 0) { - captureFrame.set_render_time_ms(capture_time); + int64_t time_since_capture = + Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() - capture_time; + internal_capture_time -= time_since_capture; + captureFrame.set_render_time_ms(internal_capture_time); } else { - captureFrame.set_render_time_ms(TickTime::MillisecondTimestamp()); + captureFrame.set_render_time_ms(internal_capture_time); } TRACE_EVENT1("webrtc", "VC::DeliverCapturedFrame", diff --git a/webrtc/modules/video_capture/video_capture_impl.h b/webrtc/modules/video_capture/video_capture_impl.h index 905c37ea5d..76119b62b8 100644 --- a/webrtc/modules/video_capture/video_capture_impl.h +++ b/webrtc/modules/video_capture/video_capture_impl.h @@ -74,6 +74,7 @@ public: virtual int32_t Process(); // Implement VideoCaptureExternal + // |capture_time| must be specified in the NTP time format in milliseconds. virtual int32_t IncomingFrame(uint8_t* videoFrame, int32_t videoFrameLength, const VideoCaptureCapability& frameInfo, diff --git a/webrtc/system_wrappers/interface/clock.h b/webrtc/system_wrappers/interface/clock.h index 7a946d90c4..177e18fe8d 100644 --- a/webrtc/system_wrappers/interface/clock.h +++ b/webrtc/system_wrappers/interface/clock.h @@ -34,9 +34,15 @@ class Clock { // source is fixed for this clock. virtual int64_t TimeInMicroseconds() = 0; - // Retrieve an NTP absolute timestamp. + // Retrieve an NTP absolute timestamp in seconds and fractions of a second. virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) = 0; + // Retrieve an NTP absolute timestamp in milliseconds. + virtual int64_t CurrentNtpInMilliseconds() = 0; + + // Converts an NTP timestamp to a millisecond timestamp. + static int64_t NtpToMs(uint32_t seconds, uint32_t fractions); + // Returns an instance of the real-time system clock implementation. static Clock* GetRealTimeClock(); }; @@ -55,9 +61,12 @@ class SimulatedClock : public Clock { // source is fixed for this clock. virtual int64_t TimeInMicroseconds(); - // Retrieve an NTP absolute timestamp. + // Retrieve an NTP absolute timestamp in milliseconds. virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions); + // Converts an NTP timestamp to a millisecond timestamp. + virtual int64_t CurrentNtpInMilliseconds(); + // Advance the simulated clock with a given number of milliseconds or // microseconds. void AdvanceTimeMilliseconds(int64_t milliseconds); diff --git a/webrtc/system_wrappers/source/clock.cc b/webrtc/system_wrappers/source/clock.cc index 8085e56d42..9b017b8213 100644 --- a/webrtc/system_wrappers/source/clock.cc +++ b/webrtc/system_wrappers/source/clock.cc @@ -23,6 +23,14 @@ namespace webrtc { +const double kNtpFracPerMs = 4.294967296E6; + +int64_t Clock::NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) { + const double ntp_frac_ms = static_cast(ntp_frac) / kNtpFracPerMs; + return 1000 * static_cast(ntp_secs) + + static_cast(ntp_frac_ms + 0.5); +} + #if defined(_WIN32) struct reference_point { @@ -128,6 +136,42 @@ class RealTimeClock : public Clock { virtual int64_t TimeInMicroseconds() { return TickTime::MicrosecondTimestamp(); } + + // Retrieve an NTP absolute timestamp in seconds and fractions of a second. + virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) { + timeval tv = CurrentTimeVal(); + double microseconds_in_seconds; + Adjust(tv, &seconds, µseconds_in_seconds); + fractions = static_cast( + microseconds_in_seconds * kMagicNtpFractionalUnit + 0.5); + } + + // Retrieve an NTP absolute timestamp in milliseconds. + virtual int64_t CurrentNtpInMilliseconds() { + timeval tv = CurrentTimeVal(); + uint32_t seconds; + double microseconds_in_seconds; + Adjust(tv, &seconds, µseconds_in_seconds); + return 1000 * static_cast(seconds) + + static_cast(1000.0 * microseconds_in_seconds + 0.5); + } + + protected: + virtual timeval CurrentTimeVal() const = 0; + + static void Adjust(const timeval& tv, uint32_t* adjusted_s, + double* adjusted_us_in_s) { + *adjusted_s = tv.tv_sec + kNtpJan1970; + *adjusted_us_in_s = tv.tv_usec / 1e6; + + if (*adjusted_us_in_s >= 1) { + *adjusted_us_in_s -= 1; + ++*adjusted_s; + } else if (*adjusted_us_in_s < -1) { + *adjusted_us_in_s += 1; + --*adjusted_s; + } + } }; #if defined(_WIN32) @@ -138,8 +182,8 @@ class WindowsRealTimeClock : public RealTimeClock { virtual ~WindowsRealTimeClock() {} - // Retrieve an NTP absolute timestamp. - virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) { + protected: + timeval CurrentTimeVal() const { const uint64_t FILETIME_1970 = 0x019db1ded53e8000; FILETIME StartTime; @@ -147,35 +191,20 @@ class WindowsRealTimeClock : public RealTimeClock { struct timeval tv; // We can't use query performance counter since they can change depending on - // speed steping + // speed stepping. get_time(_helpTimer, StartTime); Time = (((uint64_t) StartTime.dwHighDateTime) << 32) + (uint64_t) StartTime.dwLowDateTime; - // Convert the hecto-nano second time to tv format + // Convert the hecto-nano second time to tv format. Time -= FILETIME_1970; tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000); tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10); - - double dtemp; - - seconds = tv.tv_sec + kNtpJan1970; - dtemp = tv.tv_usec / 1e6; - - if (dtemp >= 1) { - dtemp -= 1; - seconds++; - } else if (dtemp < -1) { - dtemp += 1; - seconds--; - } - dtemp *= kMagicNtpFractionalUnit; - fractions = (uint32_t)dtemp; + return tv; } - private: WindowsHelpTimer* _helpTimer; }; @@ -186,26 +215,14 @@ class UnixRealTimeClock : public RealTimeClock { virtual ~UnixRealTimeClock() {} - // Retrieve an NTP absolute timestamp. - virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) { - double dtemp; + protected: + timeval CurrentTimeVal() const { struct timeval tv; struct timezone tz; - tz.tz_minuteswest = 0; + tz.tz_minuteswest = 0; tz.tz_dsttime = 0; gettimeofday(&tv, &tz); - - seconds = tv.tv_sec + kNtpJan1970; - dtemp = tv.tv_usec / 1e6; - if (dtemp >= 1) { - dtemp -= 1; - seconds++; - } else if (dtemp < -1) { - dtemp += 1; - seconds--; - } - dtemp *= kMagicNtpFractionalUnit; - fractions = (uint32_t)dtemp; + return tv; } }; #endif @@ -247,6 +264,10 @@ void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) { kMagicNtpFractionalUnit / 1000); } +int64_t SimulatedClock::CurrentNtpInMilliseconds() { + return TimeInMilliseconds() + 1000 * static_cast(kNtpJan1970); +} + void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) { AdvanceTimeMicroseconds(1000 * milliseconds); } diff --git a/webrtc/system_wrappers/source/clock_unittest.cc b/webrtc/system_wrappers/source/clock_unittest.cc new file mode 100644 index 0000000000..67d699e564 --- /dev/null +++ b/webrtc/system_wrappers/source/clock_unittest.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/system_wrappers/interface/clock.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { + +TEST(ClockTest, NtpTime) { + Clock* clock = Clock::GetRealTimeClock(); + uint32_t seconds; + uint32_t fractions; + clock->CurrentNtp(seconds, fractions); + int64_t milliseconds = clock->CurrentNtpInMilliseconds(); + EXPECT_GE(milliseconds, Clock::NtpToMs(seconds, fractions)); + EXPECT_NEAR(milliseconds, Clock::NtpToMs(seconds, fractions), 5); +} +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/system_wrappers_tests.gyp b/webrtc/system_wrappers/source/system_wrappers_tests.gyp index fd1db25401..f036731aeb 100644 --- a/webrtc/system_wrappers/source/system_wrappers_tests.gyp +++ b/webrtc/system_wrappers/source/system_wrappers_tests.gyp @@ -19,6 +19,7 @@ ], 'sources': [ 'aligned_malloc_unittest.cc', + 'clock_unittest.cc', 'condition_variable_unittest.cc', 'critical_section_unittest.cc', 'event_tracer_unittest.cc', diff --git a/webrtc/video_engine/include/vie_capture.h b/webrtc/video_engine/include/vie_capture.h index 45d62e5174..72a959fbf2 100644 --- a/webrtc/video_engine/include/vie_capture.h +++ b/webrtc/video_engine/include/vie_capture.h @@ -103,6 +103,7 @@ class WEBRTC_DLLEXPORT ViEExternalCapture { // This method is called by the user to deliver a new captured frame to // VideoEngine. + // |capture_time| must be specified in the NTP time format in milliseconds. virtual int IncomingFrame(unsigned char* video_frame, unsigned int video_frame_length, unsigned short width, @@ -112,6 +113,7 @@ class WEBRTC_DLLEXPORT ViEExternalCapture { // This method is specifically for delivering a new captured I420 frame to // VideoEngine. + // |capture_time| must be specified in the NTP time format in milliseconds. virtual int IncomingFrameI420( const ViEVideoFrameI420& video_frame, unsigned long long capture_time = 0) = 0;