/* * 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(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"); _file.Flush(); _file.CloseFile(); return 0; } #endif // WEBRTC_VOICE_ENGINE_CALL_REPORT_API } // namespace webrtc