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/interface/critical_section_wrapper.h" 14 #include "webrtc/system_wrappers/interface/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 { 25 #ifndef WEBRTC_VOICE_ENGINE_DTMF_API 26 return NULL; 27 #else 28 if (NULL == voiceEngine) 29 { 30 return NULL; 31 } 32 VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); 33 s->AddRef(); 34 return s; 35 #endif 36 } 37 38 #ifdef WEBRTC_VOICE_ENGINE_DTMF_API 39 40 VoEDtmfImpl::VoEDtmfImpl(voe::SharedData* shared) : 41 _dtmfFeedback(true), 42 _dtmfDirectFeedback(false), 43 _shared(shared) 44 { 45 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), 46 "VoEDtmfImpl::VoEDtmfImpl() - ctor"); 47 } 48 49 VoEDtmfImpl::~VoEDtmfImpl() 50 { 51 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), 52 "VoEDtmfImpl::~VoEDtmfImpl() - dtor"); 53 } 54 55 int VoEDtmfImpl::SendTelephoneEvent(int channel, 56 int eventCode, 57 bool outOfBand, 58 int lengthMs, 59 int attenuationDb) 60 { 61 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 62 "SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d," 63 "length=%d, attenuationDb=%d)", 64 channel, eventCode, (int)outOfBand, lengthMs, attenuationDb); 65 if (!_shared->statistics().Initialized()) 66 { 67 _shared->SetLastError(VE_NOT_INITED, kTraceError); 68 return -1; 69 } 70 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 71 voe::Channel* channelPtr = ch.channel(); 72 if (channelPtr == NULL) 73 { 74 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 75 "SendTelephoneEvent() failed to locate channel"); 76 return -1; 77 } 78 if (!channelPtr->Sending()) 79 { 80 _shared->SetLastError(VE_NOT_SENDING, kTraceError, 81 "SendTelephoneEvent() sending is not active"); 82 return -1; 83 } 84 85 // Sanity check 86 const int maxEventCode = outOfBand ? 87 static_cast<int>(kMaxTelephoneEventCode) : 88 static_cast<int>(kMaxDtmfEventCode); 89 const bool testFailed = ((eventCode < 0) || 90 (eventCode > maxEventCode) || 91 (lengthMs < kMinTelephoneEventDuration) || 92 (lengthMs > kMaxTelephoneEventDuration) || 93 (attenuationDb < kMinTelephoneEventAttenuation) || 94 (attenuationDb > kMaxTelephoneEventAttenuation)); 95 if (testFailed) 96 { 97 _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, 98 "SendTelephoneEvent() invalid parameter(s)"); 99 return -1; 100 } 101 102 const bool isDtmf = 103 (eventCode >= 0) && (eventCode <= kMaxDtmfEventCode); 104 const bool playDtmfToneDirect = 105 isDtmf && (_dtmfFeedback && _dtmfDirectFeedback); 106 107 if (playDtmfToneDirect) 108 { 109 // Mute the microphone signal while playing back the tone directly. 110 // This is to reduce the risk of introducing echo from the added output. 111 _shared->transmit_mixer()->UpdateMuteMicrophoneTime(lengthMs); 112 113 // Play out local feedback tone directly (same approach for both inband 114 // and outband). 115 // Reduce the length of the the tone with 80ms to reduce risk of echo. 116 // For non-direct feedback, outband and inband cases are handled 117 // differently. 118 _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs - 80, 119 attenuationDb); 120 } 121 122 if (outOfBand) 123 { 124 // The RTP/RTCP module will always deliver OnPlayTelephoneEvent when 125 // an event is transmitted. It is up to the VoE to utilize it or not. 126 // This flag ensures that feedback/playout is enabled; however, the 127 // channel object must still parse out the Dtmf events (0-15) from 128 // all possible events (0-255). 129 const bool playDTFMEvent = (_dtmfFeedback && !_dtmfDirectFeedback); 130 131 return channelPtr->SendTelephoneEventOutband(eventCode, 132 lengthMs, 133 attenuationDb, 134 playDTFMEvent); 135 } 136 else 137 { 138 // For Dtmf tones, we want to ensure that inband tones are played out 139 // in sync with the transmitted audio. This flag is utilized by the 140 // channel object to determine if the queued Dtmf e vent shall also 141 // be fed to the output mixer in the same step as input audio is 142 // replaced by inband Dtmf tones. 143 const bool playDTFMEvent = 144 (isDtmf && _dtmfFeedback && !_dtmfDirectFeedback); 145 146 return channelPtr->SendTelephoneEventInband(eventCode, 147 lengthMs, 148 attenuationDb, 149 playDTFMEvent); 150 } 151 } 152 153 int VoEDtmfImpl::SetSendTelephoneEventPayloadType(int channel, 154 unsigned char type) 155 { 156 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 157 "SetSendTelephoneEventPayloadType(channel=%d, type=%u)", 158 channel, type); 159 if (!_shared->statistics().Initialized()) 160 { 161 _shared->SetLastError(VE_NOT_INITED, kTraceError); 162 return -1; 163 } 164 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 165 voe::Channel* channelPtr = ch.channel(); 166 if (channelPtr == NULL) 167 { 168 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 169 "SetSendTelephoneEventPayloadType() failed to locate channel"); 170 return -1; 171 } 172 return channelPtr->SetSendTelephoneEventPayloadType(type); 173 } 174 175 int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel, 176 unsigned char& type) 177 { 178 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 179 "GetSendTelephoneEventPayloadType(channel=%d)", channel); 180 if (!_shared->statistics().Initialized()) 181 { 182 _shared->SetLastError(VE_NOT_INITED, kTraceError); 183 return -1; 184 } 185 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 186 voe::Channel* channelPtr = ch.channel(); 187 if (channelPtr == NULL) 188 { 189 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 190 "GetSendTelephoneEventPayloadType() failed to locate channel"); 191 return -1; 192 } 193 return channelPtr->GetSendTelephoneEventPayloadType(type); 194 } 195 196 int VoEDtmfImpl::PlayDtmfTone(int eventCode, 197 int lengthMs, 198 int attenuationDb) 199 { 200 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 201 "PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)", 202 eventCode, lengthMs, attenuationDb); 203 204 if (!_shared->statistics().Initialized()) 205 { 206 _shared->SetLastError(VE_NOT_INITED, kTraceError); 207 return -1; 208 } 209 if (!_shared->audio_device()->Playing()) 210 { 211 _shared->SetLastError(VE_NOT_PLAYING, kTraceError, 212 "PlayDtmfTone() no channel is playing out"); 213 return -1; 214 } 215 if ((eventCode < kMinDtmfEventCode) || 216 (eventCode > kMaxDtmfEventCode) || 217 (lengthMs < kMinTelephoneEventDuration) || 218 (lengthMs > kMaxTelephoneEventDuration) || 219 (attenuationDb <kMinTelephoneEventAttenuation) || 220 (attenuationDb > kMaxTelephoneEventAttenuation)) 221 { 222 _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, 223 "PlayDtmfTone() invalid tone parameter(s)"); 224 return -1; 225 } 226 return _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs, 227 attenuationDb); 228 } 229 230 int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) 231 { 232 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 233 "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)", 234 (int)enable, (int)directFeedback); 235 236 CriticalSectionScoped sc(_shared->crit_sec()); 237 238 _dtmfFeedback = enable; 239 _dtmfDirectFeedback = directFeedback; 240 241 return 0; 242 } 243 244 int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) 245 { 246 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 247 "GetDtmfFeedbackStatus()"); 248 249 CriticalSectionScoped sc(_shared->crit_sec()); 250 251 enabled = _dtmfFeedback; 252 directFeedback = _dtmfDirectFeedback; 253 254 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, 255 VoEId(_shared->instance_id(), -1), 256 "GetDtmfFeedbackStatus() => enabled=%d, directFeedback=%d", 257 enabled, directFeedback); 258 return 0; 259 } 260 261 int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable) 262 { 263 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 264 "SetDtmfPlayoutStatus(channel=%d, enable=%d)", 265 channel, enable); 266 267 if (!_shared->statistics().Initialized()) 268 { 269 _shared->SetLastError(VE_NOT_INITED, kTraceError); 270 return -1; 271 } 272 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 273 voe::Channel* channelPtr = ch.channel(); 274 if (channelPtr == NULL) 275 { 276 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 277 "SetDtmfPlayoutStatus() failed to locate channel"); 278 return -1; 279 } 280 return channelPtr->SetDtmfPlayoutStatus(enable); 281 } 282 283 int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled) 284 { 285 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), 286 "GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel); 287 if (!_shared->statistics().Initialized()) 288 { 289 _shared->SetLastError(VE_NOT_INITED, kTraceError); 290 return -1; 291 } 292 voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); 293 voe::Channel* channelPtr = ch.channel(); 294 if (channelPtr == NULL) 295 { 296 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 297 "GetDtmfPlayoutStatus() failed to locate channel"); 298 return -1; 299 } 300 enabled = channelPtr->DtmfPlayoutStatus(); 301 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, 302 VoEId(_shared->instance_id(), -1), 303 "GetDtmfPlayoutStatus() => enabled=%d", enabled); 304 return 0; 305 } 306 307 #endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API 308 309 } // namespace webrtc 310