Update talk to 58174641 together with http://review.webrtc.org/4319005/.
R=turaj@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/5809004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5287 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index 070c731..bb75c2a 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -339,12 +339,14 @@
};
class Capturer : public webrtc::ViEExternalCapture {
public:
- Capturer() : channel_id_(-1), denoising_(false), last_capture_time_(0) { }
+ Capturer() : channel_id_(-1), denoising_(false),
+ last_capture_time_(0), incoming_frame_num_(0) { }
int channel_id() const { return channel_id_; }
void set_channel_id(int channel_id) { channel_id_ = channel_id; }
bool denoising() const { return denoising_; }
void set_denoising(bool denoising) { denoising_ = denoising; }
- int64 last_capture_time() { return last_capture_time_; }
+ int64 last_capture_time() const { return last_capture_time_; }
+ int incoming_frame_num() const { return incoming_frame_num_; }
// From ViEExternalCapture
virtual int IncomingFrame(unsigned char* videoFrame,
@@ -359,6 +361,7 @@
const webrtc::ViEVideoFrameI420& video_frame,
unsigned long long captureTime) {
last_capture_time_ = captureTime;
+ ++incoming_frame_num_;
return 0;
}
@@ -366,6 +369,7 @@
int channel_id_;
bool denoising_;
int64 last_capture_time_;
+ int incoming_frame_num_;
};
FakeWebRtcVideoEngine(const cricket::VideoCodec* const* codecs,
@@ -408,6 +412,16 @@
int GetLastCapturer() const { return last_capturer_; }
int GetNumCapturers() const { return static_cast<int>(capturers_.size()); }
+ int GetIncomingFrameNum(int channel_id) const {
+ for (std::map<int, Capturer*>::const_iterator iter = capturers_.begin();
+ iter != capturers_.end(); ++iter) {
+ Capturer* capturer = iter->second;
+ if (capturer->channel_id() == channel_id) {
+ return capturer->incoming_frame_num();
+ }
+ }
+ return -1;
+ }
void set_fail_alloc_capturer(bool fail_alloc_capturer) {
fail_alloc_capturer_ = fail_alloc_capturer;
}
diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h
index 809816b..a68d65e 100644
--- a/talk/media/webrtc/fakewebrtcvoiceengine.h
+++ b/talk/media/webrtc/fakewebrtcvoiceengine.h
@@ -631,6 +631,13 @@
// webrtc::VoENetEqStats
WEBRTC_STUB(GetNetworkStatistics, (int, webrtc::NetworkStatistics&));
+#ifdef USE_WEBRTC_DEV_BRANCH
+ WEBRTC_FUNC_CONST(GetDecodingCallStatistics, (int channel,
+ webrtc::AudioDecodingCallStats*)) {
+ WEBRTC_CHECK_CHANNEL(channel);
+ return 0;
+ }
+#endif
// webrtc::VoENetwork
WEBRTC_FUNC(RegisterExternalTransport, (int channel,
diff --git a/talk/media/webrtc/webrtcmediaengine.h b/talk/media/webrtc/webrtcmediaengine.h
index 94e7a99..82abefa 100644
--- a/talk/media/webrtc/webrtcmediaengine.h
+++ b/talk/media/webrtc/webrtcmediaengine.h
@@ -145,6 +145,9 @@
virtual void SetVideoLogging(int min_sev, const char* filter) OVERRIDE {
delegate_->SetVideoLogging(min_sev, filter);
}
+ virtual bool StartAecDump(FILE* file) OVERRIDE {
+ return delegate_->StartAecDump(file);
+ }
virtual bool RegisterVoiceProcessor(
uint32 ssrc, VoiceProcessor* video_processor,
MediaProcessorDirection direction) OVERRIDE {
diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc
index 1c1ccc3..88e09fc 100644
--- a/talk/media/webrtc/webrtcvideoengine.cc
+++ b/talk/media/webrtc/webrtcvideoengine.cc
@@ -2119,18 +2119,6 @@
}
WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel(
- VideoCapturer* video_capturer) {
- for (SendChannelMap::iterator iter = send_channels_.begin();
- iter != send_channels_.end(); ++iter) {
- WebRtcVideoChannelSendInfo* send_channel = iter->second;
- if (send_channel->video_capturer() == video_capturer) {
- return send_channel;
- }
- }
- return NULL;
-}
-
-WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel(
uint32 local_ssrc) {
uint32 key;
if (!GetSendChannelKey(local_ssrc, &key)) {
@@ -2159,6 +2147,18 @@
return true;
}
+int WebRtcVideoMediaChannel::GetSendChannelNum(VideoCapturer* capturer) {
+ int num = 0;
+ for (SendChannelMap::iterator iter = send_channels_.begin();
+ iter != send_channels_.end(); ++iter) {
+ WebRtcVideoChannelSendInfo* send_channel = iter->second;
+ if (send_channel->video_capturer() == capturer) {
+ ++num;
+ }
+ }
+ return num;
+}
+
uint32 WebRtcVideoMediaChannel::GetDefaultChannelSsrc() {
WebRtcVideoChannelSendInfo* send_channel = send_channels_[0];
const StreamParams* sp = send_channel->stream_params();
@@ -2174,11 +2174,8 @@
return false;
}
WebRtcVideoChannelSendInfo* send_channel = send_channels_[ssrc_key];
- VideoCapturer* capturer = send_channel->video_capturer();
- if (capturer != NULL) {
- capturer->SignalVideoFrame.disconnect(this);
- send_channel->set_video_capturer(NULL);
- }
+ MaybeDisconnectCapturer(send_channel->video_capturer());
+ send_channel->set_video_capturer(NULL);
int channel_id = send_channel->channel_id();
int capture_id = send_channel->capture_id();
@@ -2217,7 +2214,7 @@
if (capturer == NULL) {
return false;
}
- capturer->SignalVideoFrame.disconnect(this);
+ MaybeDisconnectCapturer(capturer);
send_channel->set_video_capturer(NULL);
const int64 timestamp = send_channel->local_stream_info()->time_stamp();
if (send_codec_) {
@@ -2468,14 +2465,10 @@
return false;
}
VideoCapturer* old_capturer = send_channel->video_capturer();
- if (old_capturer) {
- old_capturer->SignalVideoFrame.disconnect(this);
- }
+ MaybeDisconnectCapturer(old_capturer);
send_channel->set_video_capturer(capturer);
- capturer->SignalVideoFrame.connect(
- this,
- &WebRtcVideoMediaChannel::SendFrame);
+ MaybeConnectCapturer(capturer);
if (!capturer->IsScreencast() && ratio_w_ != 0 && ratio_h_ != 0) {
capturer->UpdateAspectRatio(ratio_w_, ratio_h_);
}
@@ -2865,20 +2858,23 @@
return true;
}
-// TODO(zhurunz): Add unittests to test this function.
-// TODO(thorcarpenter): This is broken. One capturer registered on two ssrc
-// will not send any video to the second ssrc send channel. We should remove
-// GetSendChannel(capturer) and pass in an ssrc here.
void WebRtcVideoMediaChannel::SendFrame(VideoCapturer* capturer,
const VideoFrame* frame) {
- // If there's send channel registers to the |capturer|, then only send the
- // frame to that channel and return. Otherwise send the frame to the default
- // channel, which currently taking frames from the engine.
- WebRtcVideoChannelSendInfo* send_channel = GetSendChannel(capturer);
- if (send_channel) {
- SendFrame(send_channel, frame, capturer->IsScreencast());
+ // If the |capturer| is registered to any send channel, then send the frame
+ // to those send channels.
+ bool capturer_is_channel_owned = false;
+ for (SendChannelMap::iterator iter = send_channels_.begin();
+ iter != send_channels_.end(); ++iter) {
+ WebRtcVideoChannelSendInfo* send_channel = iter->second;
+ if (send_channel->video_capturer() == capturer) {
+ SendFrame(send_channel, frame, capturer->IsScreencast());
+ capturer_is_channel_owned = true;
+ }
+ }
+ if (capturer_is_channel_owned) {
return;
}
+
// TODO(hellner): Remove below for loop once the captured frame no longer
// come from the engine, i.e. the engine no longer owns a capturer.
for (SendChannelMap::iterator iter = send_channels_.begin();
@@ -3754,6 +3750,19 @@
return true;
}
+void WebRtcVideoMediaChannel::MaybeConnectCapturer(VideoCapturer* capturer) {
+ if (capturer != NULL && GetSendChannelNum(capturer) == 1) {
+ capturer->SignalVideoFrame.connect(this,
+ &WebRtcVideoMediaChannel::SendFrame);
+ }
+}
+
+void WebRtcVideoMediaChannel::MaybeDisconnectCapturer(VideoCapturer* capturer) {
+ if (capturer != NULL && GetSendChannelNum(capturer) == 1) {
+ capturer->SignalVideoFrame.disconnect(this);
+ }
+}
+
} // namespace cricket
#endif // HAVE_WEBRTC_VIDEO
diff --git a/talk/media/webrtc/webrtcvideoengine.h b/talk/media/webrtc/webrtcvideoengine.h
index 6278461..289903a 100644
--- a/talk/media/webrtc/webrtcvideoengine.h
+++ b/talk/media/webrtc/webrtcvideoengine.h
@@ -366,11 +366,12 @@
// If the local ssrc correspond to that of the default channel the key is 0.
// For all other channels the returned key will be the same as the local ssrc.
bool GetSendChannelKey(uint32 local_ssrc, uint32* key);
- WebRtcVideoChannelSendInfo* GetSendChannel(VideoCapturer* video_capturer);
WebRtcVideoChannelSendInfo* GetSendChannel(uint32 local_ssrc);
// Creates a new unique key that can be used for inserting a new send channel
// into |send_channels_|
bool CreateSendChannelKey(uint32 local_ssrc, uint32* key);
+ // Get the number of the send channels |capturer| registered with.
+ int GetSendChannelNum(VideoCapturer* capturer);
bool IsDefaultChannel(int channel_id) const {
return channel_id == vie_channel_;
@@ -404,6 +405,13 @@
bool SetLocalRtxSsrc(int channel_id, const StreamParams& send_params,
uint32 primary_ssrc, int stream_idx);
+ // Connect |capturer| to WebRtcVideoMediaChannel if it is only registered
+ // to one send channel, i.e. the first send channel.
+ void MaybeConnectCapturer(VideoCapturer* capturer);
+ // Disconnect |capturer| from WebRtcVideoMediaChannel if it is only registered
+ // to one send channel, i.e. the last send channel.
+ void MaybeDisconnectCapturer(VideoCapturer* capturer);
+
// Global state.
WebRtcVideoEngine* engine_;
VoiceMediaChannel* voice_channel_;
diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc
index 2b83cce..d5886a1 100644
--- a/talk/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc
@@ -1216,6 +1216,53 @@
EXPECT_FALSE(vie_.GetCaptureDenoising(capture_id));
}
+TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) {
+ EXPECT_TRUE(SetupEngine());
+
+ // Start the capturer
+ cricket::FakeVideoCapturer capturer;
+ cricket::VideoFormat capture_format_vga = cricket::VideoFormat(640, 480,
+ cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420);
+ EXPECT_EQ(cricket::CS_RUNNING, capturer.Start(capture_format_vga));
+
+ // Add send streams and connect the capturer
+ for (unsigned int i = 0; i < sizeof(kSsrcs2)/sizeof(kSsrcs2[0]); ++i) {
+ EXPECT_TRUE(channel_->AddSendStream(
+ cricket::StreamParams::CreateLegacy(kSsrcs2[i])));
+ // Register the capturer to the ssrc.
+ EXPECT_TRUE(channel_->SetCapturer(kSsrcs2[i], &capturer));
+ }
+
+ const int channel0 = vie_.GetChannelFromLocalSsrc(kSsrcs2[0]);
+ ASSERT_NE(-1, channel0);
+ const int channel1 = vie_.GetChannelFromLocalSsrc(kSsrcs2[1]);
+ ASSERT_NE(-1, channel1);
+ ASSERT_NE(channel0, channel1);
+
+ // Set send codec.
+ std::vector<cricket::VideoCodec> codecs;
+ cricket::VideoCodec send_codec(100, "VP8", 640, 480, 30, 0);
+ codecs.push_back(send_codec);
+ EXPECT_TRUE(channel_->SetSendCodecs(codecs));
+
+ EXPECT_TRUE(capturer.CaptureFrame());
+ EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0));
+ EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1));
+
+ EXPECT_TRUE(channel_->RemoveSendStream(kSsrcs2[0]));
+ EXPECT_TRUE(capturer.CaptureFrame());
+ // channel0 is the default channel, so it won't be deleted.
+ // But it should be disconnected from the capturer.
+ EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0));
+ EXPECT_EQ(2, vie_.GetIncomingFrameNum(channel1));
+
+ EXPECT_TRUE(channel_->RemoveSendStream(kSsrcs2[1]));
+ EXPECT_TRUE(capturer.CaptureFrame());
+ EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0));
+ // channel1 has already been deleted.
+ EXPECT_EQ(-1, vie_.GetIncomingFrameNum(channel1));
+}
+
// Disabled since its flaky: b/11288120
TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) {
diff --git a/webrtc/common_types.h b/webrtc/common_types.h
index 30231ce..bcdb5e8 100644
--- a/webrtc/common_types.h
+++ b/webrtc/common_types.h
@@ -382,6 +382,25 @@
int addedSamples;
};
+// Statistics for calls to AudioCodingModule::PlayoutData10Ms().
+struct AudioDecodingCallStats {
+ AudioDecodingCallStats()
+ : calls_to_silence_generator(0),
+ calls_to_neteq(0),
+ decoded_normal(0),
+ decoded_plc(0),
+ decoded_cng(0),
+ decoded_plc_cng(0) {}
+
+ int calls_to_silence_generator; // Number of calls where silence generated,
+ // and NetEq was disengaged from decoding.
+ int calls_to_neteq; // Number of calls to NetEq.
+ int decoded_normal; // Number of calls where audio RTP packet decoded.
+ int decoded_plc; // Number of calls resulted in PLC.
+ int decoded_cng; // Number of calls where comfort noise generated due to DTX.
+ int decoded_plc_cng; // Number of calls resulted where PLC faded to CNG.
+};
+
typedef struct
{
int min; // minumum
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
index 5da42ad..ac92198 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
@@ -19,6 +19,7 @@
#include "webrtc/common_types.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/modules/audio_coding/main/acm2/nack.h"
#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h"
#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h"
@@ -461,6 +462,7 @@
audio_frame->vad_activity_ = previous_audio_activity_;
SetAudioFrameActivityAndType(vad_enabled_, type, audio_frame);
previous_audio_activity_ = audio_frame->vad_activity_;
+ call_stats_.DecodedByNetEq(audio_frame->speech_type_);
return 0;
}
@@ -761,6 +763,9 @@
return false;
}
+ // Update statistics.
+ call_stats_.DecodedBySilenceGenerator();
+
// Set the values if already got a packet, otherwise set to default values.
if (last_audio_decoder_ >= 0) {
current_sample_rate_hz_ = ACMCodecDB::database_[last_audio_decoder_].plfreq;
@@ -832,6 +837,12 @@
}
}
+void AcmReceiver::GetDecodingCallStatistics(
+ AudioDecodingCallStats* stats) const {
+ CriticalSectionScoped lock(neteq_crit_sect_);
+ *stats = call_stats_.GetDecodingStatistics();
+}
+
} // namespace acm2
} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.h b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
index 9267c1e..81eb520 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
@@ -18,6 +18,7 @@
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/modules/audio_coding/main/acm2/initial_delay_manager.h"
#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h"
#include "webrtc/modules/interface/module_common_types.h"
@@ -320,6 +321,10 @@
//
NetEqBackgroundNoiseMode BackgroundNoiseModeForTest() const;
+ //
+ // Get statistics of calls to GetAudio().
+ void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const;
+
private:
int PayloadType2CodecIndex(uint8_t payload_type) const;
@@ -361,6 +366,8 @@
// initial delay is set.
scoped_ptr<InitialDelayManager::SyncStream> missing_packets_sync_stream_;
scoped_ptr<InitialDelayManager::SyncStream> late_packets_sync_stream_;
+
+ CallStatistics call_stats_;
};
} // namespace acm2
diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
index 0754126..f51c3bf 100644
--- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
+++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
@@ -84,6 +84,8 @@
'audio_coding_module.cc',
'audio_coding_module_impl.cc',
'audio_coding_module_impl.h',
+ 'call_statistics.cc',
+ 'call_statistics.h',
'initial_delay_manager.cc',
'initial_delay_manager.h',
'nack.cc',
diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
index ce05218..4c64e07 100644
--- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
+++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
@@ -20,6 +20,7 @@
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_resampler.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
@@ -1979,6 +1980,11 @@
return kExperimentalAcmVersion;
}
+void AudioCodingModuleImpl::GetDecodingCallStatistics(
+ AudioDecodingCallStats* call_stats) const {
+ receiver_.GetDecodingCallStatistics(call_stats);
+}
+
} // namespace acm2
} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h
index 17fab39..bc4ea0f 100644
--- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h
+++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h
@@ -228,6 +228,8 @@
std::vector<uint16_t> GetNackList(int round_trip_time_ms) const;
+ void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const;
+
private:
int UnregisterReceiveCodecSafe(int payload_type);
diff --git a/webrtc/modules/audio_coding/main/acm2/call_statistics.cc b/webrtc/modules/audio_coding/main/acm2/call_statistics.cc
new file mode 100644
index 0000000..9153325
--- /dev/null
+++ b/webrtc/modules/audio_coding/main/acm2/call_statistics.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 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/audio_coding/main/acm2/call_statistics.h"
+
+#include <cassert>
+
+namespace webrtc {
+
+namespace acm2 {
+
+void CallStatistics::DecodedByNetEq(AudioFrame::SpeechType speech_type) {
+ ++decoding_stat_.calls_to_neteq;
+ switch (speech_type) {
+ case AudioFrame::kNormalSpeech: {
+ ++decoding_stat_.decoded_normal;
+ break;
+ }
+ case AudioFrame::kPLC: {
+ ++decoding_stat_.decoded_plc;
+ break;
+ }
+ case AudioFrame::kCNG: {
+ ++decoding_stat_.decoded_cng;
+ break;
+ }
+ case AudioFrame::kPLCCNG: {
+ ++decoding_stat_.decoded_plc_cng;
+ break;
+ }
+ case AudioFrame::kUndefined: {
+ // If the audio is decoded by NetEq, |kUndefined| is not an option.
+ assert(false);
+ }
+ }
+}
+
+void CallStatistics::DecodedBySilenceGenerator() {
+ ++decoding_stat_.calls_to_silence_generator;
+}
+
+const AudioDecodingCallStats& CallStatistics::GetDecodingStatistics() const {
+ return decoding_stat_;
+}
+
+} // namespace acm2
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/call_statistics.h b/webrtc/modules/audio_coding/main/acm2/call_statistics.h
new file mode 100644
index 0000000..2aece0f
--- /dev/null
+++ b/webrtc/modules/audio_coding/main/acm2/call_statistics.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CALL_STATISTICS_H_
+#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CALL_STATISTICS_H_
+
+#include "webrtc/common_types.h"
+#include "webrtc/modules/interface/module_common_types.h"
+
+//
+// This class is for book keeping of calls to ACM. It is not useful to log API
+// calls which are supposed to be called every 10ms, e.g. PlayoutData10Ms(),
+// however, it is useful to know the number of such calls in a given time
+// interval. The current implementation covers calls to PlayoutData10Ms() with
+// detailed accounting of the decoded speech type.
+//
+// Thread Safety
+// =============
+// Please note that this class in not thread safe. The class must be protected
+// if different APIs are called from different threads.
+//
+
+namespace webrtc {
+
+namespace acm2 {
+
+class CallStatistics {
+ public:
+ CallStatistics() {}
+ ~CallStatistics() {}
+
+ // Call this method to indicate that NetEq engaged in decoding. |speech_type|
+ // is the audio-type according to NetEq.
+ void DecodedByNetEq(AudioFrame::SpeechType speech_type);
+
+ // Call this method to indicate that a decoding call resulted in generating
+ // silence, i.e. call to NetEq is bypassed and the output audio is zero.
+ void DecodedBySilenceGenerator();
+
+ // Get statistics for decoding. The statistics include the number of calls to
+ // NetEq and silence generator, as well as the type of speech pulled of off
+ // NetEq, c.f. declaration of AudioDecodingCallStats for detailed description.
+ const AudioDecodingCallStats& GetDecodingStatistics() const;
+
+ private:
+ // Reset the decoding statistics.
+ void ResetDecodingStatistics();
+
+ AudioDecodingCallStats decoding_stat_;
+};
+
+} // namespace acm2
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CALL_STATISTICS_H_
diff --git a/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc b/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc
new file mode 100644
index 0000000..61aadd7
--- /dev/null
+++ b/webrtc/modules/audio_coding/main/acm2/call_statistics_unittest.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 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 "gtest/gtest.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
+
+namespace webrtc {
+
+namespace acm2 {
+
+TEST(CallStatisticsTest, InitializedZero) {
+ CallStatistics call_stats;
+ AudioDecodingCallStats stats;
+
+ stats = call_stats.GetDecodingStatistics();
+ EXPECT_EQ(0, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(0, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+}
+
+TEST(CallStatisticsTest, AllCalls) {
+ CallStatistics call_stats;
+ AudioDecodingCallStats stats;
+
+ call_stats.DecodedBySilenceGenerator();
+ call_stats.DecodedByNetEq(AudioFrame::kNormalSpeech);
+ call_stats.DecodedByNetEq(AudioFrame::kPLC);
+ call_stats.DecodedByNetEq(AudioFrame::kPLCCNG);
+ call_stats.DecodedByNetEq(AudioFrame::kCNG);
+
+ stats = call_stats.GetDecodingStatistics();
+ EXPECT_EQ(4, stats.calls_to_neteq);
+ EXPECT_EQ(1, stats.calls_to_silence_generator);
+ EXPECT_EQ(1, stats.decoded_normal);
+ EXPECT_EQ(1, stats.decoded_cng);
+ EXPECT_EQ(1, stats.decoded_plc);
+ EXPECT_EQ(1, stats.decoded_plc_cng);
+}
+
+} // namespace acm2
+
+} // namespace webrtc
+
+
+
diff --git a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h
index f8b9690..db45add 100644
--- a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h
+++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h
@@ -931,6 +931,9 @@
// is returned.
//
virtual std::vector<uint16_t> GetNackList(int round_trip_time_ms) const = 0;
+
+ virtual void GetDecodingCallStatistics(
+ AudioDecodingCallStats* call_stats) const = 0;
};
struct AudioCodingModuleFactory {
diff --git a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc
index 1e71a04..556f530 100644
--- a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc
+++ b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.cc
@@ -18,6 +18,7 @@
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/modules/audio_coding/main/source/acm_dtmf_detection.h"
#include "webrtc/modules/audio_coding/main/source/acm_generic_codec.h"
#include "webrtc/modules/audio_coding/main/source/acm_resampler.h"
@@ -2273,6 +2274,9 @@
{
CriticalSectionScoped lock(acm_crit_sect_);
+ // Update call statistics.
+ call_stats_.DecodedByNetEq(audio_frame->speech_type_);
+
if (update_nack) {
assert(nack_.get());
nack_->UpdateLastDecodedPacket(decoded_seq_num, decoded_timestamp);
@@ -2879,6 +2883,9 @@
return false;
}
+ // Record call to silence generator.
+ call_stats_.DecodedBySilenceGenerator();
+
// We stop accumulating packets, if the number of packets or the total size
// exceeds a threshold.
int max_num_packets;
@@ -3030,6 +3037,12 @@
return kLegacyAcmVersion;
}
+void AudioCodingModuleImpl::GetDecodingCallStatistics(
+ AudioDecodingCallStats* call_stats) const {
+ CriticalSectionScoped lock(acm_crit_sect_);
+ *call_stats = call_stats_.GetDecodingStatistics();
+}
+
} // namespace acm1
} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h
index 7acde17..f0b22f1 100644
--- a/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h
+++ b/webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h
@@ -19,6 +19,7 @@
#include "webrtc/modules/audio_coding/main/source/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/source/acm_neteq.h"
#include "webrtc/modules/audio_coding/main/source/acm_resampler.h"
+#include "webrtc/modules/audio_coding/main/acm2/call_statistics.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
namespace webrtc {
@@ -303,6 +304,8 @@
// Disable NACK.
void DisableNack();
+ void GetDecodingCallStatistics(AudioDecodingCallStats* call_stats) const;
+
private:
// Change required states after starting to receive the codec corresponding
// to |index|.
@@ -441,6 +444,8 @@
Clock* clock_;
scoped_ptr<acm2::Nack> nack_;
bool nack_enabled_;
+
+ acm2::CallStatistics call_stats_;
};
} // namespace acm1
diff --git a/webrtc/modules/audio_coding/neteq/webrtc_neteq.c b/webrtc/modules/audio_coding/neteq/webrtc_neteq.c
index de1ccd1..fad690d 100644
--- a/webrtc/modules/audio_coding/neteq/webrtc_neteq.c
+++ b/webrtc/modules/audio_coding/neteq/webrtc_neteq.c
@@ -1104,14 +1104,6 @@
/* If CN or internal CNG */
*outputType = kOutputCNG;
-#ifdef NETEQ_VAD
- }
- else if ( NetEqMainInst->DSPinst.VADInst.VADDecision == 0 )
- {
- /* post-decode VAD says passive speaker */
- *outputType = kOutputVADPassive;
-#endif /* NETEQ_VAD */
-
}
else if ((NetEqMainInst->DSPinst.w16_mode == MODE_EXPAND)
&& (NetEqMainInst->DSPinst.ExpandInst.w16_expandMuteFactor == 0))
@@ -1125,6 +1117,14 @@
/* PLC mode */
*outputType = kOutputPLC;
+#ifdef NETEQ_VAD
+ }
+ else if ( NetEqMainInst->DSPinst.VADInst.VADDecision == 0 )
+ {
+ /* post-decode VAD says passive speaker */
+ *outputType = kOutputVADPassive;
+#endif /* NETEQ_VAD */
+
}
else
{
diff --git a/webrtc/modules/audio_coding/neteq4/neteq_impl.cc b/webrtc/modules/audio_coding/neteq4/neteq_impl.cc
index 73ca5e4..fb27af2 100644
--- a/webrtc/modules/audio_coding/neteq4/neteq_impl.cc
+++ b/webrtc/modules/audio_coding/neteq4/neteq_impl.cc
@@ -1887,13 +1887,13 @@
assert(expand_.get());
if (last_mode_ == kModeCodecInternalCng || last_mode_ == kModeRfc3389Cng) {
return kOutputCNG;
- } else if (vad_->running() && !vad_->active_speech()) {
- return kOutputVADPassive;
} else if (last_mode_ == kModeExpand && expand_->MuteFactor(0) == 0) {
// Expand mode has faded down to background noise only (very long expand).
return kOutputPLCtoCNG;
} else if (last_mode_ == kModeExpand) {
return kOutputPLC;
+ } else if (vad_->running() && !vad_->active_speech()) {
+ return kOutputVADPassive;
} else {
return kOutputNormal;
}
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index e33816c..ef354ab 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -102,6 +102,7 @@
],
'sources': [
'audio_coding/main/acm2/acm_receiver_unittest.cc',
+ 'audio_coding/main/acm2/call_statistics_unittest.cc',
'audio_coding/main/acm2/initial_delay_manager_unittest.cc',
'audio_coding/main/acm2/nack_unittest.cc',
'audio_coding/main/source/acm_neteq_unittest.cc',
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 37016ce..4e1913d 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -4619,6 +4619,10 @@
return return_value;
}
+void Channel::GetDecodingCallStatistics(AudioDecodingCallStats* stats) const {
+ audio_coding_->GetDecodingCallStatistics(stats);
+}
+
bool Channel::GetDelayEstimate(int* jitter_buffer_delay_ms,
int* playout_buffer_delay_ms) const {
if (_average_jitter_buffer_delay_us == 0) {
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index 4647056..da55e9d 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -201,6 +201,7 @@
// VoENetEqStats
int GetNetworkStatistics(NetworkStatistics& stats);
+ void GetDecodingCallStatistics(AudioDecodingCallStats* stats) const;
// VoEVideoSync
bool GetDelayEstimate(int* jitter_buffer_delay_ms,
diff --git a/webrtc/voice_engine/include/voe_neteq_stats.h b/webrtc/voice_engine/include/voe_neteq_stats.h
index f4e8f7b..1e8c240 100644
--- a/webrtc/voice_engine/include/voe_neteq_stats.h
+++ b/webrtc/voice_engine/include/voe_neteq_stats.h
@@ -35,6 +35,10 @@
// The statistics are reset after the query.
virtual int GetNetworkStatistics(int channel, NetworkStatistics& stats) = 0;
+ // Get statistics of calls to AudioCodingModule::PlayoutData10Ms().
+ virtual int GetDecodingCallStatistics(
+ int channel, AudioDecodingCallStats* stats) const = 0;
+
protected:
VoENetEqStats() {}
virtual ~VoENetEqStats() {}
diff --git a/webrtc/voice_engine/voe_neteq_stats_impl.cc b/webrtc/voice_engine/voe_neteq_stats_impl.cc
index 264f486..37d1cda 100644
--- a/webrtc/voice_engine/voe_neteq_stats_impl.cc
+++ b/webrtc/voice_engine/voe_neteq_stats_impl.cc
@@ -17,7 +17,6 @@
#include "webrtc/voice_engine/include/voe_errors.h"
#include "webrtc/voice_engine/voice_engine_impl.h"
-
namespace webrtc {
VoENetEqStats* VoENetEqStats::GetInterface(VoiceEngine* voiceEngine)
@@ -73,6 +72,27 @@
return channelPtr->GetNetworkStatistics(stats);
}
+int VoENetEqStatsImpl::GetDecodingCallStatistics(
+ int channel, AudioDecodingCallStats* stats) const {
+ ANDROID_NOT_SUPPORTED(_shared->statistics());
+
+ if (!_shared->statistics().Initialized()) {
+ _shared->SetLastError(VE_NOT_INITED, kTraceError);
+ return -1;
+ }
+ voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+ voe::Channel* channelPtr = ch.channel();
+ if (channelPtr == NULL) {
+ _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+ "GetDecodingCallStatistics() failed to locate "
+ "channel");
+ return -1;
+ }
+
+ channelPtr->GetDecodingCallStatistics(stats);
+ return 0;
+}
+
#endif // #ifdef WEBRTC_VOICE_ENGINE_NETEQ_STATS_API
} // namespace webrtc
diff --git a/webrtc/voice_engine/voe_neteq_stats_impl.h b/webrtc/voice_engine/voe_neteq_stats_impl.h
index e99ebc0..74b624b 100644
--- a/webrtc/voice_engine/voe_neteq_stats_impl.h
+++ b/webrtc/voice_engine/voe_neteq_stats_impl.h
@@ -13,6 +13,7 @@
#include "webrtc/voice_engine/include/voe_neteq_stats.h"
+#include "webrtc/common_types.h"
#include "webrtc/voice_engine/shared_data.h"
namespace webrtc {
@@ -23,6 +24,9 @@
virtual int GetNetworkStatistics(int channel,
NetworkStatistics& stats);
+ virtual int GetDecodingCallStatistics(
+ int channel, AudioDecodingCallStats* stats) const;
+
protected:
VoENetEqStatsImpl(voe::SharedData* shared);
virtual ~VoENetEqStatsImpl();
diff --git a/webrtc/voice_engine/voe_neteq_stats_unittest.cc b/webrtc/voice_engine/voe_neteq_stats_unittest.cc
new file mode 100644
index 0000000..66a9a69
--- /dev/null
+++ b/webrtc/voice_engine/voe_neteq_stats_unittest.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2013 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/voice_engine/include/voe_neteq_stats.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/audio_device/include/fake_audio_device.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+#include "webrtc/voice_engine/include/voe_base.h"
+#include "webrtc/voice_engine/include/voe_hardware.h"
+#include "webrtc/voice_engine/voice_engine_defines.h"
+#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
+#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
+#include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h"
+#include "webrtc/modules/audio_coding/main/source/audio_coding_module_impl.h"
+
+namespace webrtc {
+namespace voe {
+namespace {
+
+const int kSampleRateHz = 16000;
+const int kNumSamples10ms = kSampleRateHz / 100;
+const int kFrameSizeMs = 10; // Multiple of 10.
+const int kFrameSizeSamples = kFrameSizeMs / 10 * kNumSamples10ms;
+const int kPayloadSizeBytes = kFrameSizeSamples * sizeof(int16_t);
+const uint8_t kPayloadType = 111;
+
+class RtpUtility {
+ public:
+ RtpUtility(int samples_per_packet, uint8_t payload_type)
+ : samples_per_packet_(samples_per_packet), payload_type_(payload_type) {}
+
+ virtual ~RtpUtility() {}
+
+ void Populate(WebRtcRTPHeader* rtp_header) {
+ rtp_header->header.sequenceNumber = 0xABCD;
+ rtp_header->header.timestamp = 0xABCDEF01;
+ rtp_header->header.payloadType = payload_type_;
+ rtp_header->header.markerBit = false;
+ rtp_header->header.ssrc = 0x1234;
+ rtp_header->header.numCSRCs = 0;
+ rtp_header->frameType = kAudioFrameSpeech;
+
+ rtp_header->header.payload_type_frequency = kSampleRateHz;
+ rtp_header->type.Audio.channel = 1;
+ rtp_header->type.Audio.isCNG = false;
+ }
+
+ void Forward(WebRtcRTPHeader* rtp_header) {
+ ++rtp_header->header.sequenceNumber;
+ rtp_header->header.timestamp += samples_per_packet_;
+ }
+
+ private:
+ int samples_per_packet_;
+ uint8_t payload_type_;
+};
+
+// This factory method allows access to ACM of a channel, facilitating insertion
+// of packets to and pulling audio of ACM.
+struct InsertAcm : AudioCodingModuleFactory {
+ explicit InsertAcm(AudioCodingModule* acm) : acm_(acm) {}
+ ~InsertAcm() {}
+ virtual AudioCodingModule* Create(int /*id*/) const { return acm_; }
+
+ AudioCodingModule* acm_;
+};
+
+class VoENetEqStatsTest : public ::testing::Test {
+ protected:
+ VoENetEqStatsTest()
+ : acm1_(new acm1::AudioCodingModuleImpl(1, Clock::GetRealTimeClock())),
+ acm2_(new acm2::AudioCodingModuleImpl(2)),
+ voe_(VoiceEngine::Create()),
+ base_(VoEBase::GetInterface(voe_)),
+ voe_neteq_stats_(VoENetEqStats::GetInterface(voe_)),
+ channel_acm1_(-1),
+ channel_acm2_(-1),
+ adm_(new FakeAudioDeviceModule),
+ rtp_utility_(new RtpUtility(kFrameSizeSamples, kPayloadType)) {}
+
+ ~VoENetEqStatsTest() {}
+
+ void TearDown() {
+ voe_neteq_stats_->Release();
+ base_->DeleteChannel(channel_acm1_);
+ base_->DeleteChannel(channel_acm2_);
+ base_->Terminate();
+ base_->Release();
+ VoiceEngine::Delete(voe_);
+ }
+
+ void SetUp() {
+ // Check if all components are valid.
+ ASSERT_TRUE(voe_ != NULL);
+ ASSERT_TRUE(base_ != NULL);
+ ASSERT_TRUE(adm_.get() != NULL);
+ ASSERT_EQ(0, base_->Init(adm_.get()));
+
+ // Set configs.
+ config_acm1_.Set<AudioCodingModuleFactory>(new InsertAcm(acm1_));
+ config_acm2_.Set<AudioCodingModuleFactory>(new InsertAcm(acm2_));
+
+ // Create channe1s;
+ channel_acm1_ = base_->CreateChannel(config_acm1_);
+ ASSERT_NE(-1, channel_acm1_);
+
+ channel_acm2_ = base_->CreateChannel(config_acm2_);
+ ASSERT_NE(-1, channel_acm2_);
+
+ CodecInst codec;
+ AudioCodingModule::Codec("L16", &codec, kSampleRateHz, 1);
+ codec.pltype = kPayloadType;
+
+ // Register L16 codec in ACMs.
+ ASSERT_EQ(0, acm1_->RegisterReceiveCodec(codec));
+ ASSERT_EQ(0, acm2_->RegisterReceiveCodec(codec));
+
+ rtp_utility_->Populate(&rtp_header_);
+ }
+
+ void InsertPacketAndPullAudio() {
+ AudioFrame audio_frame;
+ const uint8_t kPayload[kPayloadSizeBytes] = {0};
+
+ ASSERT_EQ(0,
+ acm1_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_));
+ ASSERT_EQ(0,
+ acm2_->IncomingPacket(kPayload, kPayloadSizeBytes, rtp_header_));
+
+ ASSERT_EQ(0, acm1_->PlayoutData10Ms(-1, &audio_frame));
+ ASSERT_EQ(0, acm2_->PlayoutData10Ms(-1, &audio_frame));
+ rtp_utility_->Forward(&rtp_header_);
+ }
+
+ void JustPullAudio() {
+ AudioFrame audio_frame;
+ ASSERT_EQ(0, acm1_->PlayoutData10Ms(-1, &audio_frame));
+ ASSERT_EQ(0, acm2_->PlayoutData10Ms(-1, &audio_frame));
+ }
+
+ Config config_acm1_;
+ Config config_acm2_;
+
+ // ACMs are inserted into VoE channels, and this class is not the owner of
+ // them. Therefore, they should not be deleted, not even in destructor.
+ AudioCodingModule* acm1_;
+ AudioCodingModule* acm2_;
+
+ VoiceEngine* voe_;
+ VoEBase* base_;
+ VoENetEqStats* voe_neteq_stats_;
+ int channel_acm1_;
+ int channel_acm2_;
+ scoped_ptr<FakeAudioDeviceModule> adm_;
+ scoped_ptr<RtpUtility> rtp_utility_;
+ WebRtcRTPHeader rtp_header_;
+};
+
+// Check if the statistics are initialized correctly. Before any call to ACM
+// all fields have to be zero.
+TEST_F(VoENetEqStatsTest, InitializedToZero) {
+ AudioDecodingCallStats stats;
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats));
+ EXPECT_EQ(0, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(0, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats));
+ EXPECT_EQ(0, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(0, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+}
+
+// Apply an initial playout delay. Calls to AudioCodingModule::PlayoutData10ms()
+// should result in generating silence, check the associated field.
+TEST_F(VoENetEqStatsTest, SilenceGeneratorCalled) {
+ AudioDecodingCallStats stats;
+ const int kInitialDelay = 100;
+
+ acm1_->SetInitialPlayoutDelay(kInitialDelay);
+ acm2_->SetInitialPlayoutDelay(kInitialDelay);
+
+ AudioFrame audio_frame;
+ int num_calls = 0;
+ for (int time_ms = 0; time_ms < kInitialDelay;
+ time_ms += kFrameSizeMs, ++num_calls) {
+ InsertPacketAndPullAudio();
+ }
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats));
+ EXPECT_EQ(0, stats.calls_to_neteq);
+ EXPECT_EQ(num_calls, stats.calls_to_silence_generator);
+ EXPECT_EQ(0, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats));
+ EXPECT_EQ(0, stats.calls_to_neteq);
+ EXPECT_EQ(num_calls, stats.calls_to_silence_generator);
+ EXPECT_EQ(0, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+}
+
+// Insert some packets and pull audio. Check statistics are valid. Then,
+// simulate packet loss and check if PLC and PLC-to-CNG statistics are
+// correctly updated.
+TEST_F(VoENetEqStatsTest, NetEqCalls) {
+ AudioDecodingCallStats stats;
+ const int kNumNormalCalls = 10;
+
+ AudioFrame audio_frame;
+ for (int num_calls = 0; num_calls < kNumNormalCalls; ++num_calls) {
+ InsertPacketAndPullAudio();
+ }
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats));
+ EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(kNumNormalCalls, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats));
+ EXPECT_EQ(kNumNormalCalls, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(kNumNormalCalls, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(0, stats.decoded_plc);
+ EXPECT_EQ(0, stats.decoded_plc_cng);
+
+ const int kNumPlc = 3;
+ const int kNumPlcCng = 5;
+
+ // Simulate packet-loss. NetEq first performs PLC then PLC fades to CNG.
+ for (int n = 0; n < kNumPlc + kNumPlcCng; ++n) {
+ JustPullAudio();
+ }
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm1_, &stats));
+ EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(kNumNormalCalls, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(kNumPlc, stats.decoded_plc);
+ EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng);
+
+ ASSERT_EQ(0,
+ voe_neteq_stats_->GetDecodingCallStatistics(channel_acm2_, &stats));
+ EXPECT_EQ(kNumNormalCalls + kNumPlc + kNumPlcCng, stats.calls_to_neteq);
+ EXPECT_EQ(0, stats.calls_to_silence_generator);
+ EXPECT_EQ(kNumNormalCalls, stats.decoded_normal);
+ EXPECT_EQ(0, stats.decoded_cng);
+ EXPECT_EQ(kNumPlc, stats.decoded_plc);
+ EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng);
+}
+
+} // namespace
+
+} // namespace voe
+
+} // namespace webrtc
diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp
index 5e00077..8a06e86 100644
--- a/webrtc/voice_engine/voice_engine.gyp
+++ b/webrtc/voice_engine/voice_engine.gyp
@@ -134,6 +134,7 @@
'voe_audio_processing_unittest.cc',
'voe_base_unittest.cc',
'voe_codec_unittest.cc',
+ 'voe_neteq_stats_unittest.cc',
],
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are