| /* |
| * Copyright (c) 2011 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 "voe_call_report_impl.h" |
| |
| #include "audio_processing.h" |
| #include "channel.h" |
| #include "critical_section_wrapper.h" |
| #include "file_wrapper.h" |
| #include "trace.h" |
| #include "voe_errors.h" |
| #include "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 = |
| reinterpret_cast<VoiceEngineImpl*> (voiceEngine); |
| VoECallReportImpl* d = s; |
| (*d)++; |
| return (d); |
| #endif |
| } |
| |
| #ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API |
| |
| VoECallReportImpl::VoECallReportImpl() : |
| _file(*FileWrapper::Create()) |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1), |
| "VoECallReportImpl() - ctor"); |
| } |
| |
| VoECallReportImpl::~VoECallReportImpl() |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1), |
| "~VoECallReportImpl() - dtor"); |
| delete &_file; |
| } |
| |
| int VoECallReportImpl::Release() |
| { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1), |
| "VoECallReportImpl::Release()"); |
| (*this)--; |
| int refCount = GetCount(); |
| if (refCount < 0) |
| { |
| Reset(); |
| _engineStatistics.SetLastError(VE_INTERFACE_NOT_FOUND, |
| kTraceWarning); |
| return (-1); |
| } |
| WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1), |
| "VoECallReportImpl reference counter = %d", refCount); |
| return (refCount); |
| } |
| |
| int VoECallReportImpl::ResetCallReportStatistics(int channel) |
| { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1), |
| "ResetCallReportStatistics(channel=%d)", channel); |
| ANDROID_NOT_SUPPORTED(_engineStatistics); |
| IPHONE_NOT_SUPPORTED(); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| assert(_audioProcessingModulePtr != NULL); |
| |
| bool echoMode = |
| _audioProcessingModulePtr->echo_cancellation()->are_metrics_enabled(); |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), |
| " current AudioProcessingModule echo metric state %d)", |
| echoMode); |
| // Reset the APM statistics |
| if (_audioProcessingModulePtr->echo_cancellation()->enable_metrics(true) |
| != 0) |
| { |
| _engineStatistics.SetLastError(VE_APM_ERROR, kTraceError, |
| "ResetCallReportStatistics() unable to " |
| "set the AudioProcessingModule echo " |
| "metrics state"); |
| return -1; |
| } |
| // Restore metric states |
| _audioProcessingModulePtr->echo_cancellation()->enable_metrics(echoMode); |
| |
| // Reset channel dependent statistics |
| if (channel != -1) |
| { |
| voe::ScopedChannel sc(_channelManager, channel); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr == NULL) |
| { |
| _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, |
| "ResetCallReportStatistics() failed " |
| "to locate channel"); |
| return -1; |
| } |
| channelPtr->ResetDeadOrAliveCounters(); |
| channelPtr->ResetRTCPStatistics(); |
| } |
| else |
| { |
| WebRtc_Word32 numOfChannels = _channelManager.NumOfChannels(); |
| if (numOfChannels <= 0) |
| { |
| return 0; |
| } |
| WebRtc_Word32* channelsArray = new WebRtc_Word32[numOfChannels]; |
| _channelManager.GetChannelIds(channelsArray, numOfChannels); |
| for (int i = 0; i < numOfChannels; i++) |
| { |
| voe::ScopedChannel sc(_channelManager, channelsArray[i]); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr) |
| { |
| channelPtr->ResetDeadOrAliveCounters(); |
| channelPtr->ResetRTCPStatistics(); |
| } |
| } |
| delete[] channelsArray; |
| } |
| |
| return 0; |
| } |
| |
| int VoECallReportImpl::GetEchoMetricSummary(EchoStatistics& stats) |
| { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1), |
| "GetEchoMetricSummary()"); |
| ANDROID_NOT_SUPPORTED(_engineStatistics); |
| IPHONE_NOT_SUPPORTED(); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| assert(_audioProcessingModulePtr != 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 = |
| _audioProcessingModulePtr->echo_cancellation()->are_metrics_enabled(); |
| if (mode != false) |
| { |
| ret = |
| _audioProcessingModulePtr->echo_cancellation()->GetMetrics(&metrics); |
| if (ret != 0) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), |
| " AudioProcessingModule GetMetrics() => error"); |
| } |
| } |
| else |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), |
| " AudioProcessingModule echo metrics is not enabled"); |
| } |
| |
| if ((ret != 0) || (mode == false)) |
| { |
| // Mark complete struct as invalid (-100 dB) |
| WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -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(_instanceId, -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(_instanceId, -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(_instanceId, -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(_instanceId, -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(_instanceId, -1), |
| "GetRoundTripTimeSummary()"); |
| ANDROID_NOT_SUPPORTED(_engineStatistics); |
| IPHONE_NOT_SUPPORTED(); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| voe::ScopedChannel sc(_channelManager, channel); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr == NULL) |
| { |
| _engineStatistics.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(_instanceId, -1), |
| "GetDeadOrAliveSummary(channel=%d)", channel); |
| ANDROID_NOT_SUPPORTED(_engineStatistics); |
| IPHONE_NOT_SUPPORTED(); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.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(_instanceId, -1), |
| "GetDeadOrAliveSummary(channel=%d)", channel); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| voe::ScopedChannel sc(_channelManager, channel); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr == NULL) |
| { |
| _engineStatistics.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(_instanceId, -1), |
| "WriteReportToFile(fileNameUTF8=%s)", fileNameUTF8); |
| ANDROID_NOT_SUPPORTED(_engineStatistics); |
| IPHONE_NOT_SUPPORTED(); |
| |
| if (!_engineStatistics.Initialized()) |
| { |
| _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| |
| if (NULL == fileNameUTF8) |
| { |
| _engineStatistics.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) |
| { |
| _engineStatistics.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"); |
| |
| WebRtc_Word32 numOfChannels = _channelManager.NumOfChannels(); |
| if (numOfChannels <= 0) |
| { |
| return 0; |
| } |
| WebRtc_Word32* channelsArray = new WebRtc_Word32[numOfChannels]; |
| _channelManager.GetChannelIds(channelsArray, numOfChannels); |
| for (int ch = 0; ch < numOfChannels; ch++) |
| { |
| voe::ScopedChannel sc(_channelManager, channelsArray[ch]); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr) |
| { |
| StatVal delaysMs; |
| _file.WriteText("channel %d:\n", ch); |
| channelPtr->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 (int ch = 0; ch < numOfChannels; ch++) |
| { |
| voe::ScopedChannel sc(_channelManager, channelsArray[ch]); |
| voe::Channel* channelPtr = sc.ChannelPtr(); |
| if (channelPtr) |
| { |
| int nDead(0); |
| int nAlive(0); |
| _file.WriteText("channel %d:\n", ch); |
| GetDeadOrAliveSummary(ch, nDead, nAlive); |
| _file.WriteText(" #dead :%6d\n", nDead); |
| _file.WriteText(" #alive:%6d\n", nAlive); |
| } |
| } |
| |
| delete[] channelsArray; |
| |
| 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 |