/* * 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_dtmf_impl.h" #include "channel.h" #include "critical_section_wrapper.h" #include "output_mixer.h" #include "trace.h" #include "transmit_mixer.h" #include "voe_errors.h" #include "voice_engine_impl.h" namespace webrtc { VoEDtmf* VoEDtmf::GetInterface(VoiceEngine* voiceEngine) { #ifndef WEBRTC_VOICE_ENGINE_DTMF_API return NULL; #else if (NULL == voiceEngine) { return NULL; } VoiceEngineImpl* s = reinterpret_cast (voiceEngine); VoEDtmfImpl* d = s; ( *d)++; return (d); #endif } #ifdef WEBRTC_VOICE_ENGINE_DTMF_API VoEDtmfImpl::VoEDtmfImpl() : _dtmfFeedback(true), _dtmfDirectFeedback(false) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1 ), "VoEDtmfImpl::VoEDtmfImpl() - ctor"); } VoEDtmfImpl::~VoEDtmfImpl() { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1), "VoEDtmfImpl::~VoEDtmfImpl() - dtor"); } int VoEDtmfImpl::Release() { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "VoEDtmf::Release()"); (*this)--; int refCount = GetCount(); if (refCount < 0) { Reset(); // reset reference counter to zero => OK to delete VE _engineStatistics.SetLastError( VE_INTERFACE_NOT_FOUND, kTraceWarning); return (-1); } WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), "VoEDtmf reference counter = %d", refCount); return (refCount); } int VoEDtmfImpl::SendTelephoneEvent(int channel, unsigned char eventCode, bool outOfBand, int lengthMs, int attenuationDb) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d," "length=%d, attenuationDb=%d)", channel, eventCode, (int)outOfBand, lengthMs, attenuationDb); 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, "SendTelephoneEvent() failed to locate channel"); return -1; } if (!channelPtr->Sending()) { _engineStatistics.SetLastError( VE_NOT_SENDING, kTraceError, "SendTelephoneEvent() sending is not active"); return -1; } // Sanity check const int maxEventCode = outOfBand ? static_cast(kMaxTelephoneEventCode) : static_cast(kMaxDtmfEventCode); const bool testFailed = ((eventCode < 0) || (eventCode > maxEventCode) || (lengthMs < kMinTelephoneEventDuration) || (lengthMs > kMaxTelephoneEventDuration) || (attenuationDb < kMinTelephoneEventAttenuation) || (attenuationDb > kMaxTelephoneEventAttenuation)); if (testFailed) { _engineStatistics.SetLastError( VE_INVALID_ARGUMENT, kTraceError, "SendTelephoneEvent() invalid parameter(s)"); return -1; } const bool isDtmf = (eventCode >= 0) && (eventCode <= kMaxDtmfEventCode); const bool playDtmfToneDirect = isDtmf && (_dtmfFeedback && _dtmfDirectFeedback); if (playDtmfToneDirect) { // Mute the microphone signal while playing back the tone directly. // This is to reduce the risk of introducing echo from the added output. _transmitMixerPtr->UpdateMuteMicrophoneTime(lengthMs); // Play out local feedback tone directly (same approach for both inband // and outband). // Reduce the length of the the tone with 80ms to reduce risk of echo. // For non-direct feedback, outband and inband cases are handled // differently. _outputMixerPtr->PlayDtmfTone(eventCode, lengthMs-80, attenuationDb); } if (outOfBand) { // The RTP/RTCP module will always deliver OnPlayTelephoneEvent when // an event is transmitted. It is up to the VoE to utilize it or not. // This flag ensures that feedback/playout is enabled; however, the // channel object must still parse out the Dtmf events (0-15) from // all possible events (0-255). const bool playDTFMEvent = (_dtmfFeedback && !_dtmfDirectFeedback); return channelPtr->SendTelephoneEventOutband(eventCode, lengthMs, attenuationDb, playDTFMEvent); } else { // For Dtmf tones, we want to ensure that inband tones are played out // in sync with the transmitted audio. This flag is utilized by the // channel object to determine if the queued Dtmf e vent shall also // be fed to the output mixer in the same step as input audio is // replaced by inband Dtmf tones. const bool playDTFMEvent = (isDtmf && _dtmfFeedback && !_dtmfDirectFeedback); return channelPtr->SendTelephoneEventInband(eventCode, lengthMs, attenuationDb, playDTFMEvent); } } int VoEDtmfImpl::SetSendTelephoneEventPayloadType(int channel, unsigned char type) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "SetSendTelephoneEventPayloadType(channel=%d, type=%u)", channel, type); 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, "SetSendTelephoneEventPayloadType() failed to locate channel"); return -1; } return channelPtr->SetSendTelephoneEventPayloadType(type); } int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel, unsigned char& type) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "GetSendTelephoneEventPayloadType(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, "GetSendTelephoneEventPayloadType() failed to locate channel"); return -1; } return channelPtr->GetSendTelephoneEventPayloadType(type); } int VoEDtmfImpl::PlayDtmfTone(unsigned char eventCode, int lengthMs, int attenuationDb) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)", eventCode, lengthMs, attenuationDb); if (!_engineStatistics.Initialized()) { _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); return -1; } if (!_audioDevicePtr->Playing()) { _engineStatistics.SetLastError( VE_NOT_PLAYING, kTraceError, "PlayDtmfTone() no channel is playing out"); return -1; } if ((eventCode < kMinDtmfEventCode) || (eventCode > kMaxDtmfEventCode) || (lengthMs < kMinTelephoneEventDuration) || (lengthMs > kMaxTelephoneEventDuration) || (attenuationDb kMaxTelephoneEventAttenuation)) { _engineStatistics.SetLastError( VE_INVALID_ARGUMENT, kTraceError, "PlayDtmfTone() invalid tone parameter(s)"); return -1; } return _outputMixerPtr->PlayDtmfTone(eventCode, lengthMs, attenuationDb); } int VoEDtmfImpl::StartPlayingDtmfTone(unsigned char eventCode, int attenuationDb) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "StartPlayingDtmfTone(eventCode=%d, attenuationDb=%d)", eventCode, attenuationDb); if (!_engineStatistics.Initialized()) { _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); return -1; } if (!_audioDevicePtr->Playing()) { _engineStatistics.SetLastError( VE_NOT_PLAYING, kTraceError, "StartPlayingDtmfTone() no channel is playing out"); return -1; } if ((eventCode < kMinDtmfEventCode) || (eventCode > kMaxDtmfEventCode) || (attenuationDb < kMinTelephoneEventAttenuation) || (attenuationDb > kMaxTelephoneEventAttenuation)) { _engineStatistics.SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartPlayingDtmfTone() invalid tone parameter(s)"); return -1; } return _outputMixerPtr->StartPlayingDtmfTone(eventCode, attenuationDb); } int VoEDtmfImpl::StopPlayingDtmfTone() { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "StopPlayingDtmfTone()"); if (!_engineStatistics.Initialized()) { _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError); return -1; } return _outputMixerPtr->StopPlayingDtmfTone(); } int VoEDtmfImpl::RegisterTelephoneEventDetection( int channel, TelephoneEventDetectionMethods detectionMethod, VoETelephoneEventObserver& observer) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "RegisterTelephoneEventDetection(channel=%d, detectionMethod=%d," "observer=0x%x)", channel, detectionMethod, &observer); #ifdef WEBRTC_DTMF_DETECTION 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, "RegisterTelephoneEventDetection() failed to locate channel"); return -1; } return channelPtr->RegisterTelephoneEventDetection(detectionMethod, observer); #else _engineStatistics.SetLastError( VE_FUNC_NOT_SUPPORTED, kTraceError, "SetTelephoneEventDetectionStatus() Dtmf detection is not supported"); return -1; #endif } int VoEDtmfImpl::DeRegisterTelephoneEventDetection(int channel) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "DeRegisterTelephoneEventDetection(channel=%d)", channel); #ifdef WEBRTC_DTMF_DETECTION 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, "DeRegisterTelephoneEventDe tection() failed to locate channel"); return -1; } return channelPtr->DeRegisterTelephoneEventDetection(); #else _engineStatistics.SetLastError( VE_FUNC_NOT_SUPPORTED, kTraceError, "DeRegisterTelephoneEventDetection() Dtmf detection is not supported"); return -1; #endif } int VoEDtmfImpl::GetTelephoneEventDetectionStatus( int channel, bool& enabled, TelephoneEventDetectionMethods& detectionMethod) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "GetTelephoneEventDetectionStatus(channel=%d)", channel); #ifdef WEBRTC_DTMF_DETECTION 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, "GetTelephoneEventDetectionStatus() failed to locate channel"); return -1; } return channelPtr->GetTelephoneEventDetectionStatus(enabled, detectionMethod); #else _engineStatistics.SetLastError( VE_FUNC_NOT_SUPPORTED, kTraceError, "GetTelephoneEventDetectionStatus() Dtmf detection is not supported"); return -1; #endif } int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)", (int)enable, (int)directFeedback); CriticalSectionScoped sc(*_apiCritPtr); _dtmfFeedback = enable; _dtmfDirectFeedback = directFeedback; return 0; } int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "GetDtmfFeedbackStatus()"); CriticalSectionScoped sc(*_apiCritPtr); enabled = _dtmfFeedback; directFeedback = _dtmfDirectFeedback; WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), "GetDtmfFeedbackStatus() => enabled=%d, directFeedback=%d", enabled, directFeedback); return 0; } int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "SetDtmfPlayoutStatus(channel=%d, enable=%d)", channel, enable); 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, "SetDtmfPlayoutStatus() failed to locate channel"); return -1; } return channelPtr->SetDtmfPlayoutStatus(enable); } int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1), "GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel); 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, "GetDtmfPlayoutStatus() failed to locate channel"); return -1; } enabled = channelPtr->DtmfPlayoutStatus(); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), "GetDtmfPlayoutStatus() => enabled=%d", enabled); return 0; } #endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API } // namespace webrtc