summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Chromium Automerger <chromium-automerger@android>2014-09-25 12:15:39 +0000
committerAndroid Chromium Automerger <chromium-automerger@android>2014-09-25 12:15:39 +0000
commitdb1e40e5746058ca5a1cffbb68150b3e8b3e4339 (patch)
tree8c54d3753557a2aaa4573d9808ebde41c109243d
parent8af00ea2e38cd722cdc20c41427cdab7e165591e (diff)
parent53545bbfc47f2cddb7038395369a0dcd457c8b34 (diff)
downloadwebrtc-db1e40e5746058ca5a1cffbb68150b3e8b3e4339.tar.gz
Merge third_party/webrtc from https://chromium.googlesource.com/external/webrtc/trunk/webrtc.git at 53545bbfc47f2cddb7038395369a0dcd457c8b34
This commit was generated by merge_from_chromium.py. Change-Id: I9a04351d8f1bfde313d4565bd7f276b9684920d7
-rw-r--r--base/thread.cc33
-rw-r--r--base/thread.h19
-rw-r--r--base/thread_unittest.cc72
-rw-r--r--modules/audio_coding/main/test/RTPFile.cc14
-rw-r--r--modules/audio_processing/audio_buffer.cc31
-rw-r--r--modules/audio_processing/audio_processing_impl.cc9
-rw-r--r--modules/audio_processing/test/audio_processing_unittest.cc41
-rw-r--r--modules/desktop_capture/screen_capturer_mac.mm1
-rw-r--r--modules/modules.gyp1
-rw-r--r--modules/rtp_rtcp/source/rtp_format_h264.h2
-rw-r--r--modules/video_coding/main/source/jitter_buffer.cc5
-rw-r--r--modules/video_coding/main/source/jitter_estimator.cc161
-rw-r--r--modules/video_coding/main/source/jitter_estimator.h19
-rw-r--r--modules/video_coding/main/source/jitter_estimator_tests.cc160
14 files changed, 465 insertions, 103 deletions
diff --git a/base/thread.cc b/base/thread.cc
index 9d2917d9..40257ab8 100644
--- a/base/thread.cc
+++ b/base/thread.cc
@@ -411,15 +411,12 @@ void Thread::Stop() {
}
void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
- AssertBlockingIsAllowedOnCurrentThread();
-
if (fStop_)
return;
// Sent messages are sent to the MessageHandler directly, in the context
// of "thread", like Win32 SendMessage. If in the right context,
// call the handler directly.
-
Message msg;
msg.phandler = phandler;
msg.message_id = id;
@@ -429,6 +426,8 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
return;
}
+ AssertBlockingIsAllowedOnCurrentThread();
+
AutoThread thread;
Thread *current_thread = Thread::Current();
ASSERT(current_thread != NULL); // AutoThread ensures this
@@ -451,7 +450,9 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
crit_.Enter();
while (!ready) {
crit_.Leave();
- current_thread->ReceiveSends();
+ // We need to limit "ReceiveSends" to |this| thread to avoid an arbitrary
+ // thread invoking calls on the current thread.
+ current_thread->ReceiveSendsFromThread(this);
current_thread->socketserver()->Wait(kForever, false);
waited = true;
crit_.Enter();
@@ -475,17 +476,23 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
}
void Thread::ReceiveSends() {
+ ReceiveSendsFromThread(NULL);
+}
+
+void Thread::ReceiveSendsFromThread(const Thread* source) {
// Receive a sent message. Cleanup scenarios:
// - thread sending exits: We don't allow this, since thread can exit
// only via Join, so Send must complete.
// - thread receiving exits: Wakeup/set ready in Thread::Clear()
// - object target cleared: Wakeup/set ready in Thread::Clear()
+ _SendMessage smsg;
+
crit_.Enter();
- while (!sendlist_.empty()) {
- _SendMessage smsg = sendlist_.front();
- sendlist_.pop_front();
+ while (PopSendMessageFromThread(source, &smsg)) {
crit_.Leave();
+
smsg.msg.phandler->OnMessage(&smsg.msg);
+
crit_.Enter();
*smsg.ready = true;
smsg.thread->socketserver()->WakeUp();
@@ -493,6 +500,18 @@ void Thread::ReceiveSends() {
crit_.Leave();
}
+bool Thread::PopSendMessageFromThread(const Thread* source, _SendMessage* msg) {
+ for (std::list<_SendMessage>::iterator it = sendlist_.begin();
+ it != sendlist_.end(); ++it) {
+ if (it->thread == source || source == NULL) {
+ *msg = *it;
+ sendlist_.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
void Thread::Clear(MessageHandler *phandler, uint32 id,
MessageList* removed) {
CritScope cs(&crit_);
diff --git a/base/thread.h b/base/thread.h
index 25b0f569..34ec45e3 100644
--- a/base/thread.h
+++ b/base/thread.h
@@ -165,7 +165,6 @@ class Thread : public MessageQueue {
// See ScopedDisallowBlockingCalls for details.
template <class ReturnT, class FunctorT>
ReturnT Invoke(const FunctorT& functor) {
- AssertBlockingIsAllowedOnCurrentThread();
FunctorMessageHandler<ReturnT, FunctorT> handler(functor);
Send(&handler);
return handler.result();
@@ -210,6 +209,10 @@ class Thread : public MessageQueue {
// of whatever code is conditionally executing because of the return value!
bool RunningForTest() { return running(); }
+ // Sets the per-thread allow-blocking-calls flag and returns the previous
+ // value.
+ bool SetAllowBlockingCalls(bool allow);
+
protected:
// This method should be called when thread is created using non standard
// method, like derived implementation of rtc::Thread and it can not be
@@ -226,10 +229,6 @@ class Thread : public MessageQueue {
// Blocks the calling thread until this thread has terminated.
void Join();
- // Sets the per-thread allow-blocking-calls flag and returns the previous
- // value.
- bool SetAllowBlockingCalls(bool allow);
-
static void AssertBlockingIsAllowedOnCurrentThread();
friend class ScopedDisallowBlockingCalls;
@@ -248,6 +247,16 @@ class Thread : public MessageQueue {
// Return true if the thread was started and hasn't yet stopped.
bool running() { return running_.Wait(0); }
+ // Processes received "Send" requests. If |source| is not NULL, only requests
+ // from |source| are processed, otherwise, all requests are processed.
+ void ReceiveSendsFromThread(const Thread* source);
+
+ // If |source| is not NULL, pops the first "Send" message from |source| in
+ // |sendlist_|, otherwise, pops the first "Send" message of |sendlist_|.
+ // The caller must lock |crit_| before calling.
+ // Returns true if there is such a message.
+ bool PopSendMessageFromThread(const Thread* source, _SendMessage* msg);
+
std::list<_SendMessage> sendlist_;
std::string name_;
ThreadPriority priority_;
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
index 4229df28..57b6df66 100644
--- a/base/thread_unittest.cc
+++ b/base/thread_unittest.cc
@@ -276,6 +276,78 @@ TEST(ThreadTest, DISABLED_ON_MAC(Invoke)) {
thread.Invoke<void>(&LocalFuncs::Func2);
}
+// Verifies that two threads calling Invoke on each other at the same time does
+// not deadlock.
+TEST(ThreadTest, TwoThreadsInvokeNoDeadlock) {
+ AutoThread thread;
+ Thread* current_thread = Thread::Current();
+ ASSERT_TRUE(current_thread != NULL);
+
+ Thread other_thread;
+ other_thread.Start();
+
+ struct LocalFuncs {
+ static void Set(bool* out) { *out = true; }
+ static void InvokeSet(Thread* thread, bool* out) {
+ thread->Invoke<void>(Bind(&Set, out));
+ }
+ };
+
+ bool called = false;
+ other_thread.Invoke<void>(
+ Bind(&LocalFuncs::InvokeSet, current_thread, &called));
+
+ EXPECT_TRUE(called);
+}
+
+// Verifies that if thread A invokes a call on thread B and thread C is trying
+// to invoke A at the same time, thread A does not handle C's invoke while
+// invoking B.
+TEST(ThreadTest, ThreeThreadsInvoke) {
+ AutoThread thread;
+ Thread* thread_a = Thread::Current();
+ Thread thread_b, thread_c;
+ thread_b.Start();
+ thread_c.Start();
+
+ struct LocalFuncs {
+ static void Set(bool* out) { *out = true; }
+ static void InvokeSet(Thread* thread, bool* out) {
+ thread->Invoke<void>(Bind(&Set, out));
+ }
+
+ // Set |out| true and call InvokeSet on |thread|.
+ static void SetAndInvokeSet(bool* out, Thread* thread, bool* out_inner) {
+ *out = true;
+ InvokeSet(thread, out_inner);
+ }
+
+ // Asynchronously invoke SetAndInvokeSet on |thread1| and wait until
+ // |thread1| starts the call.
+ static void AsyncInvokeSetAndWait(
+ Thread* thread1, Thread* thread2, bool* out) {
+ bool async_invoked = false;
+
+ AsyncInvoker invoker;
+ invoker.AsyncInvoke<void>(
+ thread1, Bind(&SetAndInvokeSet, &async_invoked, thread2, out));
+
+ EXPECT_TRUE_WAIT(async_invoked, 2000);
+ }
+ };
+
+ bool thread_a_called = false;
+
+ // Start the sequence A --(invoke)--> B --(async invoke)--> C --(invoke)--> A.
+ // Thread B returns when C receives the call and C should be blocked until A
+ // starts to process messages.
+ thread_b.Invoke<void>(Bind(&LocalFuncs::AsyncInvokeSetAndWait,
+ &thread_c, thread_a, &thread_a_called));
+ EXPECT_FALSE(thread_a_called);
+
+ EXPECT_TRUE_WAIT(thread_a_called, 2000);
+}
+
class AsyncInvokeTest : public testing::Test {
public:
void IntCallback(int value) {
diff --git a/modules/audio_coding/main/test/RTPFile.cc b/modules/audio_coding/main/test/RTPFile.cc
index 4cef5924..b7f587b8 100644
--- a/modules/audio_coding/main/test/RTPFile.cc
+++ b/modules/audio_coding/main/test/RTPFile.cc
@@ -109,7 +109,7 @@ uint16_t RTPBuffer::Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData,
if (packet->payloadSize > 0 && payloadSize >= packet->payloadSize) {
memcpy(payloadData, packet->payloadData, packet->payloadSize);
} else {
- return 0u;
+ return 0;
}
*offset = (packet->timeStamp / (packet->frequency / 1000));
@@ -216,7 +216,7 @@ uint16_t RTPFile::Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData,
/* Check if we have reached end of file. */
if ((read_len == 0) && feof(_rtpFile)) {
_rtpEOF = true;
- return 0u;
+ return 0;
}
EXPECT_EQ(1u, fread(&plen, 2, 1, _rtpFile));
EXPECT_EQ(1u, fread(offset, 4, 1, _rtpFile));
@@ -232,13 +232,13 @@ uint16_t RTPFile::Read(WebRtcRTPHeader* rtpInfo, uint8_t* payloadData,
EXPECT_EQ(lengthBytes, plen + 8);
if (plen == 0) {
- return 0u;
- }
- if (payloadSize < (lengthBytes - 20)) {
- return 0u;
+ return 0;
}
if (lengthBytes < 20) {
- return 0u;
+ return 0;
+ }
+ if (payloadSize < (lengthBytes - 20)) {
+ return 0;
}
lengthBytes -= 20;
EXPECT_EQ(lengthBytes, fread(payloadData, 1, lengthBytes, _rtpFile));
diff --git a/modules/audio_processing/audio_buffer.cc b/modules/audio_processing/audio_buffer.cc
index fb2c200e..2bbd7710 100644
--- a/modules/audio_processing/audio_buffer.cc
+++ b/modules/audio_processing/audio_buffer.cc
@@ -406,19 +406,32 @@ int AudioBuffer::samples_per_keyboard_channel() const {
// TODO(andrew): Do deinterleaving and mixing in one step?
void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
assert(proc_samples_per_channel_ == input_samples_per_channel_);
- assert(num_proc_channels_ == num_input_channels_);
- assert(frame->num_channels_ == num_proc_channels_);
+ assert(frame->num_channels_ == num_input_channels_);
assert(frame->samples_per_channel_ == proc_samples_per_channel_);
InitForNewData();
activity_ = frame->vad_activity_;
- int16_t* interleaved = frame->data_;
- for (int i = 0; i < num_proc_channels_; i++) {
- int16_t* deinterleaved = channels_->ibuf()->channel(i);
- int interleaved_idx = i;
- for (int j = 0; j < proc_samples_per_channel_; j++) {
- deinterleaved[j] = interleaved[interleaved_idx];
- interleaved_idx += num_proc_channels_;
+ if (num_input_channels_ == 2 && num_proc_channels_ == 1) {
+ // Downmix directly; no explicit deinterleaving needed.
+ int16_t* downmixed = channels_->ibuf()->channel(0);
+ for (int i = 0; i < input_samples_per_channel_; ++i) {
+ // HACK(ajm): The downmixing in the int16_t path is in practice never
+ // called from production code. We do this weird scaling to and from float
+ // to satisfy tests checking for bit-exactness with the float path.
+ float downmix_float = (ScaleToFloat(frame->data_[i * 2]) +
+ ScaleToFloat(frame->data_[i * 2 + 1])) / 2;
+ downmixed[i] = ScaleAndRoundToInt16(downmix_float);
+ }
+ } else {
+ assert(num_proc_channels_ == num_input_channels_);
+ int16_t* interleaved = frame->data_;
+ for (int i = 0; i < num_proc_channels_; ++i) {
+ int16_t* deinterleaved = channels_->ibuf()->channel(i);
+ int interleaved_idx = i;
+ for (int j = 0; j < proc_samples_per_channel_; ++j) {
+ deinterleaved[j] = interleaved[interleaved_idx];
+ interleaved_idx += num_proc_channels_;
+ }
}
}
}
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 659b794a..d91cbd2f 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -257,10 +257,9 @@ int AudioProcessingImpl::InitializeLocked(int input_sample_rate_hz,
}
}
- // TODO(ajm): Enable this.
- // Always downmix the reverse stream to mono for analysis.
- //rev_proc_format_.set(rev_proc_rate, 1);
- rev_proc_format_.set(rev_proc_rate, rev_in_format_.num_channels());
+ // Always downmix the reverse stream to mono for analysis. This has been
+ // demonstrated to work well for AEC in most practical scenarios.
+ rev_proc_format_.set(rev_proc_rate, 1);
if (fwd_proc_format_.rate() == kSampleRate32kHz) {
split_rate_ = kSampleRate16kHz;
@@ -482,12 +481,12 @@ int AudioProcessingImpl::ProcessStreamLocked() {
RETURN_ON_ERR(high_pass_filter_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(gain_control_->AnalyzeCaptureAudio(ca));
+ RETURN_ON_ERR(noise_suppression_->AnalyzeCaptureAudio(ca));
RETURN_ON_ERR(echo_cancellation_->ProcessCaptureAudio(ca));
if (echo_control_mobile_->is_enabled() && noise_suppression_->is_enabled()) {
ca->CopyLowPassToReference();
}
- RETURN_ON_ERR(noise_suppression_->AnalyzeCaptureAudio(ca));
RETURN_ON_ERR(noise_suppression_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(echo_control_mobile_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(voice_detection_->ProcessCaptureAudio(ca));
diff --git a/modules/audio_processing/test/audio_processing_unittest.cc b/modules/audio_processing/test/audio_processing_unittest.cc
index af63bde3..a0fb303b 100644
--- a/modules/audio_processing/test/audio_processing_unittest.cc
+++ b/modules/audio_processing/test/audio_processing_unittest.cc
@@ -751,7 +751,8 @@ TEST_F(ApmTest, Channels) {
for (int i = 1; i < 3; i++) {
TestChangingChannels(i, kNoErr);
EXPECT_EQ(i, apm_->num_input_channels());
- EXPECT_EQ(i, apm_->num_reverse_channels());
+ // We always force the number of reverse channels used for processing to 1.
+ EXPECT_EQ(1, apm_->num_reverse_channels());
}
}
@@ -1671,7 +1672,7 @@ TEST_F(ApmTest, FloatAndIntInterfacesGiveIdenticalResults) {
const int num_output_channels = test->num_output_channels();
const int samples_per_channel = test->sample_rate() *
AudioProcessing::kChunkSizeMs / 1000;
- const int output_length = samples_per_channel * num_output_channels;
+ const int output_length = samples_per_channel * num_output_channels;
Init(test->sample_rate(), test->sample_rate(), test->sample_rate(),
num_input_channels, num_output_channels, num_render_channels, true);
@@ -2325,25 +2326,25 @@ TEST_P(AudioProcessingTest, Formats) {
#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
INSTANTIATE_TEST_CASE_P(
CommonFormats, AudioProcessingTest, testing::Values(
- std::tr1::make_tuple(48000, 48000, 48000, 25),
- std::tr1::make_tuple(48000, 48000, 32000, 25),
- std::tr1::make_tuple(48000, 48000, 16000, 25),
- std::tr1::make_tuple(48000, 44100, 48000, 20),
- std::tr1::make_tuple(48000, 44100, 32000, 20),
- std::tr1::make_tuple(48000, 44100, 16000, 20),
- std::tr1::make_tuple(48000, 32000, 48000, 25),
- std::tr1::make_tuple(48000, 32000, 32000, 25),
- std::tr1::make_tuple(48000, 32000, 16000, 25),
- std::tr1::make_tuple(48000, 16000, 48000, 25),
- std::tr1::make_tuple(48000, 16000, 32000, 25),
- std::tr1::make_tuple(48000, 16000, 16000, 25),
+ std::tr1::make_tuple(48000, 48000, 48000, 20),
+ std::tr1::make_tuple(48000, 48000, 32000, 20),
+ std::tr1::make_tuple(48000, 48000, 16000, 20),
+ std::tr1::make_tuple(48000, 44100, 48000, 15),
+ std::tr1::make_tuple(48000, 44100, 32000, 15),
+ std::tr1::make_tuple(48000, 44100, 16000, 15),
+ std::tr1::make_tuple(48000, 32000, 48000, 20),
+ std::tr1::make_tuple(48000, 32000, 32000, 20),
+ std::tr1::make_tuple(48000, 32000, 16000, 20),
+ std::tr1::make_tuple(48000, 16000, 48000, 20),
+ std::tr1::make_tuple(48000, 16000, 32000, 20),
+ std::tr1::make_tuple(48000, 16000, 16000, 20),
std::tr1::make_tuple(44100, 48000, 48000, 20),
std::tr1::make_tuple(44100, 48000, 32000, 20),
std::tr1::make_tuple(44100, 48000, 16000, 20),
- std::tr1::make_tuple(44100, 44100, 48000, 20),
- std::tr1::make_tuple(44100, 44100, 32000, 20),
- std::tr1::make_tuple(44100, 44100, 16000, 20),
+ std::tr1::make_tuple(44100, 44100, 48000, 15),
+ std::tr1::make_tuple(44100, 44100, 32000, 15),
+ std::tr1::make_tuple(44100, 44100, 16000, 15),
std::tr1::make_tuple(44100, 32000, 48000, 20),
std::tr1::make_tuple(44100, 32000, 32000, 20),
std::tr1::make_tuple(44100, 32000, 16000, 20),
@@ -2360,9 +2361,9 @@ INSTANTIATE_TEST_CASE_P(
std::tr1::make_tuple(32000, 32000, 48000, 30),
std::tr1::make_tuple(32000, 32000, 32000, 0),
std::tr1::make_tuple(32000, 32000, 16000, 30),
- std::tr1::make_tuple(32000, 16000, 48000, 25),
- std::tr1::make_tuple(32000, 16000, 32000, 25),
- std::tr1::make_tuple(32000, 16000, 16000, 25),
+ std::tr1::make_tuple(32000, 16000, 48000, 20),
+ std::tr1::make_tuple(32000, 16000, 32000, 20),
+ std::tr1::make_tuple(32000, 16000, 16000, 20),
std::tr1::make_tuple(16000, 48000, 48000, 25),
std::tr1::make_tuple(16000, 48000, 32000, 25),
diff --git a/modules/desktop_capture/screen_capturer_mac.mm b/modules/desktop_capture/screen_capturer_mac.mm
index be05bd99..effe7ebf 100644
--- a/modules/desktop_capture/screen_capturer_mac.mm
+++ b/modules/desktop_capture/screen_capturer_mac.mm
@@ -429,6 +429,7 @@ void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
// Lion requires us to use their new APIs for doing screen capture. These
// APIS currently crash on 10.6.8 if there is no monitor attached.
if (!CgBlitPostLion(*current_frame, region)) {
+ desktop_config_monitor_->Unlock();
callback_->OnCaptureCompleted(NULL);
return;
}
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 777523ab..9650e66d 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -237,6 +237,7 @@
'video_coding/main/interface/mock/mock_vcm_callbacks.h',
'video_coding/main/source/decoding_state_unittest.cc',
'video_coding/main/source/jitter_buffer_unittest.cc',
+ 'video_coding/main/source/jitter_estimator_tests.cc',
'video_coding/main/source/media_optimization_unittest.cc',
'video_coding/main/source/receiver_unittest.cc',
'video_coding/main/source/session_info_unittest.cc',
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h
index abd9321f..830d765c 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264.h
+++ b/modules/rtp_rtcp/source/rtp_format_h264.h
@@ -43,7 +43,7 @@ class RtpPacketizerH264 : public RtpPacketizer {
virtual ProtectionType GetProtectionType() OVERRIDE;
- virtual StorageType GetStorageType(uint32_t retrasmission_settings) OVERRIDE;
+ virtual StorageType GetStorageType(uint32_t retransmission_settings) OVERRIDE;
virtual std::string ToString() OVERRIDE;
diff --git a/modules/video_coding/main/source/jitter_buffer.cc b/modules/video_coding/main/source/jitter_buffer.cc
index 9aa34090..d09fccd9 100644
--- a/modules/video_coding/main/source/jitter_buffer.cc
+++ b/modules/video_coding/main/source/jitter_buffer.cc
@@ -122,8 +122,7 @@ void FrameList::Reset(UnorderedFrameList* free_frames) {
}
}
-VCMJitterBuffer::VCMJitterBuffer(Clock* clock,
- EventFactory* event_factory)
+VCMJitterBuffer::VCMJitterBuffer(Clock* clock, EventFactory* event_factory)
: clock_(clock),
running_(false),
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
@@ -145,7 +144,7 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock,
num_consecutive_old_frames_(0),
num_consecutive_old_packets_(0),
num_discarded_packets_(0),
- jitter_estimate_(),
+ jitter_estimate_(clock),
inter_frame_delay_(clock_->TimeInMilliseconds()),
rtt_ms_(kDefaultRtt),
nack_mode_(kNoNack),
diff --git a/modules/video_coding/main/source/jitter_estimator.cc b/modules/video_coding/main/source/jitter_estimator.cc
index 71c54a00..b36775a4 100644
--- a/modules/video_coding/main/source/jitter_estimator.cc
+++ b/modules/video_coding/main/source/jitter_estimator.cc
@@ -11,6 +11,8 @@
#include "webrtc/modules/video_coding/main/source/internal_defines.h"
#include "webrtc/modules/video_coding/main/source/jitter_estimator.h"
#include "webrtc/modules/video_coding/main/source/rtt_filter.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/field_trial.h"
#include <assert.h>
#include <math.h>
@@ -19,7 +21,13 @@
namespace webrtc {
-VCMJitterEstimator::VCMJitterEstimator(int32_t vcmId, int32_t receiverId)
+enum { kStartupDelaySamples = 30 };
+enum { kFsAccuStartupSamples = 5 };
+enum { kMaxFramerateEstimate = 200 };
+
+VCMJitterEstimator::VCMJitterEstimator(const Clock* clock,
+ int32_t vcmId,
+ int32_t receiverId)
: _vcmId(vcmId),
_receiverId(receiverId),
_phi(0.97),
@@ -32,8 +40,15 @@ VCMJitterEstimator::VCMJitterEstimator(int32_t vcmId, int32_t receiverId)
_noiseStdDevs(2.33), // ~Less than 1% chance
// (look up in normal distribution table)...
_noiseStdDevOffset(30.0), // ...of getting 30 ms freezes
- _rttFilter() {
- Reset();
+ _rttFilter(),
+ fps_counter_(30), // TODO(sprang): Use an estimator with limit based on
+ // time, rather than number of samples.
+ low_rate_experiment_(kInit),
+ clock_(clock) {
+ Reset();
+}
+
+VCMJitterEstimator::~VCMJitterEstimator() {
}
VCMJitterEstimator&
@@ -94,6 +109,7 @@ VCMJitterEstimator::Reset()
_fsCount = 0;
_startupCount = 0;
_rttFilter.Reset();
+ fps_counter_.Reset();
}
void
@@ -297,35 +313,54 @@ VCMJitterEstimator::DeviationFromExpectedDelay(int64_t frameDelayMS,
// Estimates the random jitter by calculating the variance of the
// sample distance from the line given by theta.
-void
-VCMJitterEstimator::EstimateRandomJitter(double d_dT, bool incompleteFrame)
-{
- double alpha;
- if (_alphaCount == 0)
- {
- assert(_alphaCount > 0);
- return;
- }
- alpha = static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
- _alphaCount++;
- if (_alphaCount > _alphaCountMax)
- {
- _alphaCount = _alphaCountMax;
- }
- double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
- double varNoise = alpha * _varNoise +
- (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
- if (!incompleteFrame || varNoise > _varNoise)
- {
- _avgNoise = avgNoise;
- _varNoise = varNoise;
- }
- if (_varNoise < 1.0)
- {
- // The variance should never be zero, since we might get
- // stuck and consider all samples as outliers.
- _varNoise = 1.0;
+void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
+ bool incompleteFrame) {
+ uint64_t now = clock_->TimeInMicroseconds();
+ if (_lastUpdateT != -1) {
+ fps_counter_.AddSample(now - _lastUpdateT);
+ }
+ _lastUpdateT = now;
+
+ if (_alphaCount == 0) {
+ assert(false);
+ return;
+ }
+ double alpha =
+ static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
+ _alphaCount++;
+ if (_alphaCount > _alphaCountMax)
+ _alphaCount = _alphaCountMax;
+
+ if (LowRateExperimentEnabled()) {
+ // In order to avoid a low frame rate stream to react slower to changes,
+ // scale the alpha weight relative a 30 fps stream.
+ double fps = GetFrameRate();
+ if (fps > 0.0) {
+ double rate_scale = 30.0 / fps;
+ // At startup, there can be a lot of noise in the fps estimate.
+ // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
+ // at sample #kStartupDelaySamples.
+ if (_alphaCount < kStartupDelaySamples) {
+ rate_scale =
+ (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) /
+ kStartupDelaySamples;
+ }
+ alpha = pow(alpha, rate_scale);
}
+ }
+
+ double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
+ double varNoise =
+ alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
+ if (!incompleteFrame || varNoise > _varNoise) {
+ _avgNoise = avgNoise;
+ _varNoise = varNoise;
+ }
+ if (_varNoise < 1.0) {
+ // The variance should never be zero, since we might get
+ // stuck and consider all samples as outliers.
+ _varNoise = 1.0;
+ }
}
double
@@ -387,19 +422,61 @@ VCMJitterEstimator::UpdateMaxFrameSize(uint32_t frameSizeBytes)
// Returns the current filtered estimate if available,
// otherwise tries to calculate an estimate.
-int
-VCMJitterEstimator::GetJitterEstimate(double rttMultiplier)
-{
- double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
- if (_filterJitterEstimate > jitterMS)
- {
- jitterMS = _filterJitterEstimate;
+int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) {
+ double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
+ if (_filterJitterEstimate > jitterMS)
+ jitterMS = _filterJitterEstimate;
+ if (_nackCount >= _nackLimit)
+ jitterMS += _rttFilter.RttMs() * rttMultiplier;
+
+ if (LowRateExperimentEnabled()) {
+ static const double kJitterScaleLowThreshold = 5.0;
+ static const double kJitterScaleHighThreshold = 10.0;
+ double fps = GetFrameRate();
+ // Ignore jitter for very low fps streams.
+ if (fps < kJitterScaleLowThreshold) {
+ if (fps == 0.0) {
+ return jitterMS;
+ }
+ return 0;
}
- if (_nackCount >= _nackLimit)
- {
- jitterMS += _rttFilter.RttMs() * rttMultiplier;
+
+ // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
+ // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
+ if (fps < kJitterScaleHighThreshold) {
+ jitterMS =
+ (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
+ (fps - kJitterScaleLowThreshold) * jitterMS;
}
- return static_cast<uint32_t>(jitterMS + 0.5);
+ }
+
+ return static_cast<uint32_t>(jitterMS + 0.5);
+}
+
+bool VCMJitterEstimator::LowRateExperimentEnabled() {
+ if (low_rate_experiment_ == kInit) {
+ std::string group =
+ webrtc::field_trial::FindFullName("WebRTC-ReducedJitterDelay");
+ if (group == "Disabled") {
+ low_rate_experiment_ = kDisabled;
+ } else {
+ low_rate_experiment_ = kEnabled;
+ }
+ }
+ return low_rate_experiment_ == kEnabled ? true : false;
+}
+
+double VCMJitterEstimator::GetFrameRate() const {
+ if (fps_counter_.count() == 0)
+ return 0;
+
+ double fps = 1000000.0 / fps_counter_.ComputeMean();
+ // Sanity check.
+ assert(fps >= 0.0);
+ if (fps > kMaxFramerateEstimate) {
+ fps = kMaxFramerateEstimate;
+ }
+ return fps;
}
}
diff --git a/modules/video_coding/main/source/jitter_estimator.h b/modules/video_coding/main/source/jitter_estimator.h
index dda8f8da..ec7e35ce 100644
--- a/modules/video_coding/main/source/jitter_estimator.h
+++ b/modules/video_coding/main/source/jitter_estimator.h
@@ -11,17 +11,22 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_
#define WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_
+#include "webrtc/base/rollingaccumulator.h"
#include "webrtc/modules/video_coding/main/source/rtt_filter.h"
#include "webrtc/typedefs.h"
namespace webrtc
{
+class Clock;
+
class VCMJitterEstimator
{
public:
- VCMJitterEstimator(int32_t vcmId = 0, int32_t receiverId = 0);
-
+ VCMJitterEstimator(const Clock* clock,
+ int32_t vcmId = 0,
+ int32_t receiverId = 0);
+ virtual ~VCMJitterEstimator();
VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs);
// Resets the estimate to the initial state
@@ -68,6 +73,8 @@ protected:
double _theta[2]; // Estimated line parameters (slope, offset)
double _varNoise; // Variance of the time-deviation from the line
+ virtual bool LowRateExperimentEnabled();
+
private:
// Updates the Kalman filter for the line describing
// the frame size dependent jitter.
@@ -109,6 +116,8 @@ private:
double DeviationFromExpectedDelay(int64_t frameDelayMS,
int32_t deltaFSBytes) const;
+ double GetFrameRate() const;
+
// Constants, filter parameters
int32_t _vcmId;
int32_t _receiverId;
@@ -145,8 +154,10 @@ private:
// but never goes above _nackLimit
VCMRttFilter _rttFilter;
- enum { kStartupDelaySamples = 30 };
- enum { kFsAccuStartupSamples = 5 };
+ rtc::RollingAccumulator<uint64_t> fps_counter_;
+ enum ExperimentFlag { kInit, kEnabled, kDisabled };
+ ExperimentFlag low_rate_experiment_;
+ const Clock* clock_;
};
} // namespace webrtc
diff --git a/modules/video_coding/main/source/jitter_estimator_tests.cc b/modules/video_coding/main/source/jitter_estimator_tests.cc
new file mode 100644
index 00000000..5f347505
--- /dev/null
+++ b/modules/video_coding/main/source/jitter_estimator_tests.cc
@@ -0,0 +1,160 @@
+/* Copyright (c) 2014 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/modules/video_coding/main/source/jitter_estimator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+
+namespace webrtc {
+
+class TestEstimator : public VCMJitterEstimator {
+ public:
+ explicit TestEstimator(bool exp_enabled)
+ : VCMJitterEstimator(&fake_clock_, 0, 0),
+ fake_clock_(0),
+ exp_enabled_(exp_enabled) {}
+
+ virtual bool LowRateExperimentEnabled() { return exp_enabled_; }
+
+ void AdvanceClock(int64_t microseconds) {
+ fake_clock_.AdvanceTimeMicroseconds(microseconds);
+ }
+
+ private:
+ SimulatedClock fake_clock_;
+ const bool exp_enabled_;
+};
+
+class TestVCMJitterEstimator : public ::testing::Test {
+ protected:
+ TestVCMJitterEstimator()
+ : regular_estimator_(false), low_rate_estimator_(true) {}
+
+ virtual void SetUp() { regular_estimator_.Reset(); }
+
+ TestEstimator regular_estimator_;
+ TestEstimator low_rate_estimator_;
+};
+
+// Generates some simple test data in the form of a sawtooth wave.
+class ValueGenerator {
+ public:
+ ValueGenerator(int32_t amplitude) : amplitude_(amplitude), counter_(0) {}
+ virtual ~ValueGenerator() {}
+
+ int64_t Delay() { return ((counter_ % 11) - 5) * amplitude_; }
+
+ uint32_t FrameSize() { return 1000 + Delay(); }
+
+ void Advance() { ++counter_; }
+
+ private:
+ const int32_t amplitude_;
+ int64_t counter_;
+};
+
+// 5 fps, disable jitter delay altogether.
+TEST_F(TestVCMJitterEstimator, TestLowRate) {
+ ValueGenerator gen(10);
+ uint64_t time_delta = 1000000 / 5;
+ for (int i = 0; i < 60; ++i) {
+ regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ regular_estimator_.AdvanceClock(time_delta);
+ low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ low_rate_estimator_.AdvanceClock(time_delta);
+ EXPECT_GT(regular_estimator_.GetJitterEstimate(0), 0);
+ if (i > 2)
+ EXPECT_EQ(low_rate_estimator_.GetJitterEstimate(0), 0);
+ gen.Advance();
+ }
+}
+
+// 8 fps, steady state estimate should be in interpolated interval between 0
+// and value of previous method.
+TEST_F(TestVCMJitterEstimator, TestMidRate) {
+ ValueGenerator gen(10);
+ uint64_t time_delta = 1000000 / 8;
+ for (int i = 0; i < 60; ++i) {
+ regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ regular_estimator_.AdvanceClock(time_delta);
+ low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ low_rate_estimator_.AdvanceClock(time_delta);
+ EXPECT_GT(regular_estimator_.GetJitterEstimate(0), 0);
+ EXPECT_GT(low_rate_estimator_.GetJitterEstimate(0), 0);
+ EXPECT_GE(regular_estimator_.GetJitterEstimate(0),
+ low_rate_estimator_.GetJitterEstimate(0));
+ gen.Advance();
+ }
+}
+
+// 30 fps, steady state estimate should be same as previous method.
+TEST_F(TestVCMJitterEstimator, TestHighRate) {
+ ValueGenerator gen(10);
+ uint64_t time_delta = 1000000 / 30;
+ for (int i = 0; i < 60; ++i) {
+ regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ regular_estimator_.AdvanceClock(time_delta);
+ low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ low_rate_estimator_.AdvanceClock(time_delta);
+ EXPECT_EQ(regular_estimator_.GetJitterEstimate(0),
+ low_rate_estimator_.GetJitterEstimate(0));
+ gen.Advance();
+ }
+}
+
+// 10 fps, high jitter then low jitter. Low rate estimator should converge
+// faster to low noise estimate.
+TEST_F(TestVCMJitterEstimator, TestConvergence) {
+ // Reach a steady state with high noise.
+ ValueGenerator gen(50);
+ uint64_t time_delta = 1000000 / 10;
+ for (int i = 0; i < 100; ++i) {
+ regular_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ regular_estimator_.AdvanceClock(time_delta * 2);
+ low_rate_estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
+ low_rate_estimator_.AdvanceClock(time_delta * 2);
+ gen.Advance();
+ }
+
+ int threshold = regular_estimator_.GetJitterEstimate(0) / 2;
+
+ // New generator with zero noise.
+ ValueGenerator low_gen(0);
+ int regular_iterations = 0;
+ int low_rate_iterations = 0;
+ for (int i = 0; i < 500; ++i) {
+ if (regular_iterations == 0) {
+ regular_estimator_.UpdateEstimate(low_gen.Delay(), low_gen.FrameSize());
+ regular_estimator_.AdvanceClock(time_delta);
+ if (regular_estimator_.GetJitterEstimate(0) < threshold) {
+ regular_iterations = i;
+ }
+ }
+
+ if (low_rate_iterations == 0) {
+ low_rate_estimator_.UpdateEstimate(low_gen.Delay(), low_gen.FrameSize());
+ low_rate_estimator_.AdvanceClock(time_delta);
+ if (low_rate_estimator_.GetJitterEstimate(0) < threshold) {
+ low_rate_iterations = i;
+ }
+ }
+
+ if (regular_iterations != 0 && low_rate_iterations != 0) {
+ break;
+ }
+
+ gen.Advance();
+ }
+
+ EXPECT_NE(regular_iterations, 0);
+ EXPECT_NE(low_rate_iterations, 0);
+ EXPECT_LE(low_rate_iterations, regular_iterations);
+}
+}