blob: 861354f3db12b6bf6e92330ffc6797915307bb1e [file] [log] [blame]
/*
* 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/voice_engine/voe_call_report_impl.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/file_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
#include "webrtc/voice_engine/channel.h"
#include "webrtc/voice_engine/include/voe_errors.h"
#include "webrtc/voice_engine/voice_engine_impl.h"
namespace webrtc
{
VoECallReport* VoECallReport::GetInterface(VoiceEngine* voiceEngine)
{
#ifndef WEBRTC_VOICE_ENGINE_CALL_REPORT_API
return NULL;
#else
if (NULL == voiceEngine)
{
return NULL;
}
VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
s->AddRef();
return s;
#endif
}
#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API
VoECallReportImpl::VoECallReportImpl(voe::SharedData* shared) :
_file(*FileWrapper::Create()), _shared(shared)
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
"VoECallReportImpl() - ctor");
}
VoECallReportImpl::~VoECallReportImpl()
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
"~VoECallReportImpl() - dtor");
delete &_file;
}
int VoECallReportImpl::ResetCallReportStatistics(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"ResetCallReportStatistics(channel=%d)", channel);
ANDROID_NOT_SUPPORTED(_shared->statistics());
IPHONE_NOT_SUPPORTED(_shared->statistics());
if (!_shared->statistics().Initialized())
{
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
assert(_shared->audio_processing() != NULL);
bool echoMode =
_shared->audio_processing()->echo_cancellation()->are_metrics_enabled();
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
" current AudioProcessingModule echo metric state %d)",
echoMode);
// Reset the APM statistics
if (_shared->audio_processing()->echo_cancellation()->enable_metrics(true)
!= 0)
{
_shared->SetLastError(VE_APM_ERROR, kTraceError,
"ResetCallReportStatistics() unable to "
"set the AudioProcessingModule echo metrics state");
return -1;
}
// Restore metric states
_shared->audio_processing()->echo_cancellation()->enable_metrics(echoMode);
// Reset channel dependent statistics
if (channel != -1)
{
voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == NULL)
{
_shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"ResetCallReportStatistics() failed to locate channel");
return -1;
}
channelPtr->ResetDeadOrAliveCounters();
channelPtr->ResetRTCPStatistics();
} else {
for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
it.IsValid();
it.Increment()) {
it.GetChannel()->ResetDeadOrAliveCounters();
it.GetChannel()->ResetRTCPStatistics();
}
}
return 0;
}
int VoECallReportImpl::GetEchoMetricSummary(EchoStatistics& stats)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"GetEchoMetricSummary()");
ANDROID_NOT_SUPPORTED(_shared->statistics());
IPHONE_NOT_SUPPORTED(_shared->statistics());
if (!_shared->statistics().Initialized())
{
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
assert(_shared->audio_processing() != NULL);
return (GetEchoMetricSummaryInternal(stats));
}
int VoECallReportImpl::GetEchoMetricSummaryInternal(EchoStatistics& stats)
{
// Retrieve echo metrics from the AudioProcessingModule
int ret(0);
bool mode(false);
EchoCancellation::Metrics metrics;
// Ensure that echo metrics is enabled
mode =
_shared->audio_processing()->echo_cancellation()->are_metrics_enabled();
if (mode != false)
{
ret = _shared->audio_processing()->echo_cancellation()->
GetMetrics(&metrics);
if (ret != 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
VoEId(_shared->instance_id(), -1),
" AudioProcessingModule GetMetrics() => error");
}
}
else
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
VoEId(_shared->instance_id(), -1),
" AudioProcessingModule echo metrics is not enabled");
}
if ((ret != 0) || (mode == false))
{
// Mark complete struct as invalid (-100 dB)
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
VoEId(_shared->instance_id(), -1),
" unable to retrieve echo metrics from the AudioProcessingModule");
stats.erl.min = -100;
stats.erl.max = -100;
stats.erl.average = -100;
stats.erle.min = -100;
stats.erle.max = -100;
stats.erle.average = -100;
stats.rerl.min = -100;
stats.rerl.max = -100;
stats.rerl.average = -100;
stats.a_nlp.min = -100;
stats.a_nlp.max = -100;
stats.a_nlp.average = -100;
}
else
{
// Deliver output results to user
stats.erl.min = metrics.echo_return_loss.minimum;
stats.erl.max = metrics.echo_return_loss.maximum;
stats.erl.average = metrics.echo_return_loss.average;
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
VoEId(_shared->instance_id(), -1), " erl: min=%d, max=%d, avg=%d",
stats.erl.min, stats.erl.max, stats.erl.average);
stats.erle.min = metrics.echo_return_loss_enhancement.minimum;
stats.erle.max = metrics.echo_return_loss_enhancement.maximum;
stats.erle.average = metrics.echo_return_loss_enhancement.average;
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
VoEId(_shared->instance_id(), -1), " erle: min=%d, max=%d, avg=%d",
stats.erle.min, stats.erle.max, stats.erle.average);
stats.rerl.min = metrics.residual_echo_return_loss.minimum;
stats.rerl.max = metrics.residual_echo_return_loss.maximum;
stats.rerl.average = metrics.residual_echo_return_loss.average;
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
VoEId(_shared->instance_id(), -1), " rerl: min=%d, max=%d, avg=%d",
stats.rerl.min, stats.rerl.max, stats.rerl.average);
stats.a_nlp.min = metrics.a_nlp.minimum;
stats.a_nlp.max = metrics.a_nlp.maximum;
stats.a_nlp.average = metrics.a_nlp.average;
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
VoEId(_shared->instance_id(), -1),
" a_nlp: min=%d, max=%d, avg=%d",
stats.a_nlp.min, stats.a_nlp.max, stats.a_nlp.average);
}
return 0;
}
int VoECallReportImpl::GetRoundTripTimeSummary(int channel, StatVal& delaysMs)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"GetRoundTripTimeSummary()");
ANDROID_NOT_SUPPORTED(_shared->statistics());
IPHONE_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,
"GetRoundTripTimeSummary() failed to locate channel");
return -1;
}
return channelPtr->GetRoundTripTimeSummary(delaysMs);
}
int VoECallReportImpl::GetDeadOrAliveSummary(int channel,
int& numOfDeadDetections,
int& numOfAliveDetections)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"GetDeadOrAliveSummary(channel=%d)", channel);
ANDROID_NOT_SUPPORTED(_shared->statistics());
IPHONE_NOT_SUPPORTED(_shared->statistics());
if (!_shared->statistics().Initialized())
{
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
return (GetDeadOrAliveSummaryInternal(channel, numOfDeadDetections,
numOfAliveDetections));
}
int VoECallReportImpl::GetDeadOrAliveSummaryInternal(int channel,
int& numOfDeadDetections,
int& numOfAliveDetections)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"GetDeadOrAliveSummary(channel=%d)", channel);
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,
"GetRoundTripTimeSummary() failed to locate channel");
return -1;
}
return channelPtr->GetDeadOrAliveCounters(numOfDeadDetections,
numOfAliveDetections);
}
int VoECallReportImpl::WriteReportToFile(const char* fileNameUTF8)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"WriteReportToFile(fileNameUTF8=%s)", fileNameUTF8);
ANDROID_NOT_SUPPORTED(_shared->statistics());
IPHONE_NOT_SUPPORTED(_shared->statistics());
if (!_shared->statistics().Initialized())
{
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
if (NULL == fileNameUTF8)
{
_shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
"WriteReportToFile() invalid filename");
return -1;
}
if (_file.Open())
{
_file.CloseFile();
}
// Open text file in write mode
if (_file.OpenFile(fileNameUTF8, false, false, true) != 0)
{
_shared->SetLastError(VE_BAD_FILE, kTraceError,
"WriteReportToFile() unable to open the file");
return -1;
}
// Summarize information and add it to the open file
//
_file.WriteText("WebRtc VoiceEngine Call Report\n");
_file.WriteText("==============================\n");
_file.WriteText("\nNetwork Packet Round Trip Time (RTT)\n");
_file.WriteText("------------------------------------\n\n");
if (_shared->channel_manager().NumOfChannels() == 0)
return 0;
for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
it.IsValid();
it.Increment()) {
StatVal delaysMs;
_file.WriteText("channel %d:\n", it.GetChannel()->ChannelId());
it.GetChannel()->GetRoundTripTimeSummary(delaysMs);
_file.WriteText(" min:%5d [ms]\n", delaysMs.min);
_file.WriteText(" max:%5d [ms]\n", delaysMs.max);
_file.WriteText(" avg:%5d [ms]\n", delaysMs.average);
}
_file.WriteText("\nDead-or-Alive Connection Detections\n");
_file.WriteText("------------------------------------\n\n");
for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
it.IsValid();
it.Increment()) {
int dead = 0;
int alive = 0;
_file.WriteText("channel %d:\n", it.GetChannel()->ChannelId());
GetDeadOrAliveSummary(it.GetChannel()->ChannelId(), dead, alive);
_file.WriteText(" #dead :%6d\n", dead);
_file.WriteText(" #alive:%6d\n", alive);
}
EchoStatistics echo;
GetEchoMetricSummary(echo);
_file.WriteText("\nEcho Metrics\n");
_file.WriteText("------------\n\n");
_file.WriteText("erl:\n");
_file.WriteText(" min:%5d [dB]\n", echo.erl.min);
_file.WriteText(" max:%5d [dB]\n", echo.erl.max);
_file.WriteText(" avg:%5d [dB]\n", echo.erl.average);
_file.WriteText("\nerle:\n");
_file.WriteText(" min:%5d [dB]\n", echo.erle.min);
_file.WriteText(" max:%5d [dB]\n", echo.erle.max);
_file.WriteText(" avg:%5d [dB]\n", echo.erle.average);
_file.WriteText("rerl:\n");
_file.WriteText(" min:%5d [dB]\n", echo.rerl.min);
_file.WriteText(" max:%5d [dB]\n", echo.rerl.max);
_file.WriteText(" avg:%5d [dB]\n", echo.rerl.average);
_file.WriteText("a_nlp:\n");
_file.WriteText(" min:%5d [dB]\n", echo.a_nlp.min);
_file.WriteText(" max:%5d [dB]\n", echo.a_nlp.max);
_file.WriteText(" avg:%5d [dB]\n", echo.a_nlp.average);
_file.WriteText("\n<END>");
_file.Flush();
_file.CloseFile();
return 0;
}
#endif // WEBRTC_VOICE_ENGINE_CALL_REPORT_API
} // namespace webrtc