1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/voice_engine/voe_dtmf_impl.h" 12 13 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" 14 #include "webrtc/system_wrappers/include/trace.h" 15 #include "webrtc/voice_engine/channel.h" 16 #include "webrtc/voice_engine/include/voe_errors.h" 17 #include "webrtc/voice_engine/output_mixer.h" 18 #include "webrtc/voice_engine/transmit_mixer.h" 19 #include "webrtc/voice_engine/voice_engine_impl.h" 20 21 namespace webrtc { 22 23 VoEDtmf* VoEDtmf::GetInterface(VoiceEngine* voiceEngine) { 24 #ifndef WEBRTC_VOICE_ENGINE_DTMF_API 25 return NULL; 26 #else 27 if (NULL == voiceEngine) { 28 return NULL; 29 } 30 VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); 31 s->AddRef(); 32 return s; 33 #endif 34 } 35 36 #ifdef WEBRTC_VOICE_ENGINE_DTMF_API 37 38 VoEDtmfImpl::VoEDtmfImpl(voe::SharedData* shared) 39 : _dtmfFeedback(true), _dtmfDirectFeedback(false), _shared(shared) { 40 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), 41 "VoEDtmfImpl::VoEDtmfImpl() - ctor"); 42 } 43 44 VoEDtmfImpl::~VoEDtmfImpl() { 45 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), 46 "VoEDtmfImpl::~VoEDtmfImpl() - dtor"); 47 } 48 49 int VoEDtmfImpl::SendTelephoneEvent(int channel, 50 int eventCode, 51 bool outOfBand, 52 int lengthMs, 53 int attenuationDb) { 54 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 55 "SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d," 56 "length=%d, attenuationDb=%d)", 57 channel, eventCode, (int)outOfBand, lengthMs, attenuationDb); 58 if (!_shared->statistics().Initialized()) { 59 _shared->SetLastError(VE_NOT_INITED, kTraceError); 60 return -1; 61 } 62 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 63 voe::Channel* channelPtr = ch.channel(); 64 if (channelPtr == NULL) { 65 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 66 "SendTelephoneEvent() failed to locate channel"); 67 return -1; 68 } 69 if (!channelPtr->Sending()) { 70 _shared->SetLastError(VE_NOT_SENDING, kTraceError, 71 "SendTelephoneEvent() sending is not active"); 72 return -1; 73 } 74 75 // Sanity check 76 const int maxEventCode = outOfBand ? static_cast<int>(kMaxTelephoneEventCode) 77 : static_cast<int>(kMaxDtmfEventCode); 78 const bool testFailed = ((eventCode < 0) || (eventCode > maxEventCode) || 79 (lengthMs < kMinTelephoneEventDuration) || 80 (lengthMs > kMaxTelephoneEventDuration) || 81 (attenuationDb < kMinTelephoneEventAttenuation) || 82 (attenuationDb > kMaxTelephoneEventAttenuation)); 83 if (testFailed) { 84 _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, 85 "SendTelephoneEvent() invalid parameter(s)"); 86 return -1; 87 } 88 89 const bool isDtmf = (eventCode >= 0) && (eventCode <= kMaxDtmfEventCode); 90 const bool playDtmfToneDirect = 91 isDtmf && (_dtmfFeedback && _dtmfDirectFeedback); 92 93 if (playDtmfToneDirect) { 94 // Mute the microphone signal while playing back the tone directly. 95 // This is to reduce the risk of introducing echo from the added output. 96 _shared->transmit_mixer()->UpdateMuteMicrophoneTime(lengthMs); 97 98 // Play out local feedback tone directly (same approach for both inband 99 // and outband). 100 // Reduce the length of the the tone with 80ms to reduce risk of echo. 101 // For non-direct feedback, outband and inband cases are handled 102 // differently. 103 _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs - 80, 104 attenuationDb); 105 } 106 107 if (outOfBand) { 108 // The RTP/RTCP module will always deliver OnPlayTelephoneEvent when 109 // an event is transmitted. It is up to the VoE to utilize it or not. 110 // This flag ensures that feedback/playout is enabled; however, the 111 // channel object must still parse out the Dtmf events (0-15) from 112 // all possible events (0-255). 113 const bool playDTFMEvent = (_dtmfFeedback && !_dtmfDirectFeedback); 114 115 return channelPtr->SendTelephoneEventOutband(eventCode, lengthMs, 116 attenuationDb, playDTFMEvent); 117 } else { 118 // For Dtmf tones, we want to ensure that inband tones are played out 119 // in sync with the transmitted audio. This flag is utilized by the 120 // channel object to determine if the queued Dtmf e vent shall also 121 // be fed to the output mixer in the same step as input audio is 122 // replaced by inband Dtmf tones. 123 const bool playDTFMEvent = 124 (isDtmf && _dtmfFeedback && !_dtmfDirectFeedback); 125 126 return channelPtr->SendTelephoneEventInband(eventCode, lengthMs, 127 attenuationDb, playDTFMEvent); 128 } 129 } 130 131 int VoEDtmfImpl::SetSendTelephoneEventPayloadType(int channel, 132 unsigned char type) { 133 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 134 "SetSendTelephoneEventPayloadType(channel=%d, type=%u)", channel, 135 type); 136 if (!_shared->statistics().Initialized()) { 137 _shared->SetLastError(VE_NOT_INITED, kTraceError); 138 return -1; 139 } 140 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 141 voe::Channel* channelPtr = ch.channel(); 142 if (channelPtr == NULL) { 143 _shared->SetLastError( 144 VE_CHANNEL_NOT_VALID, kTraceError, 145 "SetSendTelephoneEventPayloadType() failed to locate channel"); 146 return -1; 147 } 148 return channelPtr->SetSendTelephoneEventPayloadType(type); 149 } 150 151 int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel, 152 unsigned char& type) { 153 if (!_shared->statistics().Initialized()) { 154 _shared->SetLastError(VE_NOT_INITED, kTraceError); 155 return -1; 156 } 157 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 158 voe::Channel* channelPtr = ch.channel(); 159 if (channelPtr == NULL) { 160 _shared->SetLastError( 161 VE_CHANNEL_NOT_VALID, kTraceError, 162 "GetSendTelephoneEventPayloadType() failed to locate channel"); 163 return -1; 164 } 165 return channelPtr->GetSendTelephoneEventPayloadType(type); 166 } 167 168 int VoEDtmfImpl::PlayDtmfTone(int eventCode, int lengthMs, int attenuationDb) { 169 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 170 "PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)", 171 eventCode, lengthMs, attenuationDb); 172 173 if (!_shared->statistics().Initialized()) { 174 _shared->SetLastError(VE_NOT_INITED, kTraceError); 175 return -1; 176 } 177 if (!_shared->audio_device()->Playing()) { 178 _shared->SetLastError(VE_NOT_PLAYING, kTraceError, 179 "PlayDtmfTone() no channel is playing out"); 180 return -1; 181 } 182 if ((eventCode < kMinDtmfEventCode) || (eventCode > kMaxDtmfEventCode) || 183 (lengthMs < kMinTelephoneEventDuration) || 184 (lengthMs > kMaxTelephoneEventDuration) || 185 (attenuationDb < kMinTelephoneEventAttenuation) || 186 (attenuationDb > kMaxTelephoneEventAttenuation)) { 187 _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, 188 "PlayDtmfTone() invalid tone parameter(s)"); 189 return -1; 190 } 191 return _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs, 192 attenuationDb); 193 } 194 195 int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) { 196 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 197 "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)", 198 (int)enable, (int)directFeedback); 199 200 CriticalSectionScoped sc(_shared->crit_sec()); 201 202 _dtmfFeedback = enable; 203 _dtmfDirectFeedback = directFeedback; 204 205 return 0; 206 } 207 208 int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) { 209 CriticalSectionScoped sc(_shared->crit_sec()); 210 211 enabled = _dtmfFeedback; 212 directFeedback = _dtmfDirectFeedback; 213 return 0; 214 } 215 #endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API 216 217 } // namespace webrtc 218